* fix: TreeSelect组件单选模式开启searchable绑定值后输入框仍然存在问题

* fix: 日期时间范围选择器的时间选择点击无效问题 (#8149)
This commit is contained in:
RUNZE LU 2023-09-18 19:17:12 +08:00 committed by GitHub
parent d117f0f12b
commit b3981f6846
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 160 additions and 37 deletions

View File

@ -11,6 +11,7 @@ import {themeable, ThemeProps} from 'amis-core';
import {LocaleProps, localeable} from 'amis-core'; import {LocaleProps, localeable} from 'amis-core';
import {autobind} from 'amis-core'; import {autobind} from 'amis-core';
import {ShortCuts} from './DatePicker'; import {ShortCuts} from './DatePicker';
import type {ViewMode} from './calendar/Calendar';
export interface CalendarMobileProps extends ThemeProps, LocaleProps { export interface CalendarMobileProps extends ThemeProps, LocaleProps {
className?: string; className?: string;
@ -25,7 +26,7 @@ export interface CalendarMobileProps extends ThemeProps, LocaleProps {
maxDuration?: moment.Duration; maxDuration?: moment.Duration;
dateFormat?: string; dateFormat?: string;
embed?: boolean; embed?: boolean;
viewMode?: 'days' | 'months' | 'years' | 'time' | 'quarters'; viewMode?: ViewMode;
close?: () => void; close?: () => void;
confirm?: (startDate?: any, endTime?: any) => void; confirm?: (startDate?: any, endTime?: any) => void;
onChange?: (data: any, callback?: () => void) => void; onChange?: (data: any, callback?: () => void) => void;

View File

@ -30,6 +30,7 @@ import Input from './Input';
import Button from './Button'; import Button from './Button';
import type {PlainObject, ThemeProps, LocaleProps} from 'amis-core'; import type {PlainObject, ThemeProps, LocaleProps} from 'amis-core';
import type {ViewMode} from './calendar/Calendar';
export interface DateRangePickerProps extends ThemeProps, LocaleProps { export interface DateRangePickerProps extends ThemeProps, LocaleProps {
className?: string; className?: string;
@ -67,7 +68,7 @@ export interface DateRangePickerProps extends ThemeProps, LocaleProps {
popOverContainer?: any; popOverContainer?: any;
dateFormat?: string; dateFormat?: string;
embed?: boolean; embed?: boolean;
viewMode?: 'days' | 'months' | 'years' | 'time' | 'quarters'; viewMode?: ViewMode;
borderMode?: 'full' | 'half' | 'none'; borderMode?: 'full' | 'half' | 'none';
onFocus?: Function; onFocus?: Function;
onBlur?: Function; onBlur?: Function;
@ -882,12 +883,23 @@ export class DateRangePicker extends React.Component<
filterDate( filterDate(
date: moment.Moment, date: moment.Moment,
originValue?: moment.Moment, options: {
timeFormat?: string, type: 'start' | 'end';
type: 'start' | 'end' = 'start' originValue?: moment.Moment;
timeFormat?: string;
subControlViewMode?: 'time';
} = {type: 'start'}
): moment.Moment { ): moment.Moment {
const {type, originValue, timeFormat, subControlViewMode} = options || {
type: 'start'
};
let value = date.clone(); let value = date.clone();
/** 日期时间选择器组件支持用户选择时间,如果用户手动选择了时间,则不需要走默认处理 */
if (subControlViewMode && subControlViewMode === 'time') {
return value;
}
// 没有初始值 // 没有初始值
if (!originValue) { if (!originValue) {
value = value[type === 'start' ? 'startOf' : 'endOf']('day'); value = value[type === 'start' ? 'startOf' : 'endOf']('day');
@ -915,18 +927,26 @@ export class DateRangePicker extends React.Component<
} }
} }
handleStartDateChange(newValue: moment.Moment) { /**
const {timeFormat, minDate, inputFormat, displayFormat, type} = this.props; * @param {Moment} newValue
let {startDate, endDateOpenedFirst} = this.state; * @param {ViewMode=} subControlViewMode 'time'datetime选择器的触发控件
*/
handleStartDateChange(
newValue: moment.Moment,
subControlViewMode?: Extract<ViewMode, 'time'>
) {
const {minDate, inputFormat, displayFormat, type} = this.props;
let {startDate, endDateOpenedFirst, curTimeFormat: timeFormat} = this.state;
if (minDate && newValue.isBefore(minDate)) { if (minDate && newValue.isBefore(minDate)) {
newValue = minDate; newValue = minDate;
} }
const date = this.filterDate(
newValue, const date = this.filterDate(newValue, {
startDate || minDate, type: 'start',
originValue: startDate || minDate,
timeFormat, timeFormat,
'start' subControlViewMode
); });
const newState = { const newState = {
startDate: date, startDate: date,
startInputValue: date.format(displayFormat || inputFormat) startInputValue: date.format(displayFormat || inputFormat)
@ -944,13 +964,29 @@ export class DateRangePicker extends React.Component<
this.setState(newState); this.setState(newState);
} }
handelEndDateChange(newValue: moment.Moment) { /**
const {embed, timeFormat, inputFormat, displayFormat, type} = this.props; * @param {Moment} newValue
let {startDate, endDate, endDateOpenedFirst} = this.state; * @param {string=} subControlViewMode 'time'datetime选择器的触发控件
*/
handelEndDateChange(
newValue: moment.Moment,
subControlViewMode?: Extract<ViewMode, 'time'>
) {
const {embed, inputFormat, displayFormat, type} = this.props;
let {
startDate,
endDate,
endDateOpenedFirst,
curTimeFormat: timeFormat
} = this.state;
newValue = this.getEndDateByDuration(newValue); newValue = this.getEndDateByDuration(newValue);
const editState = endDateOpenedFirst ? 'start' : 'end'; const editState = endDateOpenedFirst ? 'start' : 'end';
const date = this.filterDate(newValue, {
const date = this.filterDate(newValue, endDate, timeFormat, 'end'); type: 'end',
originValue: endDate,
timeFormat,
subControlViewMode
});
this.setState( this.setState(
{ {
endDate: date, endDate: date,

View File

@ -16,6 +16,9 @@ import 'moment/locale/zh-cn';
import 'moment/locale/de'; import 'moment/locale/de';
import type {RendererEnv} from 'amis-core'; import type {RendererEnv} from 'amis-core';
/** 视图模式 */
export type ViewMode = 'days' | 'months' | 'years' | 'time' | 'quarters';
export type DateType = export type DateType =
| 'year' | 'year'
| 'month' | 'month'
@ -48,7 +51,7 @@ interface BaseDatePickerProps {
className?: string; className?: string;
value?: any; value?: any;
defaultValue?: any; defaultValue?: any;
viewMode?: 'years' | 'months' | 'days' | 'time' | 'quarters'; viewMode?: ViewMode;
dateFormat?: boolean | string; dateFormat?: boolean | string;
inputFormat?: boolean | string; inputFormat?: boolean | string;
displayForamt?: boolean | string; displayForamt?: boolean | string;
@ -63,7 +66,7 @@ interface BaseDatePickerProps {
onViewModeChange?: (type: string) => void; onViewModeChange?: (type: string) => void;
requiredConfirm?: boolean; requiredConfirm?: boolean;
onClose?: () => void; onClose?: () => void;
onChange?: (value: any) => void; onChange?: (value: any, viewMode?: Extract<ViewMode, 'time'>) => void;
isEndDate?: boolean; isEndDate?: boolean;
minDate?: moment.Moment; minDate?: moment.Moment;
maxDate?: moment.Moment; maxDate?: moment.Moment;
@ -480,7 +483,7 @@ class BaseDatePicker extends React.Component<
inputValue: date.format(state.displayForamt as string) inputValue: date.format(state.displayForamt as string)
}); });
} }
this.props.onChange && this.props.onChange(date); this.props.onChange && this.props.onChange(date, 'time');
}; };
setDate = (type: 'month' | 'year' | 'quarters') => { setDate = (type: 'month' | 'year' | 'quarters') => {

View File

@ -19,9 +19,11 @@ import type {RendererEnv} from 'amis-core';
import Picker from '../Picker'; import Picker from '../Picker';
import {PickerOption} from '../PickerColumn'; import {PickerOption} from '../PickerColumn';
import {DateType} from './Calendar'; import {DateType} from './Calendar';
import type {TimeScale} from './TimeView';
import {Icon} from '../icons'; import {Icon} from '../icons';
import type {TimeScale} from './TimeView';
import type {ViewMode} from './Calendar';
interface CustomDaysViewProps extends LocaleProps { interface CustomDaysViewProps extends LocaleProps {
classPrefix?: string; classPrefix?: string;
prevIcon?: string; prevIcon?: string;
@ -37,7 +39,10 @@ interface CustomDaysViewProps extends LocaleProps {
isEndDate?: boolean; isEndDate?: boolean;
renderDay?: Function; renderDay?: Function;
onClose?: () => void; onClose?: () => void;
onChange: (value: moment.Moment) => void; onChange: (
value: moment.Moment,
viewMode?: Extract<ViewMode, 'time'>
) => void;
onConfirm?: (value: number[], types: DateType[]) => void; onConfirm?: (value: number[], types: DateType[]) => void;
setDateTimeState: (state: any) => void; setDateTimeState: (state: any) => void;
showTime: () => void; showTime: () => void;
@ -294,6 +299,7 @@ export class CustomDaysView extends React.Component<CustomDaysViewProps> {
showTime = () => { showTime = () => {
const {selectedDate, viewDate, timeFormat} = this.props; const {selectedDate, viewDate, timeFormat} = this.props;
return ( return (
<div key="stb" className="rdtShowTime"> <div key="stb" className="rdtShowTime">
{(selectedDate || viewDate || moment()).format(timeFormat)} {(selectedDate || viewDate || moment()).format(timeFormat)}
@ -306,15 +312,16 @@ export class CustomDaysView extends React.Component<CustomDaysViewProps> {
value: number value: number
) => { ) => {
const date = (this.props.selectedDate || this.props.viewDate).clone(); const date = (this.props.selectedDate || this.props.viewDate).clone();
date[type](value);
date[type](value);
const updatedDate = date.clone();
this.props.setDateTimeState({ this.props.setDateTimeState({
viewDate: date.clone(), viewDate: updatedDate,
selectedDate: date.clone() selectedDate: updatedDate
}); });
if (!this.props.requiredConfirm) { if (!this.props.requiredConfirm) {
this.props.onChange(date); this.props.onChange(date, 'time');
} }
}; };

View File

@ -530,3 +530,76 @@ test('Tree: item disabled', async () => {
transfer: '' transfer: ''
}); });
}); });
test('Tree: single value mode should not render input when searchable enabled and default value settled', async () => {
const {container} = render(
amisRender({
type: 'container',
body: [
{
"type": "tree-select",
"name": "tree",
"label": "Tree",
"searchable": true,
"value": 2,
"inputClassName": "single",
"options": [
{
"label": "Folder A",
"value": 1,
"children": [
{
"label": "file A",
"value": 2
},
{
"label": "file B",
"value": 3
}
]
}
]
},
{
"type": "tree-select",
"name": "tree2",
"label": "Tree2",
"searchable": true,
"value": "2,4",
"multiple": true,
"inputClassName": "multiple",
"options": [
{
"label": "Folder A",
"value": 1,
"children": [
{
"label": "file A",
"value": 2
},
{
"label": "file B",
"value": 3
}
]
},
{
"label": "file C",
"value": 4
}
]
}
]
},
{},
makeEnv({})
));
const singleModeInput = container.querySelector('.single .cxd-ResultBox-value-input');
const multipleModeInput = container.querySelector('.multiple .cxd-ResultBox-value-input');
/** 单选模式且已选值,不应该再有 input */
expect(singleModeInput).not.toBeInTheDocument();
/** 多选模式始终都有 input */
expect(multipleModeInput).toBeInTheDocument();
})

View File

@ -695,8 +695,13 @@ export default class TreeSelectControl extends React.Component<
env, env,
loadingConfig loadingConfig
} = this.props; } = this.props;
const {isOpened} = this.state; const {isOpened} = this.state;
const resultValue = multiple
? selectedOptions
: selectedOptions.length
? this.renderItem(selectedOptions[0])
: '';
return ( return (
<div ref={this.container} className={cx(`TreeSelectControl`, className)}> <div ref={this.container} className={cx(`TreeSelectControl`, className)}>
<ResultBox <ResultBox
@ -716,13 +721,7 @@ export default class TreeSelectControl extends React.Component<
'is-opened': this.state.isOpened, 'is-opened': this.state.isOpened,
'is-disabled': disabled 'is-disabled': disabled
})} })}
result={ result={resultValue}
multiple
? selectedOptions
: selectedOptions.length
? this.renderItem(selectedOptions[0])
: ''
}
onResultClick={this.handleOutClick} onResultClick={this.handleOutClick}
value={this.state.inputValue} value={this.state.inputValue}
onChange={this.handleInputChange} onChange={this.handleInputChange}
@ -733,7 +732,11 @@ export default class TreeSelectControl extends React.Component<
onBlur={this.handleBlur} onBlur={this.handleBlur}
onKeyDown={this.handleInputKeyDown} onKeyDown={this.handleInputKeyDown}
clearable={clearable} clearable={clearable}
allowInput={!mobileUI && (searchable || isEffectiveApi(autoComplete))} allowInput={
!mobileUI &&
(searchable || isEffectiveApi(autoComplete)) &&
(multiple || !resultValue)
}
hasDropDownArrow hasDropDownArrow
readOnly={mobileUI} readOnly={mobileUI}
mobileUI={mobileUI} mobileUI={mobileUI}