* 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

@ -254,7 +254,7 @@ order: 14
可以通过 `disabledDate` 字符函数来控制,比如不允许选择周一、周六、周日
函数签名: `(currentDate: moment.Moment, props: any) => boolean`
函数签名: `(currentDate: moment.Moment, props: any) => boolean`
示例: `"return currentDate.day() == 1 || currentDate.day() == 0 || currentDate.day() == 6"`
```schema: scope="body"

View File

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

View File

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

View File

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

View File

@ -19,9 +19,11 @@ import type {RendererEnv} from 'amis-core';
import Picker from '../Picker';
import {PickerOption} from '../PickerColumn';
import {DateType} from './Calendar';
import type {TimeScale} from './TimeView';
import {Icon} from '../icons';
import type {TimeScale} from './TimeView';
import type {ViewMode} from './Calendar';
interface CustomDaysViewProps extends LocaleProps {
classPrefix?: string;
prevIcon?: string;
@ -37,7 +39,10 @@ interface CustomDaysViewProps extends LocaleProps {
isEndDate?: boolean;
renderDay?: Function;
onClose?: () => void;
onChange: (value: moment.Moment) => void;
onChange: (
value: moment.Moment,
viewMode?: Extract<ViewMode, 'time'>
) => void;
onConfirm?: (value: number[], types: DateType[]) => void;
setDateTimeState: (state: any) => void;
showTime: () => void;
@ -294,6 +299,7 @@ export class CustomDaysView extends React.Component<CustomDaysViewProps> {
showTime = () => {
const {selectedDate, viewDate, timeFormat} = this.props;
return (
<div key="stb" className="rdtShowTime">
{(selectedDate || viewDate || moment()).format(timeFormat)}
@ -306,15 +312,16 @@ export class CustomDaysView extends React.Component<CustomDaysViewProps> {
value: number
) => {
const date = (this.props.selectedDate || this.props.viewDate).clone();
date[type](value);
date[type](value);
const updatedDate = date.clone();
this.props.setDateTimeState({
viewDate: date.clone(),
selectedDate: date.clone()
viewDate: updatedDate,
selectedDate: updatedDate
});
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: ''
});
});
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,
loadingConfig
} = this.props;
const {isOpened} = this.state;
const resultValue = multiple
? selectedOptions
: selectedOptions.length
? this.renderItem(selectedOptions[0])
: '';
return (
<div ref={this.container} className={cx(`TreeSelectControl`, className)}>
<ResultBox
@ -716,13 +721,7 @@ export default class TreeSelectControl extends React.Component<
'is-opened': this.state.isOpened,
'is-disabled': disabled
})}
result={
multiple
? selectedOptions
: selectedOptions.length
? this.renderItem(selectedOptions[0])
: ''
}
result={resultValue}
onResultClick={this.handleOutClick}
value={this.state.inputValue}
onChange={this.handleInputChange}
@ -733,7 +732,11 @@ export default class TreeSelectControl extends React.Component<
onBlur={this.handleBlur}
onKeyDown={this.handleInputKeyDown}
clearable={clearable}
allowInput={!mobileUI && (searchable || isEffectiveApi(autoComplete))}
allowInput={
!mobileUI &&
(searchable || isEffectiveApi(autoComplete)) &&
(multiple || !resultValue)
}
hasDropDownArrow
readOnly={mobileUI}
mobileUI={mobileUI}