mirror of
https://gitee.com/baidu/amis.git
synced 2024-12-02 03:48:13 +08:00
Merge pull request #8726 from lurunze1226/feat-date-time-picker-confirm
feat: InputDateTime在closeOnSelect为false时开启确认模式; chore: datetime类选择器首次选择时时间设置为当前值
This commit is contained in:
commit
19766cd093
@ -379,6 +379,34 @@ order: 14
|
||||
}
|
||||
```
|
||||
|
||||
## 确认模式
|
||||
|
||||
> `3.6.0`及以上版本
|
||||
|
||||
设置`"closeOnSelect": false`,点选日期时间后,不会自动关闭浮层,需要点击底部工具栏的确认才会关闭。点击**取消按钮**或者**浮层外部区域**也会关闭浮层,并将值重置为初始状态。
|
||||
|
||||
> 注意:该特性仅对`input-datetime`有效,其他日期时间组件无效。开启内嵌模式后,该特性无效。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "form",
|
||||
"api": "/api/mock2/form/saveForm",
|
||||
"debug": true,
|
||||
"body": [
|
||||
{
|
||||
"type": "input-datetime",
|
||||
"name": "datetime",
|
||||
"label": "日期时间",
|
||||
"shortcuts": ["yesterday", "today", "tomorrow"],
|
||||
"closeOnSelect": false,
|
||||
"valueFormat": "YYYY-MM-DD HH:mm:ss",
|
||||
"displayFormat": "YYYY-MM-DD HH:mm:ss",
|
||||
"clearable": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 属性表
|
||||
|
||||
除了支持 [普通表单项属性表](./formitem#%E5%B1%9E%E6%80%A7%E8%A1%A8) 中的配置以外,还支持下面一些配置
|
||||
|
@ -13,18 +13,22 @@ import {
|
||||
isExpression,
|
||||
FormulaExec,
|
||||
filterDate,
|
||||
string2regExp
|
||||
string2regExp,
|
||||
autobind
|
||||
} from 'amis-core';
|
||||
import PopUp from './PopUp';
|
||||
import {Overlay} from 'amis-core';
|
||||
import {ClassNamesFn, themeable, ThemeProps} from 'amis-core';
|
||||
import {themeable, ThemeProps} from 'amis-core';
|
||||
import Calendar from './calendar/Calendar';
|
||||
import {localeable, LocaleProps, TranslateFn} from 'amis-core';
|
||||
import {ucFirst} from 'amis-core';
|
||||
import CalendarMobile from './CalendarMobile';
|
||||
import Input from './Input';
|
||||
import type {PlainObject} from 'amis-core';
|
||||
import type {RendererEnv} from 'amis-core';
|
||||
import Button from './Button';
|
||||
|
||||
import type {Moment} from 'moment';
|
||||
import type {PlainObject, RendererEnv} from 'amis-core';
|
||||
import type {ChangeEventViewMode, MutableUnitOfTime} from './calendar/Calendar';
|
||||
|
||||
const availableShortcuts: {[propName: string]: any} = {
|
||||
now: {
|
||||
@ -344,6 +348,7 @@ export interface DatePickerState {
|
||||
inputValue: string | undefined; // 手动输入的值
|
||||
curTimeFormat: string; // 根据displayFormat / inputFormat 计算展示的时间粒度
|
||||
curDateFormat: string; // 根据displayFormat / inputFormat 计算展示的日期粒度
|
||||
isModified: boolean;
|
||||
}
|
||||
|
||||
export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
||||
@ -420,7 +425,8 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
||||
displayFormat || inputFormat
|
||||
) || '',
|
||||
curTimeFormat,
|
||||
curDateFormat
|
||||
curDateFormat,
|
||||
isModified: false
|
||||
} as DatePickerState;
|
||||
}
|
||||
|
||||
@ -490,6 +496,14 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
||||
}
|
||||
}
|
||||
|
||||
isConfirmMode() {
|
||||
const {closeOnSelect, embed, mobileUI} = this.props;
|
||||
const {curTimeFormat} = this.state;
|
||||
|
||||
/** 日期时间选择器才支持confirm */
|
||||
return closeOnSelect === false && !!curTimeFormat && !embed && !mobileUI;
|
||||
}
|
||||
|
||||
focus() {
|
||||
if (!this.dom) {
|
||||
return;
|
||||
@ -545,9 +559,22 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
||||
}
|
||||
|
||||
close() {
|
||||
this.setState({
|
||||
isOpened: false
|
||||
});
|
||||
const isConfirmMode = this.isConfirmMode();
|
||||
|
||||
if (isConfirmMode) {
|
||||
const {value, valueFormat, format, displayFormat, inputFormat} =
|
||||
this.props;
|
||||
|
||||
this.setState({
|
||||
value: normalizeDate(value, valueFormat || format),
|
||||
inputValue:
|
||||
normalizeDate(value, valueFormat || format)?.format(
|
||||
displayFormat || inputFormat
|
||||
) || ''
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({isOpened: false, isModified: false});
|
||||
}
|
||||
|
||||
clearValue(e: React.MouseEvent<any>) {
|
||||
@ -555,14 +582,14 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
||||
e.stopPropagation();
|
||||
const onChange = this.props.onChange;
|
||||
onChange('');
|
||||
this.setState({inputValue: ''});
|
||||
this.setState({inputValue: '', isModified: false});
|
||||
}
|
||||
|
||||
// 清空
|
||||
clear() {
|
||||
const onChange = this.props.onChange;
|
||||
onChange('');
|
||||
this.setState({inputValue: ''});
|
||||
this.setState({inputValue: '', isModified: false});
|
||||
}
|
||||
|
||||
// 重置
|
||||
@ -576,11 +603,16 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
||||
this.setState({
|
||||
inputValue: normalizeDate(resetValue, valueFormat || format)?.format(
|
||||
displayFormat || inputFormat || ''
|
||||
)
|
||||
),
|
||||
isModified: false
|
||||
});
|
||||
}
|
||||
|
||||
handleChange(value: moment.Moment) {
|
||||
/**
|
||||
* 如果为日期时间选择器,则单独处理时间选择事件,点击确认的时候才触发onChange
|
||||
*/
|
||||
@autobind
|
||||
handleConfirm() {
|
||||
const {
|
||||
onChange,
|
||||
format,
|
||||
@ -589,14 +621,12 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
||||
maxDate,
|
||||
inputFormat,
|
||||
displayFormat,
|
||||
closeOnSelect,
|
||||
utc,
|
||||
viewMode
|
||||
utc
|
||||
} = this.props;
|
||||
let value = this.state.value;
|
||||
const isConfirmMode = this.isConfirmMode();
|
||||
|
||||
const {curDateFormat, curTimeFormat} = this.state;
|
||||
|
||||
if (!moment.isMoment(value)) {
|
||||
if (!isConfirmMode || !value) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -611,17 +641,88 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
||||
? moment.utc(value).format(valueFormat || format)
|
||||
: value.format(valueFormat || format)
|
||||
);
|
||||
if (closeOnSelect && curDateFormat && !curTimeFormat) {
|
||||
this.close();
|
||||
}
|
||||
|
||||
this.setState({
|
||||
inputValue: utc
|
||||
? moment.utc(value).format(displayFormat || inputFormat)
|
||||
: value.format(displayFormat || inputFormat)
|
||||
: value.format(displayFormat || inputFormat),
|
||||
isOpened: false,
|
||||
isModified: true
|
||||
});
|
||||
}
|
||||
|
||||
handleChange(value: Moment, viewMode?: ChangeEventViewMode) {
|
||||
const {
|
||||
onChange,
|
||||
format,
|
||||
valueFormat,
|
||||
minDate,
|
||||
maxDate,
|
||||
inputFormat,
|
||||
displayFormat,
|
||||
closeOnSelect,
|
||||
utc,
|
||||
value: defaultValue
|
||||
} = this.props;
|
||||
const {curDateFormat, curTimeFormat, isModified} = this.state;
|
||||
const isConfirmMode = this.isConfirmMode();
|
||||
|
||||
if (!moment.isMoment(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (minDate && value && value.isBefore(minDate, 'second')) {
|
||||
value = minDate;
|
||||
} else if (maxDate && value && value.isAfter(maxDate, 'second')) {
|
||||
value = maxDate;
|
||||
}
|
||||
|
||||
/** 首次选择且当前未绑定值,则默认使用当前时间 */
|
||||
if (!defaultValue && !!curTimeFormat && !isModified) {
|
||||
const now = moment();
|
||||
const timePart: Record<MutableUnitOfTime, number> = {
|
||||
date: value.get('date'),
|
||||
hour: value.get('hour'),
|
||||
minute: value.get('minute'),
|
||||
second: value.get('second'),
|
||||
millisecond: value.get('millisecond')
|
||||
};
|
||||
|
||||
Object.keys(timePart).forEach((unit: MutableUnitOfTime) => {
|
||||
/** 首次选择时间,日期使用当前时间; 将未设置过的时间字段设置为当前值 */
|
||||
if (
|
||||
(unit === 'date' && viewMode === 'time') ||
|
||||
(unit !== 'date' && timePart[unit] === 0)
|
||||
) {
|
||||
timePart[unit] = now.get(unit);
|
||||
}
|
||||
});
|
||||
|
||||
value.set(timePart);
|
||||
}
|
||||
|
||||
const updatedValue = utc
|
||||
? moment.utc(value).format(valueFormat || format)
|
||||
: value.format(valueFormat || format);
|
||||
const updatedInputValue = utc
|
||||
? moment.utc(value).format(displayFormat || inputFormat)
|
||||
: value.format(displayFormat || inputFormat);
|
||||
|
||||
if (isConfirmMode) {
|
||||
this.setState({value, inputValue: updatedInputValue});
|
||||
this.inputValueCache = updatedInputValue;
|
||||
} else {
|
||||
onChange(updatedValue);
|
||||
|
||||
if (closeOnSelect && curDateFormat && !curTimeFormat) {
|
||||
this.close();
|
||||
}
|
||||
|
||||
this.setState({inputValue: updatedInputValue});
|
||||
}
|
||||
this.setState({isModified: true});
|
||||
}
|
||||
|
||||
// 手动输入日期
|
||||
inputChange(e: React.ChangeEvent<HTMLInputElement>) {
|
||||
const {
|
||||
@ -850,13 +951,24 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
||||
env,
|
||||
onClick,
|
||||
onMouseEnter,
|
||||
onMouseLeave
|
||||
onMouseLeave,
|
||||
closeOnSelect
|
||||
} = this.props;
|
||||
|
||||
const __ = this.props.translate;
|
||||
const {curTimeFormat, curDateFormat} = this.state;
|
||||
const isOpened = this.state.isOpened;
|
||||
const {curTimeFormat, curDateFormat, isOpened} = this.state;
|
||||
const isConfirmMode = this.isConfirmMode();
|
||||
let date: moment.Moment | undefined = this.state.value;
|
||||
let isConfirmBtnDisbaled = false;
|
||||
|
||||
if (isConfirmMode) {
|
||||
const lastModifiedValue = normalizeDate(value, valueFormat || format);
|
||||
|
||||
isConfirmBtnDisbaled =
|
||||
date && lastModifiedValue
|
||||
? moment(date).isSame(lastModifiedValue, 'second')
|
||||
: date === lastModifiedValue;
|
||||
}
|
||||
|
||||
const calendarMobile = (
|
||||
<CalendarMobile
|
||||
@ -1041,6 +1153,22 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
|
||||
onMouseLeave={onMouseLeave}
|
||||
// utc={utc}
|
||||
/>
|
||||
{isConfirmMode ? (
|
||||
<div className={`${ns}DateRangePicker-actions`}>
|
||||
<Button size="sm" onClick={this.close}>
|
||||
{__('cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
level="primary"
|
||||
size="sm"
|
||||
disabled={isConfirmBtnDisbaled}
|
||||
className={cx('m-l-sm')}
|
||||
onClick={this.handleConfirm}
|
||||
>
|
||||
{__('confirm')}
|
||||
</Button>
|
||||
</div>
|
||||
) : null}
|
||||
</PopOver>
|
||||
</Overlay>
|
||||
) : null}
|
||||
|
@ -32,7 +32,11 @@ import Button from './Button';
|
||||
|
||||
import type {Moment} from 'moment';
|
||||
import type {PlainObject, ThemeProps, LocaleProps} from 'amis-core';
|
||||
import type {ViewMode} from './calendar/Calendar';
|
||||
import type {
|
||||
ViewMode,
|
||||
ChangeEventViewMode,
|
||||
MutableUnitOfTime
|
||||
} from './calendar/Calendar';
|
||||
|
||||
export interface DateRangePickerProps extends ThemeProps, LocaleProps {
|
||||
className?: string;
|
||||
@ -890,24 +894,66 @@ export class DateRangePicker extends React.Component<
|
||||
type: 'start' | 'end';
|
||||
originValue?: moment.Moment;
|
||||
timeFormat?: string;
|
||||
subControlViewMode?: 'time';
|
||||
subControlViewMode?: ChangeEventViewMode;
|
||||
/** 自动初始化绑定值,用于首次选择且当前未绑定值,默认使用当前时间 */
|
||||
autoInitDefaultValue?: boolean;
|
||||
} = {type: 'start'}
|
||||
): moment.Moment {
|
||||
const {type, originValue, timeFormat, subControlViewMode} = options || {
|
||||
const {
|
||||
type,
|
||||
originValue,
|
||||
timeFormat,
|
||||
subControlViewMode,
|
||||
autoInitDefaultValue
|
||||
} = options || {
|
||||
type: 'start'
|
||||
};
|
||||
let value = date.clone();
|
||||
const {transform, data} = this.props;
|
||||
const transformFn =
|
||||
transform && typeof transform === 'string'
|
||||
? str2function(transform, 'value', 'config', 'props', 'data', 'moment')
|
||||
: transform;
|
||||
const {startDate, endDate} = this.state;
|
||||
|
||||
/** 此时为点选后的值初始化设置,不应该被内部转化逻辑和transformFn限制 */
|
||||
if (autoInitDefaultValue === true) {
|
||||
const now = moment();
|
||||
|
||||
/** 如果已经设置了结束时间且当前时间已经超出了结束时间,则开始时间不能超过结束时间 */
|
||||
if (!startDate && endDate && type === 'start' && now.isAfter(endDate)) {
|
||||
value = endDate.clone();
|
||||
return value;
|
||||
}
|
||||
|
||||
const timePart: Record<MutableUnitOfTime, number> = {
|
||||
date: value.get('date'),
|
||||
hour: value.get('hour'),
|
||||
minute: value.get('minute'),
|
||||
second: value.get('second'),
|
||||
millisecond: value.get('millisecond')
|
||||
};
|
||||
|
||||
Object.keys(timePart).forEach((unit: MutableUnitOfTime) => {
|
||||
/** 首次选择时间,日期使用当前时间; 将未设置过的时间字段设置为当前值 */
|
||||
if (
|
||||
(unit === 'date' && subControlViewMode === 'time') ||
|
||||
(unit !== 'date' && timePart[unit] === 0)
|
||||
) {
|
||||
timePart[unit] = now.get(unit);
|
||||
}
|
||||
});
|
||||
|
||||
value.set(timePart);
|
||||
return value;
|
||||
}
|
||||
|
||||
/** 日期时间选择器组件支持用户选择时间,如果用户手动选择了时间,则不需要走默认处理 */
|
||||
if (subControlViewMode && subControlViewMode === 'time') {
|
||||
return value;
|
||||
}
|
||||
|
||||
const transformFn =
|
||||
transform && typeof transform === 'string'
|
||||
? str2function(transform, 'value', 'config', 'props', 'data', 'moment')
|
||||
: transform;
|
||||
|
||||
// 没有初始值
|
||||
if (!originValue) {
|
||||
value = value[type === 'start' ? 'startOf' : 'endOf']('day');
|
||||
@ -951,19 +997,31 @@ export class DateRangePicker extends React.Component<
|
||||
*/
|
||||
handleStartDateChange(
|
||||
newValue: moment.Moment,
|
||||
subControlViewMode?: Extract<ViewMode, 'time'>
|
||||
subControlViewMode?: ChangeEventViewMode
|
||||
) {
|
||||
const {minDate, inputFormat, displayFormat, type} = this.props;
|
||||
let {startDate, endDateOpenedFirst, curTimeFormat: timeFormat} = this.state;
|
||||
const {
|
||||
minDate,
|
||||
inputFormat,
|
||||
displayFormat,
|
||||
type,
|
||||
value: defaultValue
|
||||
} = this.props;
|
||||
let {
|
||||
startDate,
|
||||
oldStartDate,
|
||||
endDateOpenedFirst,
|
||||
curTimeFormat: timeFormat
|
||||
} = this.state;
|
||||
if (minDate && newValue.isBefore(minDate)) {
|
||||
newValue = minDate;
|
||||
}
|
||||
|
||||
const date = this.filterDate(newValue, {
|
||||
type: 'start',
|
||||
originValue: startDate || minDate,
|
||||
timeFormat,
|
||||
subControlViewMode
|
||||
subControlViewMode,
|
||||
autoInitDefaultValue:
|
||||
!!timeFormat && newValue && (!oldStartDate || !startDate)
|
||||
});
|
||||
const newState = {
|
||||
startDate: date,
|
||||
@ -988,12 +1046,19 @@ export class DateRangePicker extends React.Component<
|
||||
*/
|
||||
handelEndDateChange(
|
||||
newValue: moment.Moment,
|
||||
subControlViewMode?: Extract<ViewMode, 'time'>
|
||||
subControlViewMode?: ChangeEventViewMode
|
||||
) {
|
||||
const {embed, inputFormat, displayFormat, type} = this.props;
|
||||
const {
|
||||
embed,
|
||||
inputFormat,
|
||||
displayFormat,
|
||||
type,
|
||||
value: defaultValue
|
||||
} = this.props;
|
||||
let {
|
||||
startDate,
|
||||
endDate,
|
||||
oldEndDate,
|
||||
endDateOpenedFirst,
|
||||
curTimeFormat: timeFormat
|
||||
} = this.state;
|
||||
@ -1003,8 +1068,11 @@ export class DateRangePicker extends React.Component<
|
||||
type: 'end',
|
||||
originValue: endDate,
|
||||
timeFormat,
|
||||
subControlViewMode
|
||||
subControlViewMode,
|
||||
autoInitDefaultValue:
|
||||
!!timeFormat && newValue && (!oldEndDate || !endDate)
|
||||
});
|
||||
|
||||
this.setState(
|
||||
{
|
||||
endDate: date,
|
||||
|
@ -14,7 +14,9 @@ import {
|
||||
import {PickerOption} from '../PickerColumn';
|
||||
import 'moment/locale/zh-cn';
|
||||
import 'moment/locale/de';
|
||||
|
||||
import type {RendererEnv} from 'amis-core';
|
||||
import type {unitOfTime} from 'moment';
|
||||
|
||||
/** 视图模式 */
|
||||
export type ViewMode = 'days' | 'months' | 'years' | 'time' | 'quarters';
|
||||
@ -26,6 +28,16 @@ export type DateType =
|
||||
| 'hours'
|
||||
| 'minutes'
|
||||
| 'seconds';
|
||||
|
||||
/** 底层View组件修改的值类型:time时间、days日期 */
|
||||
export type ChangeEventViewMode = Extract<ViewMode, 'time' | 'days'>;
|
||||
|
||||
/** 可改变的时间单位 */
|
||||
export type MutableUnitOfTime = Extract<
|
||||
unitOfTime.All,
|
||||
'date' | 'hour' | 'minute' | 'second' | 'millisecond'
|
||||
>;
|
||||
|
||||
export interface BoundaryObject {
|
||||
max: number;
|
||||
min: number;
|
||||
@ -69,7 +81,7 @@ interface BaseDatePickerProps {
|
||||
onMouseEnter?: (date: moment.Moment) => any;
|
||||
onMouseLeave?: (date: moment.Moment) => any;
|
||||
onClose?: () => void;
|
||||
onChange?: (value: any, viewMode?: Extract<ViewMode, 'time'>) => void;
|
||||
onChange?: (value: any, viewMode?: ChangeEventViewMode) => void;
|
||||
isEndDate?: boolean;
|
||||
minDate?: moment.Moment;
|
||||
maxDate?: moment.Moment;
|
||||
@ -595,7 +607,7 @@ class BaseDatePicker extends React.Component<
|
||||
}
|
||||
}
|
||||
|
||||
that.props.onChange(date);
|
||||
that.props.onChange(date, 'days');
|
||||
};
|
||||
|
||||
getDateBoundary = (currentDate: moment.Moment) => {
|
||||
|
@ -21,6 +21,7 @@ import {PickerOption} from '../PickerColumn';
|
||||
import {DateType} from './Calendar';
|
||||
import {Icon} from '../icons';
|
||||
|
||||
import type {Moment} from 'moment';
|
||||
import type {TimeScale} from './TimeView';
|
||||
import type {ViewMode} from './Calendar';
|
||||
|
||||
@ -252,12 +253,33 @@ export class CustomDaysView extends React.Component<CustomDaysViewProps> {
|
||||
|
||||
componentDidMount() {
|
||||
const {timeFormat, selectedDate, viewDate, isEndDate} = this.props;
|
||||
const date = selectedDate || (isEndDate ? viewDate.endOf('day') : viewDate);
|
||||
this.setupTime(date, timeFormat, 'init');
|
||||
}
|
||||
|
||||
componentDidUpdate(
|
||||
prevProps: Readonly<CustomDaysViewProps>,
|
||||
prevState: Readonly<{}>,
|
||||
snapshot?: any
|
||||
): void {
|
||||
const currentDate = this.props.selectedDate;
|
||||
|
||||
if (
|
||||
moment.isMoment(currentDate) &&
|
||||
currentDate.isValid() &&
|
||||
!currentDate.isSame(prevProps.selectedDate)
|
||||
) {
|
||||
const {timeFormat} = this.props;
|
||||
this.setupTime(currentDate, timeFormat);
|
||||
}
|
||||
}
|
||||
|
||||
setupTime(date: Moment, timeFormat: string, mode?: 'init') {
|
||||
const formatMap = {
|
||||
hours: 'HH',
|
||||
minutes: 'mm',
|
||||
seconds: 'ss'
|
||||
};
|
||||
const date = selectedDate || (isEndDate ? viewDate.endOf('day') : viewDate);
|
||||
timeFormat.split(':').forEach((format, i) => {
|
||||
const type = /h/i.test(format)
|
||||
? 'hours'
|
||||
@ -271,7 +293,7 @@ export class CustomDaysView extends React.Component<CustomDaysViewProps> {
|
||||
type,
|
||||
parseInt(date.format(formatMap[type]), 10),
|
||||
i,
|
||||
'init'
|
||||
mode
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -81,3 +81,152 @@ test('Renderer:datetime displayFormat valueFormat', async () => {
|
||||
moment(1559826660, 'X').format('YYYY/MM/DD HH:mm:ss')
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* CASE: 日期时间选择器确认模式
|
||||
* 前提条件:
|
||||
* - 当前组件为input-datetime类型
|
||||
* - closeOnSelect为false
|
||||
* - 未开启内嵌模式
|
||||
* - 非移动端交互
|
||||
* 预期:
|
||||
* 1. 选择日期后,点击取消按钮,值重置
|
||||
* 2. 选择日期后,点击确定按钮,值更新
|
||||
*/
|
||||
test('Renderer:InputDateTime confirm mode', async () => {
|
||||
const {container} = render(
|
||||
amisRender({
|
||||
type: 'form',
|
||||
body: [
|
||||
{
|
||||
"name": "datetime",
|
||||
"label": "日期",
|
||||
"type": "input-datetime",
|
||||
"closeOnSelect": false
|
||||
}
|
||||
],
|
||||
title: 'The form',
|
||||
actions: []
|
||||
}, {}, makeEnv({}))
|
||||
);
|
||||
|
||||
const trigger = container.querySelector('.cxd-DatePicker')!;
|
||||
const inputEl = (container.querySelector(".cxd-DatePicker-input") as HTMLInputElement)!;
|
||||
const getCancelBtn = () => (container.querySelector('.cxd-DateRangePicker-actions > button[type=button]')!);
|
||||
const getConfirmBtn = () => (container.querySelector('.cxd-DateRangePicker-actions > .cxd-Button.cxd-Button--primary')!);
|
||||
expect(trigger).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(trigger);
|
||||
wait(200);
|
||||
/** 未选择新值,确认按钮禁用 */
|
||||
expect(getConfirmBtn()).toHaveClass('is-disabled');
|
||||
|
||||
let todayEl = document.querySelector('.rdtDay.rdtToday')!;
|
||||
let yesterdayEl = todayEl?.previousSibling!;
|
||||
let tomorrowEl = todayEl?.nextSibling!;
|
||||
|
||||
if (yesterdayEl) {
|
||||
fireEvent.click(yesterdayEl);
|
||||
}
|
||||
else {
|
||||
fireEvent.click(tomorrowEl);
|
||||
}
|
||||
wait(200);
|
||||
|
||||
/** 选择日期之后禁用消失 */
|
||||
expect(getConfirmBtn()).not.toHaveClass('is-disabled');
|
||||
|
||||
fireEvent.click(getCancelBtn());
|
||||
wait(200);
|
||||
/** 取消之后重置值 */
|
||||
expect(inputEl?.value).toEqual('');
|
||||
|
||||
fireEvent.click(trigger);
|
||||
wait(200);
|
||||
|
||||
todayEl = document.querySelector('.rdtDay.rdtToday')!;
|
||||
yesterdayEl = todayEl?.previousSibling!;
|
||||
tomorrowEl = todayEl?.nextSibling!;
|
||||
|
||||
if (yesterdayEl) {
|
||||
fireEvent.click(yesterdayEl);
|
||||
}
|
||||
else {
|
||||
fireEvent.click(tomorrowEl);
|
||||
}
|
||||
wait(200);
|
||||
|
||||
fireEvent.click(getConfirmBtn());
|
||||
wait(200);
|
||||
/** 确定之后有值 */
|
||||
expect(inputEl?.value).not.toEqual('');
|
||||
}, 7000);
|
||||
|
||||
/**
|
||||
* CASE: 日期时间选择器首次选择日期或时间后,时间自动设置为当前时间
|
||||
* 前提条件:
|
||||
* - 当前组件为input-datetime或者input-datetime-range类型
|
||||
* - 当前组件未绑定值
|
||||
* - 当前操作为首次编辑
|
||||
* 预期:
|
||||
* 1. 选择日期后,时间自动设置为当前时间
|
||||
* 2. 选择时间后(H、m、s),所选择时间为点选值,其他时间字段自动设置为当前时间
|
||||
* 3. 后续选择日期或者时间,不会改变点选值
|
||||
* 4. 如果为范围选择器,先选择结束时间,则开始时间不能超过结束时间
|
||||
*/
|
||||
test('Renderer:InputDateTime Picker selects date or time for the first time', async () => {
|
||||
const {container} = render(
|
||||
amisRender({
|
||||
type: 'form',
|
||||
body: [
|
||||
{
|
||||
"name": "datetime",
|
||||
"label": "日期",
|
||||
"type": "input-datetime",
|
||||
"valueFormat": "YYYY-MM-DD HH:mm:ss",
|
||||
"displayFormat": "YYYY-MM-DD HH:mm:ss",
|
||||
"closeOnSelect": false
|
||||
}
|
||||
],
|
||||
title: 'The form',
|
||||
actions: []
|
||||
}, {}, makeEnv({}))
|
||||
);
|
||||
|
||||
const trigger = container.querySelector('.cxd-DatePicker')!;
|
||||
const inputEl = (container.querySelector(".cxd-DatePicker-input") as HTMLInputElement)!;
|
||||
const getConfirmBtn = () => (container.querySelector('.cxd-DateRangePicker-actions > .cxd-Button.cxd-Button--primary')!);
|
||||
expect(trigger).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(trigger);
|
||||
wait(200);
|
||||
|
||||
let todayEl = document.querySelector('.rdtDay.rdtToday')!;
|
||||
let yesterdayEl = todayEl?.previousSibling!;
|
||||
let tomorrowEl = todayEl?.nextSibling!;
|
||||
|
||||
const currentTime = new Date();
|
||||
const currentSeconds = currentTime.getSeconds();
|
||||
|
||||
/** 跳过0秒,用于后续测试值和00:00:00的Diff */
|
||||
if (currentSeconds === 0) {
|
||||
wait(1000);
|
||||
}
|
||||
|
||||
if (yesterdayEl) {
|
||||
fireEvent.click(yesterdayEl);
|
||||
}
|
||||
else {
|
||||
fireEvent.click(tomorrowEl);
|
||||
}
|
||||
wait(200);
|
||||
const timeStr = inputEl?.value?.split(/\s+/)?.[1];
|
||||
/** 时间值设置为当前时间 */
|
||||
expect(timeStr !== '00:00:00').toEqual(true);
|
||||
|
||||
fireEvent.click(todayEl);
|
||||
wait(200);
|
||||
const newTimeStr = inputEl?.value?.split(/\s+/)?.[1];
|
||||
/** 切换日期后时间不会再变 */
|
||||
expect(newTimeStr === timeStr).toEqual(true);
|
||||
}, 7000);
|
||||
|
Loading…
Reference in New Issue
Block a user