refactor(DatePicker): refactor with composition api

This commit is contained in:
chenjiahan 2020-09-27 16:30:35 +08:00
parent 486ad58042
commit 6388423c96
3 changed files with 195 additions and 208 deletions

View File

@ -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 { isDate } from '../utils/validate/date';
import { padZero } from '../utils/format/string'; import { padZero } from '../utils/format/string';
import { times, sharedProps, getTrueValue, getMonthEndDay } from './utils'; import { times, sharedProps, getTrueValue, getMonthEndDay } from './utils';
import Picker from '../picker'; import Picker from '../picker';
import { pickerProps } from '../picker/shared'; import { pickerProps } from '../picker/shared';
import { useExpose } from '../composition/use-expose';
const currentYear = new Date().getFullYear(); const currentYear = new Date().getFullYear();
const [createComponent] = createNamespace('date-picker'); const [createComponent] = createNamespace('date-picker');
@ -29,161 +31,23 @@ export default createComponent({
emits: ['confirm', 'cancel', 'change', 'update:modelValue'], emits: ['confirm', 'cancel', 'change', 'update:modelValue'],
data() { setup(props, { emit }) {
return { const formatValue = (value) => {
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) {
if (!isDate(value)) { if (!isDate(value)) {
value = this.minDate; value = props.minDate;
} }
value = Math.max(value, this.minDate.getTime()); value = Math.max(value, props.minDate.getTime());
value = Math.min(value, this.maxDate.getTime()); value = Math.min(value, props.maxDate.getTime());
return new Date(value); return new Date(value);
}, };
getBoundary(type, value) { const picker = ref();
const boundary = this[`${type}Date`]; const currentDate = ref(formatValue(props.modelValue));
const getBoundary = (type, value) => {
const boundary = props[`${type}Date`];
const year = boundary.getFullYear(); const year = boundary.getFullYear();
let month = 1; let month = 1;
let date = 1; let date = 1;
@ -217,19 +81,134 @@ export default createComponent({
[`${type}Hour`]: hour, [`${type}Hour`]: hour,
[`${type}Minute`]: minute, [`${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) => { const getValue = (type) => {
let index = 0; let index = 0;
this.originColumns.forEach((column, columnIndex) => { originColumns.value.forEach((column, columnIndex) => {
if (type === column.type) { if (type === column.type) {
index = columnIndex; index = columnIndex;
} }
}); });
const { values } = this.originColumns[index]; const { values } = originColumns.value[index];
return getTrueValue(values[indexes[index]]); return getTrueValue(values[indexes[index]]);
}; };
@ -237,7 +216,7 @@ export default createComponent({
let month; let month;
let day; let day;
if (type === 'month-day') { if (type === 'month-day') {
year = this.currentDate.getFullYear(); year = currentDate.value.getFullYear();
month = getValue('month'); month = getValue('month');
day = getValue('day'); day = getValue('day');
} else { } else {
@ -262,63 +241,66 @@ export default createComponent({
} }
const value = new Date(year, month - 1, day, hour, minute); 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) { const onCancel = () => {
this.updateInnerValue(); emit('cancel');
};
this.$nextTick(() => { const onChange = () => {
this.$nextTick(() => { updateInnerValue();
this.$emit('change', picker); nextTick(() => {
nextTick(() => {
emit('change', picker.value);
}); });
}); });
}, };
updateColumnValue() { onMounted(() => {
const value = this.currentDate; updateColumnValue();
const { formatter } = this; nextTick(updateInnerValue);
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];
}); });
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 () => (
<Picker <Picker
ref="picker" ref={picker}
columns={this.columns} columns={columns.value}
readonly={this.readonly} readonly={props.readonly}
onChange={this.onChange} onChange={onChange}
onConfirm={this.onConfirm} onCancel={onCancel}
onCancel={this.onCancel} onConfirm={onConfirm}
{...props} {...pick(props, Object.keys(pickerProps))}
/> />
); );
}, },

View File

@ -5,6 +5,7 @@ import { padZero } from '../utils/format/string';
import { times, sharedProps } from './utils'; import { times, sharedProps } from './utils';
import Picker from '../picker'; import Picker from '../picker';
import { pickerProps } from '../picker/shared'; import { pickerProps } from '../picker/shared';
import { useExpose } from '../composition/use-expose';
const [createComponent] = createNamespace('time-picker'); const [createComponent] = createNamespace('time-picker');
@ -160,6 +161,10 @@ export default createComponent({
} }
); );
useExpose({
getPicker: () => picker.value,
});
return () => ( return () => (
<Picker <Picker
ref={picker} ref={picker}

View File

@ -12,16 +12,16 @@ export default createComponent({
...DatePicker.props, ...DatePicker.props,
}, },
setup(props) { setup(props, { attrs }) {
const root = ref(); const root = ref();
useExpose({ useExpose({
getPicker: () => root.value && root.value.getPicker(), getPicker: () => root.value?.getPicker(),
}); });
return () => { return () => {
const Component = props.type === 'time' ? TimePicker : DatePicker; const Component = props.type === 'time' ? TimePicker : DatePicker;
return <Component ref={root} class={bem()} {...props} />; return <Component ref={root} class={bem()} {...{ ...props, ...attrs }} />;
}; };
}, },
}); });