From 6388423c9609e099565e51423271e333fab38a55 Mon Sep 17 00:00:00 2001 From: chenjiahan Date: Sun, 27 Sep 2020 16:30:35 +0800 Subject: [PATCH] refactor(DatePicker): refactor with composition api --- src/datetime-picker/DatePicker.js | 392 ++++++++++++++---------------- src/datetime-picker/TimePicker.js | 5 + src/datetime-picker/index.js | 6 +- 3 files changed, 195 insertions(+), 208 deletions(-) diff --git a/src/datetime-picker/DatePicker.js b/src/datetime-picker/DatePicker.js index 8cd041842..d16c75428 100644 --- a/src/datetime-picker/DatePicker.js +++ b/src/datetime-picker/DatePicker.js @@ -1,9 +1,11 @@ -import { createNamespace } from '../utils'; +import { ref, watch, computed, nextTick, onMounted } from 'vue'; +import { createNamespace, pick } from '../utils'; import { isDate } from '../utils/validate/date'; import { padZero } from '../utils/format/string'; import { times, sharedProps, getTrueValue, getMonthEndDay } from './utils'; import Picker from '../picker'; import { pickerProps } from '../picker/shared'; +import { useExpose } from '../composition/use-expose'; const currentYear = new Date().getFullYear(); const [createComponent] = createNamespace('date-picker'); @@ -29,161 +31,23 @@ export default createComponent({ emits: ['confirm', 'cancel', 'change', 'update:modelValue'], - data() { - return { - currentDate: this.formatValue(this.modelValue), - }; - }, - - computed: { - ranges() { - const { - maxYear, - maxDate, - maxMonth, - maxHour, - maxMinute, - } = this.getBoundary('max', this.currentDate); - - const { - minYear, - minDate, - minMonth, - minHour, - minMinute, - } = this.getBoundary('min', this.currentDate); - - let result = [ - { - type: 'year', - range: [minYear, maxYear], - }, - { - type: 'month', - range: [minMonth, maxMonth], - }, - { - type: 'day', - range: [minDate, maxDate], - }, - { - type: 'hour', - range: [minHour, maxHour], - }, - { - type: 'minute', - range: [minMinute, maxMinute], - }, - ]; - - switch (this.type) { - case 'date': - result = result.slice(0, 3); - break; - case 'year-month': - result = result.slice(0, 2); - break; - case 'month-day': - result = result.slice(1, 3); - break; - case 'datehour': - result = result.slice(0, 4); - break; - } - - if (this.columnsOrder) { - const columnsOrder = this.columnsOrder.concat( - result.map((column) => column.type) - ); - result.sort( - (a, b) => columnsOrder.indexOf(a.type) - columnsOrder.indexOf(b.type) - ); - } - - return result; - }, - - originColumns() { - return this.ranges.map(({ type, range: rangeArr }) => { - let values = times(rangeArr[1] - rangeArr[0] + 1, (index) => { - const value = padZero(rangeArr[0] + index); - return value; - }); - - if (this.filter) { - values = this.filter(type, values); - } - - return { - type, - values, - }; - }); - }, - - columns() { - return this.originColumns.map((column) => ({ - values: column.values.map((value) => - this.formatter(column.type, value) - ), - })); - }, - }, - - watch: { - filter: 'updateInnerValue', - minDate: 'updateInnerValue', - maxDate: 'updateInnerValue', - columns: 'updateColumnValue', - - currentDate(val) { - this.$emit('update:modelValue', val); - }, - - modelValue(val) { - val = this.formatValue(val); - - if (val.valueOf() !== this.currentDate.valueOf()) { - this.currentDate = val; - } - }, - }, - - mounted() { - this.updateColumnValue(); - - this.$nextTick(() => { - this.updateInnerValue(); - }); - }, - - methods: { - // @exposed-api - getPicker() { - return this.$refs.picker; - }, - - onConfirm() { - this.$emit('confirm', this.currentDate); - }, - - onCancel() { - this.$emit('cancel'); - }, - - formatValue(value) { + setup(props, { emit }) { + const formatValue = (value) => { if (!isDate(value)) { - value = this.minDate; + value = props.minDate; } - value = Math.max(value, this.minDate.getTime()); - value = Math.min(value, this.maxDate.getTime()); + value = Math.max(value, props.minDate.getTime()); + value = Math.min(value, props.maxDate.getTime()); return new Date(value); - }, + }; - getBoundary(type, value) { - const boundary = this[`${type}Date`]; + const picker = ref(); + const currentDate = ref(formatValue(props.modelValue)); + + const getBoundary = (type, value) => { + const boundary = props[`${type}Date`]; const year = boundary.getFullYear(); let month = 1; let date = 1; @@ -217,19 +81,134 @@ export default createComponent({ [`${type}Hour`]: hour, [`${type}Minute`]: minute, }; - }, + }; + + const ranges = computed(() => { + const { maxYear, maxDate, maxMonth, maxHour, maxMinute } = getBoundary( + 'max', + currentDate.value + ); + + const { minYear, minDate, minMonth, minHour, minMinute } = getBoundary( + 'min', + currentDate.value + ); + + let result = [ + { + type: 'year', + range: [minYear, maxYear], + }, + { + type: 'month', + range: [minMonth, maxMonth], + }, + { + type: 'day', + range: [minDate, maxDate], + }, + { + type: 'hour', + range: [minHour, maxHour], + }, + { + type: 'minute', + range: [minMinute, maxMinute], + }, + ]; + + switch (props.type) { + case 'date': + result = result.slice(0, 3); + break; + case 'year-month': + result = result.slice(0, 2); + break; + case 'month-day': + result = result.slice(1, 3); + break; + case 'datehour': + result = result.slice(0, 4); + break; + } + + if (props.columnsOrder) { + const columnsOrder = props.columnsOrder.concat( + result.map((column) => column.type) + ); + result.sort( + (a, b) => columnsOrder.indexOf(a.type) - columnsOrder.indexOf(b.type) + ); + } + + return result; + }); + + const originColumns = computed(() => + ranges.value.map(({ type, range: rangeArr }) => { + let values = times(rangeArr[1] - rangeArr[0] + 1, (index) => { + const value = padZero(rangeArr[0] + index); + return value; + }); + + if (props.filter) { + values = props.filter(type, values); + } + + return { + type, + values, + }; + }) + ); + + const columns = computed(() => + originColumns.value.map((column) => ({ + values: column.values.map((value) => + props.formatter(column.type, value) + ), + })) + ); + + const updateColumnValue = () => { + const { value } = currentDate; + const { formatter } = props; + + const values = originColumns.value.map((column) => { + switch (column.type) { + case 'year': + return formatter('year', `${value.getFullYear()}`); + case 'month': + return formatter('month', padZero(value.getMonth() + 1)); + case 'day': + return formatter('day', padZero(value.getDate())); + case 'hour': + return formatter('hour', padZero(value.getHours())); + case 'minute': + return formatter('minute', padZero(value.getMinutes())); + default: + // no default + return null; + } + }); + + nextTick(() => { + picker.value.setValues(values); + }); + }; + + const updateInnerValue = () => { + const { type } = props; + const indexes = picker.value.getIndexes(); - updateInnerValue() { - const { type } = this; - const indexes = this.getPicker().getIndexes(); const getValue = (type) => { let index = 0; - this.originColumns.forEach((column, columnIndex) => { + originColumns.value.forEach((column, columnIndex) => { if (type === column.type) { index = columnIndex; } }); - const { values } = this.originColumns[index]; + const { values } = originColumns.value[index]; return getTrueValue(values[indexes[index]]); }; @@ -237,7 +216,7 @@ export default createComponent({ let month; let day; if (type === 'month-day') { - year = this.currentDate.getFullYear(); + year = currentDate.value.getFullYear(); month = getValue('month'); day = getValue('day'); } else { @@ -262,63 +241,66 @@ export default createComponent({ } const value = new Date(year, month - 1, day, hour, minute); + currentDate.value = formatValue(value); + }; - this.currentDate = this.formatValue(value); - }, + const onConfirm = () => { + emit('confirm', currentDate.value); + }; - onChange(picker) { - this.updateInnerValue(); + const onCancel = () => { + emit('cancel'); + }; - this.$nextTick(() => { - this.$nextTick(() => { - this.$emit('change', picker); + const onChange = () => { + updateInnerValue(); + nextTick(() => { + nextTick(() => { + emit('change', picker.value); }); }); - }, + }; - updateColumnValue() { - const value = this.currentDate; - const { formatter } = this; - - const values = this.originColumns.map((column) => { - switch (column.type) { - case 'year': - return formatter('year', `${value.getFullYear()}`); - case 'month': - return formatter('month', padZero(value.getMonth() + 1)); - case 'day': - return formatter('day', padZero(value.getDate())); - case 'hour': - return formatter('hour', padZero(value.getHours())); - case 'minute': - return formatter('minute', padZero(value.getMinutes())); - default: - // no default - return null; - } - }); - - this.$nextTick(() => { - this.getPicker().setValues(values); - }); - }, - }, - - render() { - const props = {}; - Object.keys(pickerProps).forEach((key) => { - props[key] = this[key]; + onMounted(() => { + updateColumnValue(); + nextTick(updateInnerValue); }); - return ( + watch(columns, updateColumnValue); + + watch(currentDate, (value) => { + emit('update:modelValue', value); + }); + + watch( + [() => props.filter, () => props.minDate, () => props.maxDate], + updateInnerValue + ); + + watch( + () => props.modelValue, + (value) => { + value = formatValue(value); + + if (value.valueOf() !== currentDate.value.valueOf()) { + currentDate.value = value; + } + } + ); + + useExpose({ + getPicker: () => picker.value, + }); + + return () => ( ); }, diff --git a/src/datetime-picker/TimePicker.js b/src/datetime-picker/TimePicker.js index 1a0974c20..3e24fc778 100644 --- a/src/datetime-picker/TimePicker.js +++ b/src/datetime-picker/TimePicker.js @@ -5,6 +5,7 @@ import { padZero } from '../utils/format/string'; import { times, sharedProps } from './utils'; import Picker from '../picker'; import { pickerProps } from '../picker/shared'; +import { useExpose } from '../composition/use-expose'; const [createComponent] = createNamespace('time-picker'); @@ -160,6 +161,10 @@ export default createComponent({ } ); + useExpose({ + getPicker: () => picker.value, + }); + return () => ( root.value && root.value.getPicker(), + getPicker: () => root.value?.getPicker(), }); return () => { const Component = props.type === 'time' ? TimePicker : DatePicker; - return ; + return ; }; }, });