From 1d6bdbbeaed10979b0e13530902cbbd582d8981e Mon Sep 17 00:00:00 2001 From: WB01081293 Date: Tue, 30 Jan 2024 11:14:10 +0800 Subject: [PATCH 1/3] feat(DatePicker2): should support prohibit minutes and seconds close #3617 --- .../__docs__/demo/disabledDate/index.tsx | 14 ++ .../date-picker2/__tests__/index-spec.js | 12 ++ components/date-picker2/panels/date-panel.jsx | 17 +- .../date-picker2/panels/range-panel.jsx | 16 +- components/date-picker2/panels/time-panel.jsx | 160 +++++++++++++++++- 5 files changed, 213 insertions(+), 6 deletions(-) diff --git a/components/date-picker2/__docs__/demo/disabledDate/index.tsx b/components/date-picker2/__docs__/demo/disabledDate/index.tsx index ffedb21971..fbcebc5454 100644 --- a/components/date-picker2/__docs__/demo/disabledDate/index.tsx +++ b/components/date-picker2/__docs__/demo/disabledDate/index.tsx @@ -22,6 +22,13 @@ const disabledDate = function (date, mode) { } }; +const disabledCurrentTime = function (date) { + return ( + date.valueOf() < Number(dayjs('2024-01-22 13:30:10').valueOf()) || + date.valueOf() > Number(dayjs('2024-01-28 18:30:10').valueOf()) + ); +}; + ReactDOM.render(
console.log(val)} /> @@ -36,6 +43,13 @@ ReactDOM.render( console.log(val)} />

+ console.log(val)} + /> +
+
, mountNode ); diff --git a/components/date-picker2/__tests__/index-spec.js b/components/date-picker2/__tests__/index-spec.js index 5ea28490ae..773d5c69fe 100644 --- a/components/date-picker2/__tests__/index-spec.js +++ b/components/date-picker2/__tests__/index-spec.js @@ -1128,6 +1128,18 @@ describe('Picker', () => { wrapper = mount(); assert(wrapper.find('.next-icon-loading').length === 1); }); + + it('should support prohibit minutes and seconds', () => { + const disabledDate = function (date) { + return date.valueOf() < Number(moment('2024-01-22 13:30:10').valueOf()) || date.valueOf() > Number(moment('2024-01-28 18:30:10').valueOf()) + }; + wrapper = mount( console.log(val)} />); + clickDate('2024-01-22 13:30:09'); + clickOk(); + clickDate('2024-01-28 18:30:11'); + clickOk(); + assert.deepEqual(getStrValue(), [``, ``]); + }); }); }); diff --git a/components/date-picker2/panels/date-panel.jsx b/components/date-picker2/panels/date-panel.jsx index 17b49f22ee..6c8838bc9a 100644 --- a/components/date-picker2/panels/date-panel.jsx +++ b/components/date-picker2/panels/date-panel.jsx @@ -72,6 +72,19 @@ class DatePanel extends React.Component { func.invoke(this.props, 'onPanelChange', [v, mode]); }; + checkValueDisabled = (v, mode) => { + const { showTime, disabledDate, panelMode } = this.props; + + if (showTime && mode === panelMode) { + return ( + disabledDate(v.hour(0).minute(0).second(0), mode) && + disabledDate(v.hour(23).minute(59).second(59), mode) + ); + } + + return disabledDate && disabledDate(v, mode); + }; + render() { const { mode, @@ -109,7 +122,7 @@ class DatePanel extends React.Component { colNum={showTime ? 6 : undefined} onSelect={this.handleSelect} onPanelChange={this.handlePanelChange} - disabledDate={disabledDate} + disabledDate={this.checkValueDisabled} dateCellRender={dateCellRender} /> {showTime && mode === panelMode ? ( @@ -119,6 +132,8 @@ class DatePanel extends React.Component { value={value || this.state.defaultTime} onSelect={this.onTimeSelect} disabledTime={disabledTime} + disabledDate={disabledDate} + panelMode={mode} timePanelProps={{ ..._disabledTime, ...timePanelProps }} /> ) : null} diff --git a/components/date-picker2/panels/range-panel.jsx b/components/date-picker2/panels/range-panel.jsx index c41eadca28..4377b3decb 100644 --- a/components/date-picker2/panels/range-panel.jsx +++ b/components/date-picker2/panels/range-panel.jsx @@ -145,7 +145,7 @@ class RangePanel extends React.Component { disabledDate, value: [begin, end], } = this.props; - + const unit = mode2unit(mode); return ( @@ -328,8 +328,17 @@ class RangePanel extends React.Component { }); }; + checkValueDisabled = (v, mode) => { + const disabledDate = this.props.justBeginInput ? this.props.disabledDate : this.disabledDate; + return ( + disabledDate(v.hour(0).minute(0).second(0), mode) && + disabledDate(v.hour(23).minute(59).second(59), mode) + ); + }; + + renderRangeTime = sharedProps => { - const { value, prefix, showTime, inputType, timePanelProps = {}, disabledTime } = this.props; + const { value, prefix, showTime, inputType, timePanelProps = {}, disabledTime, mode } = this.props; const className = classnames(`${prefix}range-picker2-panel`, { [`${prefix}range-picker2-panel-single`]: this.hasModeChanged, @@ -346,6 +355,7 @@ class RangePanel extends React.Component { @@ -356,6 +366,8 @@ class RangePanel extends React.Component { value={value[inputType] || this.state.defaultTime[inputType]} onSelect={this.onTimeSelect} disabledTime={disabledTime} + disabledDate={sharedProps.disabledDate} + panelMode={mode} timePanelProps={{ ..._disabledTime, ...timePanelProps }} /> ) : null} diff --git a/components/date-picker2/panels/time-panel.jsx b/components/date-picker2/panels/time-panel.jsx index acdb6bf65b..e3325d583b 100644 --- a/components/date-picker2/panels/time-panel.jsx +++ b/components/date-picker2/panels/time-panel.jsx @@ -7,6 +7,155 @@ import { func } from '../../util'; const DECADE_TIME_FORMAT = 'HH:mm:ss'; +const WithTimePanel = function (WrappedComponent) { + return class extends React.Component { + static propTypes = { + ...WrappedComponent.propTypes, + disabledDate: PT.func, + panelMode: PT.string, + }; + + constructor(props) { + super(props); + + let _disabledTime = false; + if (typeof props.disabledDate === 'function' && !props.value) { + _disabledTime = true; + } + + this.state = { + disabledTime: _disabledTime, + }; + } + + getDisabledStatus = () => { + const { value, timePanelProps } = this.props; + let disabled = this.state.disabledTime; + if (value) { + disabled = timePanelProps.disabled ?? false; + } + + return disabled; + }; + + disabledItems = list => index => { + return list.indexOf(index) >= 0; + }; + + getDisabledTime = () => { + const { timePanelProps, value, disabledDate, panelMode } = this.props; + const { disabledHours, disabledMinutes, disabledSeconds } = timePanelProps; + + if (disabledHours || disabledMinutes || disabledSeconds) { + return { + disabledHours, + disabledMinutes, + disabledSeconds, + }; + } + + if (value && typeof disabledDate === 'function') { + let newDate = value.clone(); + const hours = 24; + const minutesAndSeconds = 60; + const _disabledHours = []; + const _disabledMinutes = []; + const _disabledSeconds = []; + let currentHour = value.get('hour'); + let currentMinute = value.get('minute'); + + for (let i = 0; i < hours; i++) { + // console + // .log( + // '=====> ', + // disabledDate(newDate.hour(i).minute(0).second(0), panelMode), + // disabledDate(newDate.hour(i).minute(59).second(59), panelMode), + // newDate.format('YYYY-MM-DD HH:mm:ss'), + // newDate.hour(i).minute(0).second(0).format('YYYY-MM-DD HH:mm:ss'), + // newDate.hour(i).minute(59).second(59).format('YYYY-MM-DD HH:mm:ss') + // ); + + // 禁用小时 + if ( + disabledDate(newDate.hour(i).minute(0).second(0), panelMode) && + disabledDate(newDate.hour(i).minute(59).second(59), panelMode) + ) { + _disabledHours.push(i); + } + } + // console.log('=====> _disabledHours', _disabledHours); + if (_disabledHours.length && _disabledHours.length < hours) { + // 边界处理 + while (_disabledHours.indexOf(currentHour) > -1) { + currentHour = (currentHour + 1) % hours; + } + for (let i = 0; i < minutesAndSeconds; i++) { + // 从当前小时开始遍历 + if ( + disabledDate( + newDate.hour(currentHour).minute(i).second(0), + panelMode + ) && + disabledDate(newDate.hour(currentHour).minute(i).second(59), panelMode) + ) { + _disabledMinutes.push(i); + } + } + } + // console.log('=====> _disabledMinutes', _disabledMinutes); + if (_disabledMinutes.length && _disabledMinutes.length < minutesAndSeconds) { + // 边界处理 + while (_disabledMinutes.indexOf(currentMinute) > -1) { + currentMinute = (currentMinute + 1) % minutesAndSeconds; + } + for (let i = 0; i < minutesAndSeconds; i++) { + // 从当前时分开始遍历 + newDate = newDate.hour(currentHour).minute(currentMinute).second(i); + if (disabledDate(newDate, panelMode)) { + _disabledSeconds.push(i); + } + } + } + + // 当前选中时间落在禁用区 + if (disabledDate(value, panelMode)) { + // 边界处理 + let currentSecond = value.get('second'); + while (_disabledSeconds.indexOf(currentSecond) > -1) { + currentSecond = (currentSecond + 1) % minutesAndSeconds; + } + newDate = newDate.hour(currentHour).minute(currentMinute).second(currentSecond); + func.invoke(this.props, 'onSelect', [newDate]); + } + + return { + disabledHours: this.disabledItems(_disabledHours), + disabledMinutes: this.disabledItems(_disabledMinutes), + disabledSeconds: this.disabledItems(_disabledSeconds), + }; + } + + return null; + }; + + render() { + const { timePanelProps, ...rest } = this.props; + + // 开启 disabledDate 属性时,需要提前禁用时分秒 + // 如果“今天”是属于禁用时间的话,先选时分秒可能导致回填数据与预期不符合 + const disabled = this.getDisabledStatus(); + const disabledTime = this.getDisabledTime(); + + return ( + + ); + } + }; +}; + class TimePanel extends React.PureComponent { static propTypes = { rtl: PT.bool, @@ -68,9 +217,14 @@ class TimePanel extends React.PureComponent { const { showHour, showMinute, showSecond } = this.getShow(); return ( -
+
-
{value ? this.formater(value) : null}
+
+ {value ? this.formater(value) : null} +
Date: Wed, 6 Mar 2024 20:34:57 +0800 Subject: [PATCH 2/3] feat(DatePicker2): test --- .../__docs__/demo/disabledDate/index.tsx | 4 +- .../date-picker2/__tests__/index-spec.js | 50 +++++-- components/date-picker2/panels/time-panel.jsx | 129 ++---------------- components/date-picker2/util.js | 92 ++++++++++++- 4 files changed, 146 insertions(+), 129 deletions(-) diff --git a/components/date-picker2/__docs__/demo/disabledDate/index.tsx b/components/date-picker2/__docs__/demo/disabledDate/index.tsx index fbcebc5454..9a42a1daff 100644 --- a/components/date-picker2/__docs__/demo/disabledDate/index.tsx +++ b/components/date-picker2/__docs__/demo/disabledDate/index.tsx @@ -24,8 +24,8 @@ const disabledDate = function (date, mode) { const disabledCurrentTime = function (date) { return ( - date.valueOf() < Number(dayjs('2024-01-22 13:30:10').valueOf()) || - date.valueOf() > Number(dayjs('2024-01-28 18:30:10').valueOf()) + date.valueOf() < Number(dayjs().valueOf()) || + date.valueOf() > Number(dayjs().add(6, 'day').valueOf()) ); }; diff --git a/components/date-picker2/__tests__/index-spec.js b/components/date-picker2/__tests__/index-spec.js index 773d5c69fe..ce1ccfd80d 100644 --- a/components/date-picker2/__tests__/index-spec.js +++ b/components/date-picker2/__tests__/index-spec.js @@ -1130,15 +1130,49 @@ describe('Picker', () => { }); it('should support prohibit minutes and seconds', () => { - const disabledDate = function (date) { - return date.valueOf() < Number(moment('2024-01-22 13:30:10').valueOf()) || date.valueOf() > Number(moment('2024-01-28 18:30:10').valueOf()) + const div = document.createElement('div'); + document.body.appendChild(div); + const disabledDate = date => { + return ( + date.valueOf() < Number(moment('2024-01-22 13:30:12').valueOf()) || + date.valueOf() > Number(moment('2024-01-28 18:30:12').valueOf()) + ); }; - wrapper = mount( console.log(val)} />); - clickDate('2024-01-22 13:30:09'); - clickOk(); - clickDate('2024-01-28 18:30:11'); - clickOk(); - assert.deepEqual(getStrValue(), [``, ``]); + wrapper = mount( + console.log(val)} + defaultPanelValue={dayjs('2024-01-22 14:30:10')} + />, + { attachTo: div } + ); + const isDisabledNode = name => { + return div.querySelector(name).classList.contains('next-disabled'); + }; + + let startHour = isDisabledNode('.next-time-picker2-menu-hour > li[title="12"]'); + let startMinute = isDisabledNode('.next-time-picker2-menu-minute > li[title="29"]'); + let startSecond = isDisabledNode('.next-time-picker2-menu-second > li[title="11"]'); + assert(startHour && startMinute && startSecond); + + ReactTestUtils.Simulate.click(div.querySelector('td[title="2024-01-28"]')); + + let endHour = isDisabledNode('.next-time-picker2-menu-hour > li[title="19"]'); + ReactTestUtils.Simulate.click( + div.querySelector('.next-time-picker2-menu-hour > li[title="18"]') + ); + + let endMinute = isDisabledNode('.next-time-picker2-menu-minute > li[title="31"]'); + ReactTestUtils.Simulate.click( + div.querySelector('.next-time-picker2-menu-minute > li[title="30"]') + ); + + let endSecond = isDisabledNode('.next-time-picker2-menu-second > li[title="13"]'); + + assert(endHour && endMinute && endSecond); }); }); }); diff --git a/components/date-picker2/panels/time-panel.jsx b/components/date-picker2/panels/time-panel.jsx index e3325d583b..5f4bde2e99 100644 --- a/components/date-picker2/panels/time-panel.jsx +++ b/components/date-picker2/panels/time-panel.jsx @@ -4,6 +4,7 @@ import PT from 'prop-types'; import TimePickerPanel from '../../time-picker2/panel'; import SharedPT from '../prop-types'; import { func } from '../../util'; +import { getDisabledTime } from '../util' const DECADE_TIME_FORMAT = 'HH:mm:ss'; @@ -15,128 +16,22 @@ const WithTimePanel = function (WrappedComponent) { panelMode: PT.string, }; - constructor(props) { - super(props); - - let _disabledTime = false; - if (typeof props.disabledDate === 'function' && !props.value) { - _disabledTime = true; - } - - this.state = { - disabledTime: _disabledTime, - }; - } - getDisabledStatus = () => { - const { value, timePanelProps } = this.props; - let disabled = this.state.disabledTime; - if (value) { - disabled = timePanelProps.disabled ?? false; - } + const { value, timePanelProps, disabledDate } = this.props; - return disabled; - }; - - disabledItems = list => index => { - return list.indexOf(index) >= 0; - }; - - getDisabledTime = () => { - const { timePanelProps, value, disabledDate, panelMode } = this.props; - const { disabledHours, disabledMinutes, disabledSeconds } = timePanelProps; - - if (disabledHours || disabledMinutes || disabledSeconds) { - return { - disabledHours, - disabledMinutes, - disabledSeconds, - }; + // 直接禁用时间选择,权重最高 + if (timePanelProps.disabled) { + return true; } - if (value && typeof disabledDate === 'function') { - let newDate = value.clone(); - const hours = 24; - const minutesAndSeconds = 60; - const _disabledHours = []; - const _disabledMinutes = []; - const _disabledSeconds = []; - let currentHour = value.get('hour'); - let currentMinute = value.get('minute'); - - for (let i = 0; i < hours; i++) { - // console - // .log( - // '=====> ', - // disabledDate(newDate.hour(i).minute(0).second(0), panelMode), - // disabledDate(newDate.hour(i).minute(59).second(59), panelMode), - // newDate.format('YYYY-MM-DD HH:mm:ss'), - // newDate.hour(i).minute(0).second(0).format('YYYY-MM-DD HH:mm:ss'), - // newDate.hour(i).minute(59).second(59).format('YYYY-MM-DD HH:mm:ss') - // ); - - // 禁用小时 - if ( - disabledDate(newDate.hour(i).minute(0).second(0), panelMode) && - disabledDate(newDate.hour(i).minute(59).second(59), panelMode) - ) { - _disabledHours.push(i); - } - } - // console.log('=====> _disabledHours', _disabledHours); - if (_disabledHours.length && _disabledHours.length < hours) { - // 边界处理 - while (_disabledHours.indexOf(currentHour) > -1) { - currentHour = (currentHour + 1) % hours; - } - for (let i = 0; i < minutesAndSeconds; i++) { - // 从当前小时开始遍历 - if ( - disabledDate( - newDate.hour(currentHour).minute(i).second(0), - panelMode - ) && - disabledDate(newDate.hour(currentHour).minute(i).second(59), panelMode) - ) { - _disabledMinutes.push(i); - } - } - } - // console.log('=====> _disabledMinutes', _disabledMinutes); - if (_disabledMinutes.length && _disabledMinutes.length < minutesAndSeconds) { - // 边界处理 - while (_disabledMinutes.indexOf(currentMinute) > -1) { - currentMinute = (currentMinute + 1) % minutesAndSeconds; - } - for (let i = 0; i < minutesAndSeconds; i++) { - // 从当前时分开始遍历 - newDate = newDate.hour(currentHour).minute(currentMinute).second(i); - if (disabledDate(newDate, panelMode)) { - _disabledSeconds.push(i); - } - } - } - - // 当前选中时间落在禁用区 - if (disabledDate(value, panelMode)) { - // 边界处理 - let currentSecond = value.get('second'); - while (_disabledSeconds.indexOf(currentSecond) > -1) { - currentSecond = (currentSecond + 1) % minutesAndSeconds; - } - newDate = newDate.hour(currentHour).minute(currentMinute).second(currentSecond); - func.invoke(this.props, 'onSelect', [newDate]); - } - - return { - disabledHours: this.disabledItems(_disabledHours), - disabledMinutes: this.disabledItems(_disabledMinutes), - disabledSeconds: this.disabledItems(_disabledSeconds), - }; + // 开启自定义禁用日期 + if (typeof disabledDate === 'function') { + // 如果“今天”是属于禁用时间的话,先选时分秒可能导致回填数据与预期不符合,所以未选择值时,提前警用时间选择 + return !value; } - return null; - }; + return false; + } render() { const { timePanelProps, ...rest } = this.props; @@ -144,7 +39,7 @@ const WithTimePanel = function (WrappedComponent) { // 开启 disabledDate 属性时,需要提前禁用时分秒 // 如果“今天”是属于禁用时间的话,先选时分秒可能导致回填数据与预期不符合 const disabled = this.getDisabledStatus(); - const disabledTime = this.getDisabledTime(); + const disabledTime = getDisabledTime(this.props); return ( index => { + return list.indexOf(index) >= 0; + }; + + if (disabledHours || disabledMinutes || disabledSeconds) { + return { + disabledHours, + disabledMinutes, + disabledSeconds, + }; + } + + if (value && typeof disabledDate === 'function') { + let newDate = value.clone(); + const hours = 24; + const minutesAndSeconds = 60; + const _disabledHours = []; + const _disabledMinutes = []; + const _disabledSeconds = []; + let currentHour = value.get('hour'); + let currentMinute = value.get('minute'); + + for (let i = 0; i < hours; i++) { + // 禁用小时 + if ( + disabledDate(newDate.hour(i).minute(0).second(0), panelMode) && + disabledDate(newDate.hour(i).minute(59).second(59), panelMode) + ) { + _disabledHours.push(i); + } + } + + if (_disabledHours.length && _disabledHours.length < hours) { + // 边界处理 + while (_disabledHours.indexOf(currentHour) > -1) { + currentHour = (currentHour + 1) % hours; + } + for (let i = 0; i < minutesAndSeconds; i++) { + // 从当前小时开始遍历 + if ( + disabledDate(newDate.hour(currentHour).minute(i).second(0), panelMode) && + disabledDate(newDate.hour(currentHour).minute(i).second(59), panelMode) + ) { + _disabledMinutes.push(i); + } + } + } + + if (_disabledMinutes.length && _disabledMinutes.length < minutesAndSeconds) { + // 边界处理 + while (_disabledMinutes.indexOf(currentMinute) > -1) { + currentMinute = (currentMinute + 1) % minutesAndSeconds; + } + for (let i = 0; i < minutesAndSeconds; i++) { + // 从当前时分开始遍历 + newDate = newDate.hour(currentHour).minute(currentMinute).second(i); + if (disabledDate(newDate, panelMode)) { + _disabledSeconds.push(i); + } + } + } + + // 当前选中时间落在禁用区 + if (disabledDate(value, panelMode)) { + // 边界处理 + let currentSecond = value.get('second'); + while (_disabledSeconds.indexOf(currentSecond) > -1) { + currentSecond = (currentSecond + 1) % minutesAndSeconds; + } + newDate = newDate.hour(currentHour).minute(currentMinute).second(currentSecond); + func.invoke(props, 'onSelect', [newDate]); + } + + return { + disabledHours: disabledItems(_disabledHours), + disabledMinutes: disabledItems(_disabledMinutes), + disabledSeconds: disabledItems(_disabledSeconds), + }; + } + + return null; +} From c30ad1f1abae25bb955939ba02bcd5140ce29d09 Mon Sep 17 00:00:00 2001 From: WB01081293 Date: Tue, 19 Mar 2024 10:09:26 +0800 Subject: [PATCH 3/3] feat(DatePicker2): Control time, minutes and seconds close #3617 #3801 --- .../date-picker2/__tests__/index-spec.js | 128 ++++++++++++++---- components/date-picker2/panels/date-panel.jsx | 2 +- components/date-picker2/panels/time-panel.jsx | 36 +++-- components/date-picker2/picker.jsx | 12 +- components/date-picker2/util.js | 94 +++++++------ 5 files changed, 182 insertions(+), 90 deletions(-) diff --git a/components/date-picker2/__tests__/index-spec.js b/components/date-picker2/__tests__/index-spec.js index ce1ccfd80d..c53d9041f6 100644 --- a/components/date-picker2/__tests__/index-spec.js +++ b/components/date-picker2/__tests__/index-spec.js @@ -1128,51 +1128,127 @@ describe('Picker', () => { wrapper = mount(); assert(wrapper.find('.next-icon-loading').length === 1); }); - + it('should support prohibit minutes and seconds', () => { - const div = document.createElement('div'); - document.body.appendChild(div); + let value; const disabledDate = date => { return ( date.valueOf() < Number(moment('2024-01-22 13:30:12').valueOf()) || date.valueOf() > Number(moment('2024-01-28 18:30:12').valueOf()) ); }; + const handleChange = date => { + if (Array.isArray(date)) { + assert(date[0].valueOf() === Number(moment(value[0]).valueOf()), 'The start time should be the same') + assert(date[1].valueOf() === Number(moment(value[1]).valueOf()), 'The end time should be the same') + } else { + assert(date.valueOf() === Number(moment(value).valueOf())) + } + }; + + const clickDateTime = (date) => { + const m = moment(date); + clickDate(m.format('YYYY-MM-DD')); + clickTime(m.hour(), 'hour'); + clickTime(m.minute(), 'minute'); + clickTime(m.second(), 'second'); + } + + const isDisabledNode = name => { + return hasClassNames(wrapper.find(name), 'next-disabled'); + }; + wrapper = mount( - console.log(val)} + onChange={handleChange} defaultPanelValue={dayjs('2024-01-22 14:30:10')} />, - { attachTo: div } ); - const isDisabledNode = name => { - return div.querySelector(name).classList.contains('next-disabled'); - }; + findInput(0).simulate('click'); + assert(wrapper.find('.next-date-picker2-wrapper').exists()); + [22, 23, 24, 25, 26, 27, 28].forEach(item => { + assert(!hasClassNames(findDate(`2024-01-${item}`), 'next-calendar2-cell-disabled')); + }) + assert(hasClassNames(findDate('2024-01-21'), 'next-calendar2-cell-disabled')); + assert(hasClassNames(findDate('2024-01-29'), 'next-calendar2-cell-disabled')); + + wrapper.setProps({ value: '2024-01-22 13:30:10' }); + assert(wrapper.find('button.next-date-picker2-footer-ok').prop('disabled'), 'Select date to 2024-01-22 13:30:10, confirm button should be disabled'); + wrapper.setProps({ value: '2024-01-28 18:30:13' }); + assert(wrapper.find('button.next-date-picker2-footer-ok').prop('disabled'), 'Select date to 2024-01-28 18:30:13, confirm button should be disabled'); + value = '2024-01-22 14:30:10'; + clickDateTime(value); + assert(!wrapper.find('button.next-date-picker2-footer-ok').prop('disabled'), 'Select date to 2024-01-22 14:30:10, confirm button should not be disabled'); + clickOk(); - let startHour = isDisabledNode('.next-time-picker2-menu-hour > li[title="12"]'); - let startMinute = isDisabledNode('.next-time-picker2-menu-minute > li[title="29"]'); - let startSecond = isDisabledNode('.next-time-picker2-menu-second > li[title="11"]'); - assert(startHour && startMinute && startSecond); + wrapper.setProps({ + disabledTime: { + disabledHours: i => i < 5, + disabledMinutes: i => i < 10, + disabledSeconds: i => i < 10 + }, + timePanelProps: { + disabledHours: i => i > 14, + disabledMinutes: i => i > 30, + disabledSeconds: i => i > 30 + } + }); - ReactTestUtils.Simulate.click(div.querySelector('td[title="2024-01-28"]')); + clickDate('2024-01-23'); + wrapper.find('ul.next-time-picker2-menu-hour li.next-time-picker2-menu-item').forEach((item) => { + if (item.prop('title') > 14 || item.prop('title') < 5) { + assert(hasClassNames(item, 'next-disabled')); + } else { + assert(!hasClassNames(item, 'next-disabled')); + } + }); + wrapper.find('ul.next-time-picker2-menu-minute li.next-time-picker2-menu-item').forEach((item) => { + if (item.prop('title') > 30 || item.prop('title') < 10) { + assert(hasClassNames(item, 'next-disabled')); + } else { + assert(!hasClassNames(item, 'next-disabled')); + } + }); + wrapper.find('ul.next-time-picker2-menu-second li.next-time-picker2-menu-item').forEach((item) => { + if (item.prop('title') > 30 || item.prop('title') < 10) { + assert(hasClassNames(item, 'next-disabled')); + } else { + assert(!hasClassNames(item, 'next-disabled')); + } + }); - let endHour = isDisabledNode('.next-time-picker2-menu-hour > li[title="19"]'); - ReactTestUtils.Simulate.click( - div.querySelector('.next-time-picker2-menu-hour > li[title="18"]') - ); + clickDate('2024-01-22'); + [12, 15].forEach(i => { + assert(hasClassNames(wrapper.find(`ul.next-time-picker2-menu-hour [title=${i}]`), 'next-disabled')) + }) + assert(!hasClassNames(wrapper.find('ul.next-time-picker2-menu-hour [title=14]'), 'next-disabled')); - let endMinute = isDisabledNode('.next-time-picker2-menu-minute > li[title="31"]'); - ReactTestUtils.Simulate.click( - div.querySelector('.next-time-picker2-menu-minute > li[title="30"]') + wrapper = mount( + console.log(val)} + defaultPanelValue={dayjs('2024-01-22 14:30:10')} + />, ); - - let endSecond = isDisabledNode('.next-time-picker2-menu-second > li[title="13"]'); - + findInput(0).simulate('click'); + assert(wrapper.find('.next-date-picker2-wrapper').exists()); + value = ['2024-01-22 13:30:12', '2024-01-28 18:30:12'] + clickDateTime(value[0]) + const startHour = isDisabledNode('.next-time-picker2-menu-hour > li[title=12]'); + const startMinute = isDisabledNode('.next-time-picker2-menu-minute > li[title=29]'); + const startSecond = isDisabledNode('.next-time-picker2-menu-second > li[title=11]'); + assert(startHour && startMinute && startSecond); + clickOk(); + + clickDateTime(value[1]) + const endHour = isDisabledNode('.next-time-picker2-menu-hour > li[title=19]'); + const endMinute = isDisabledNode('.next-time-picker2-menu-minute > li[title=31]'); + const endSecond = isDisabledNode('.next-time-picker2-menu-second > li[title=14]'); assert(endHour && endMinute && endSecond); + clickOk(); }); }); }); diff --git a/components/date-picker2/panels/date-panel.jsx b/components/date-picker2/panels/date-panel.jsx index 6c8838bc9a..1280b68eea 100644 --- a/components/date-picker2/panels/date-panel.jsx +++ b/components/date-picker2/panels/date-panel.jsx @@ -75,7 +75,7 @@ class DatePanel extends React.Component { checkValueDisabled = (v, mode) => { const { showTime, disabledDate, panelMode } = this.props; - if (showTime && mode === panelMode) { + if (showTime && disabledDate && mode === panelMode) { return ( disabledDate(v.hour(0).minute(0).second(0), mode) && disabledDate(v.hour(23).minute(59).second(59), mode) diff --git a/components/date-picker2/panels/time-panel.jsx b/components/date-picker2/panels/time-panel.jsx index 5f4bde2e99..34c126a42e 100644 --- a/components/date-picker2/panels/time-panel.jsx +++ b/components/date-picker2/panels/time-panel.jsx @@ -4,7 +4,7 @@ import PT from 'prop-types'; import TimePickerPanel from '../../time-picker2/panel'; import SharedPT from '../prop-types'; import { func } from '../../util'; -import { getDisabledTime } from '../util' +import { getDisabledTime, isValueChanged } from '../util' const DECADE_TIME_FORMAT = 'HH:mm:ss'; @@ -16,35 +16,31 @@ const WithTimePanel = function (WrappedComponent) { panelMode: PT.string, }; - getDisabledStatus = () => { - const { value, timePanelProps, disabledDate } = this.props; - - // 直接禁用时间选择,权重最高 - if (timePanelProps.disabled) { - return true; + constructor(props) { + super(props); + const disabledTime = getDisabledTime(this.props); + this.state = { + ...disabledTime } + } - // 开启自定义禁用日期 - if (typeof disabledDate === 'function') { - // 如果“今天”是属于禁用时间的话,先选时分秒可能导致回填数据与预期不符合,所以未选择值时,提前警用时间选择 - return !value; + componentDidUpdate(prevProps) { + if (isValueChanged(this.props.value, prevProps.value)) { + const disabledTime = getDisabledTime(this.props); + this.setState({ + ...disabledTime + }); } - - return false; } render() { const { timePanelProps, ...rest } = this.props; - - // 开启 disabledDate 属性时,需要提前禁用时分秒 - // 如果“今天”是属于禁用时间的话,先选时分秒可能导致回填数据与预期不符合 - const disabled = this.getDisabledStatus(); - const disabledTime = getDisabledTime(this.props); + const { disabledHours, disabledMinutes, disabledSeconds } = this.state; return ( ); } @@ -110,7 +106,7 @@ class TimePanel extends React.PureComponent { render() { const { prefix, rtl, locale, timePanelProps = {}, value } = this.props; const { showHour, showMinute, showSecond } = this.getShow(); - + return (
); + const options = { + mode, + disabledDate, + disabledHours: timePanelProps && timePanelProps.disabledHours, + disabledMinutes: timePanelProps && timePanelProps.disabledMinutes, + disabledSeconds: timePanelProps && timePanelProps.disabledSeconds + } + // 底部节点 - const oKable = !!(isRange ? inputValue && inputValue[inputType] : inputValue); + const oKable = !disableDateTime(isRange ? inputValue && inputValue[inputType] : inputValue, options); const shouldShowFooter = showOk || preset || extraFooterRender; const footerNode = shouldShowFooter ? ( diff --git a/components/date-picker2/util.js b/components/date-picker2/util.js index c5e8959d69..f557a8cc09 100644 --- a/components/date-picker2/util.js +++ b/components/date-picker2/util.js @@ -1,4 +1,4 @@ -import { datejs, func } from '../util'; +import { datejs } from '../util'; import { DATE_INPUT_TYPE } from './constant'; export function setTime(targetVal, sourceVal) { @@ -50,24 +50,51 @@ export function fmtValue(value, fmt) { export function isValueChanged(newValue, oldValue) { return Array.isArray(newValue) ? isValueChanged(newValue[0], oldValue && oldValue[0]) || - isValueChanged(newValue[1], oldValue && oldValue[1]) + isValueChanged(newValue[1], oldValue && oldValue[1]) : newValue !== oldValue && !datejs(newValue).isSame(oldValue); } -export function getDisabledTime(props) { - const { timePanelProps, value, disabledDate, panelMode } = props; - const { disabledHours, disabledMinutes, disabledSeconds } = timePanelProps; - - const disabledItems = list => index => { - return list.indexOf(index) >= 0; - }; +/** + * 判读时间是否被禁用 + * @param {dayjs.ConfigType} value + * @param {object} param + * @returns {boolean} + */ +export function disableDateTime(value, { + mode, + inputType, + disabledDate, + disabledHours, + disabledMinutes, + disabledSeconds, + disabledTime = {} +}) { + value = datejs.isDayjs(value) ? value : datejs(value); + + const _disabledTime = typeof disabledTime === 'function' ? disabledTime(value, inputType) : disabledTime; + + return ( + (typeof disabledDate === 'function' && disabledDate(value, mode)) || + (typeof disabledHours === 'function' && disabledHours(value.get('hour'))) || + (typeof disabledMinutes === 'function' && disabledMinutes(value.get('minute'))) || + (typeof disabledSeconds === 'function' && disabledSeconds(value.get('second'))) || + (typeof _disabledTime.disabledHours === 'function' && _disabledTime.disabledHours(value.get('hour'))) || + (typeof _disabledTime.disabledMinutes === 'function' && _disabledTime.disabledMinutes(value.get('minute'))) || + (typeof _disabledTime.disabledSeconds === 'function' && _disabledTime.disabledSeconds(value.get('second'))) + ); +} - if (disabledHours || disabledMinutes || disabledSeconds) { - return { - disabledHours, - disabledMinutes, - disabledSeconds, - }; +export function getDisabledTime(props) { + const { timePanelProps, value, disabledDate, panelMode, disabledTime = {}, inputType } = props; + const { disabledHours, disabledMinutes, disabledSeconds } = timePanelProps || {}; + + const _disabledTime = typeof disabledTime === 'function' ? disabledTime(value, inputType) : disabledTime; + + const disabledItems = (list) => index => list.indexOf(index) >= 0; + const executedFunc = (...args) => { + return (index) => { + return args.some(func => typeof func === 'function' ? func(index) : false) + } } if (value && typeof disabledDate === 'function') { @@ -77,8 +104,8 @@ export function getDisabledTime(props) { const _disabledHours = []; const _disabledMinutes = []; const _disabledSeconds = []; - let currentHour = value.get('hour'); - let currentMinute = value.get('minute'); + const currentHour = value.get('hour'); + const currentMinute = value.get('minute'); for (let i = 0; i < hours; i++) { // 禁用小时 @@ -91,10 +118,6 @@ export function getDisabledTime(props) { } if (_disabledHours.length && _disabledHours.length < hours) { - // 边界处理 - while (_disabledHours.indexOf(currentHour) > -1) { - currentHour = (currentHour + 1) % hours; - } for (let i = 0; i < minutesAndSeconds; i++) { // 从当前小时开始遍历 if ( @@ -107,10 +130,6 @@ export function getDisabledTime(props) { } if (_disabledMinutes.length && _disabledMinutes.length < minutesAndSeconds) { - // 边界处理 - while (_disabledMinutes.indexOf(currentMinute) > -1) { - currentMinute = (currentMinute + 1) % minutesAndSeconds; - } for (let i = 0; i < minutesAndSeconds; i++) { // 从当前时分开始遍历 newDate = newDate.hour(currentHour).minute(currentMinute).second(i); @@ -119,24 +138,17 @@ export function getDisabledTime(props) { } } } - - // 当前选中时间落在禁用区 - if (disabledDate(value, panelMode)) { - // 边界处理 - let currentSecond = value.get('second'); - while (_disabledSeconds.indexOf(currentSecond) > -1) { - currentSecond = (currentSecond + 1) % minutesAndSeconds; - } - newDate = newDate.hour(currentHour).minute(currentMinute).second(currentSecond); - func.invoke(props, 'onSelect', [newDate]); - } - return { - disabledHours: disabledItems(_disabledHours), - disabledMinutes: disabledItems(_disabledMinutes), - disabledSeconds: disabledItems(_disabledSeconds), + disabledHours: executedFunc(disabledItems(_disabledHours), disabledHours, _disabledTime.disabledHours), + disabledMinutes: executedFunc(disabledItems(_disabledMinutes), disabledMinutes, _disabledTime.disabledMinutes), + disabledSeconds: executedFunc(disabledItems(_disabledSeconds), disabledSeconds, _disabledTime.disabledSeconds), }; } - return null; + return { + ..._disabledTime, + disabledHours, + disabledMinutes, + disabledSeconds, + }; }