mirror of
https://gitee.com/baidu/amis.git
synced 2024-12-02 03:48:13 +08:00
* fix: TreeSelect组件单选模式开启searchable绑定值后输入框仍然存在问题 * fix: 日期时间范围选择器的时间选择点击无效问题 (#8149)
This commit is contained in:
parent
d117f0f12b
commit
b3981f6846
@ -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;
|
||||||
|
@ -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,
|
||||||
|
@ -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') => {
|
||||||
|
@ -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');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
})
|
||||||
|
@ -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}
|
||||||
|
Loading…
Reference in New Issue
Block a user