UNPKG

vxe-pc-ui

Version:
1,336 lines (1,245 loc) 67.7 kB
import { h, ref, Ref, computed, reactive, nextTick, watch, PropType, VNode } from 'vue' import { defineVxeComponent } from '../../ui/src/comp' import XEUtils from 'xe-utils' import { getConfig, getI18n, createEvent, useSize, renderEmptyElement } from '../../ui' import { toStringTimeDate, getDateQuarter, parseDateValue, parseDateObj, handleValueFormat, hasDateValueType, hasTimestampValueType } from './util' import type { VxeDatePanelConstructor, DatePanelInternalData, DatePanelReactData, VxeDatePanelPropTypes, DatePanelMethods, ValueOf, VxeDatePanelEmits, VxeDatePanelDefines } from '../../../types' export default defineVxeComponent({ name: 'VxeDatePanel', props: { modelValue: [String, Number, Date] as PropType<VxeDatePanelPropTypes.ModelValue>, type: { type: String as PropType<VxeDatePanelPropTypes.Type>, default: 'date' }, className: String as PropType<VxeDatePanelPropTypes.ClassName>, size: { type: String as PropType<VxeDatePanelPropTypes.Size>, default: () => getConfig().datePanel.size || getConfig().size }, multiple: Boolean as PropType<VxeDatePanelPropTypes.Multiple>, limitCount: { type: [String, Number] as PropType<VxeDatePanelPropTypes.LimitCount>, default: () => getConfig().datePanel.limitCount }, // date、week、month、quarter、year startDate: { type: [String, Number, Date] as PropType<VxeDatePanelPropTypes.StartDate>, default: () => getConfig().datePanel.startDate }, endDate: { type: [String, Number, Date] as PropType<VxeDatePanelPropTypes.EndDate>, default: () => getConfig().datePanel.endDate }, defaultDate: [String, Number, Date] as PropType<VxeDatePanelPropTypes.DefaultDate>, defaultTime: [String, Number, Date] as PropType<VxeDatePanelPropTypes.DefaultTime>, minDate: [String, Number, Date] as PropType<VxeDatePanelPropTypes.MinDate>, maxDate: [String, Number, Date] as PropType<VxeDatePanelPropTypes.MaxDate>, startDay: { type: [String, Number] as PropType<VxeDatePanelPropTypes.StartDay>, default: () => getConfig().datePanel.startDay }, labelFormat: String as PropType<VxeDatePanelPropTypes.LabelFormat>, valueFormat: String as PropType<VxeDatePanelPropTypes.ValueFormat>, timeFormat: String as PropType<VxeDatePanelPropTypes.TimeFormat>, festivalMethod: { type: Function as PropType<VxeDatePanelPropTypes.FestivalMethod>, default: () => getConfig().datePanel.festivalMethod }, disabledMethod: { type: Function as PropType<VxeDatePanelPropTypes.DisabledMethod>, default: () => getConfig().datePanel.disabledMethod }, timeConfig: Object as PropType<VxeDatePanelPropTypes.TimeConfig>, // week selectDay: { type: [String, Number] as PropType<VxeDatePanelPropTypes.SelectDay>, default: () => getConfig().datePanel.selectDay } }, emits: [ 'update:modelValue', 'change', 'click', 'clear', 'date-prev', 'date-today', 'date-next', 'confirm' ] as VxeDatePanelEmits, setup (props, context) { const { emit } = context const xID = XEUtils.uniqueId() const { computeSize } = useSize(props) const reactData = reactive<DatePanelReactData>({ visiblePanel: false, isAniVisible: false, isActivated: false, inputValue: '', inputLabel: '', datetimePanelValue: null, datePanelValue: null, datePanelLabel: '', datePanelType: 'day', selectMonth: null, currentDate: null }) const internalData: DatePanelInternalData = { yearSize: 12, monthSize: 20, quarterSize: 8, hpTimeout: undefined as any } const refElem = ref() as Ref<HTMLDivElement> const refPanelWrapper = ref() as Ref<HTMLDivElement> const refInputTimeBody = ref() as Ref<HTMLDivElement> const refMaps = { refElem } const $xeDatePanel = { xID, props, context, reactData, internalData, getRefMaps: () => refMaps } as unknown as VxeDatePanelConstructor const computeIsDateTimeType = computed(() => { const { type } = props return type === 'time' || type === 'datetime' }) const computeIsDatePanelType = computed(() => { const isDateTimeType = computeIsDateTimeType.value return isDateTimeType || ['date', 'week', 'month', 'quarter', 'year'].indexOf(props.type) > -1 }) const computeDateStartTime = computed(() => { return props.startDate ? XEUtils.toStringDate(props.startDate) : null }) const computeDateEndTime = computed(() => { return props.endDate ? XEUtils.toStringDate(props.endDate) : null }) const computeDateListValue = computed(() => { const { modelValue, multiple } = props const isDatePanelType = computeIsDatePanelType.value const dateValueFormat = computeDateValueFormat.value if (multiple && modelValue && isDatePanelType) { return XEUtils.toValueString(modelValue).split(',').map(item => { const date = parseDate(item, dateValueFormat) if (XEUtils.isValidDate(date)) { return date } return date }) } return [] }) const computeDateMultipleValue = computed(() => { const dateListValue = computeDateListValue.value const dateValueFormat = computeDateValueFormat.value return dateListValue.map(date => XEUtils.toDateString(date, dateValueFormat)) }) const computeDateMultipleLabel = computed(() => { const dateListValue = computeDateListValue.value const dateLabelFormat = computeDateLabelFormat.value return dateListValue.map(date => XEUtils.toDateString(date, dateLabelFormat)).join(', ') }) const computeLimitMaxCount = computed(() => { return props.multiple ? XEUtils.toNumber(props.limitCount) : 0 }) const computeOverCount = computed(() => { const { multiple } = props const limitMaxCount = computeLimitMaxCount.value const dateMultipleValue = computeDateMultipleValue.value if (multiple && limitMaxCount) { return dateMultipleValue.length >= limitMaxCount } return false }) const computeTimeOpts = computed(() => { return Object.assign({}, getConfig().datePanel.timeConfig, props.timeConfig) }) const computeDateValueFormat = computed(() => { const { type, valueFormat } = props return handleValueFormat(type, valueFormat) }) const computeDateValue = computed(() => { const { modelValue } = props const isDatePanelType = computeIsDatePanelType.value const dateValueFormat = computeDateValueFormat.value let val = null if (modelValue && isDatePanelType) { const date = parseDate(modelValue, dateValueFormat) if (XEUtils.isValidDate(date)) { val = date } } return val }) const computeIsDisabledPrevDateBtn = computed(() => { const dateStartTime = computeDateStartTime.value const { selectMonth } = reactData if (selectMonth && dateStartTime) { return selectMonth <= dateStartTime } return false }) const computeIsDisabledNextDateBtn = computed(() => { const dateEndTime = computeDateEndTime.value const { selectMonth } = reactData if (selectMonth && dateEndTime) { return XEUtils.getWhatMonth(selectMonth, 0, 'last') >= dateEndTime } return false }) const computeDateTimeLabel = computed(() => { const { datetimePanelValue } = reactData const hasTimeSecond = computeHasTimeSecond.value const hasTimeMinute = computeHasTimeMinute.value if (datetimePanelValue) { return XEUtils.toDateString(datetimePanelValue, hasTimeMinute && hasTimeSecond ? 'HH:mm:ss' : (hasTimeMinute ? 'HH:mm' : 'HH')) } return '' }) const computeDateHMSTime = computed(() => { const dateValue = computeDateValue.value const isDateTimeType = computeIsDateTimeType.value return dateValue && isDateTimeType ? (dateValue.getHours() * 3600 + dateValue.getMinutes() * 60 + dateValue.getSeconds()) * 1000 : 0 }) const computeDateLabelFormat = computed(() => { const { labelFormat } = props const isDatePanelType = computeIsDatePanelType.value if (isDatePanelType) { return labelFormat || getI18n(`vxe.input.date.labelFormat.${props.type}`) } return '' }) const computeYearList = computed(() => { const { yearSize } = internalData const { selectMonth, currentDate } = reactData const years: VxeDatePanelDefines.DateYearItem[] = [] if (selectMonth && currentDate) { const currFullYear = currentDate.getFullYear() const selectFullYear = selectMonth.getFullYear() const startYearDate = new Date(selectFullYear - selectFullYear % yearSize, 0, 1) for (let index = -4; index < yearSize + 4; index++) { const date = XEUtils.getWhatYear(startYearDate, index, 'first') const itemFullYear = date.getFullYear() years.push({ date, isCurrent: true, isPrev: index < 0, isNow: currFullYear === itemFullYear, isNext: index >= yearSize, year: itemFullYear }) } } return years }) const computeSelectDatePanelObj = computed(() => { const isDatePanelType = computeIsDatePanelType.value let y = '' let m = '' if (isDatePanelType) { const { datePanelType, selectMonth } = reactData const yearList = computeYearList.value let year = '' let month if (selectMonth) { year = selectMonth.getFullYear() month = selectMonth.getMonth() + 1 } if (datePanelType === 'quarter' || datePanelType === 'month') { y = getI18n('vxe.datePicker.yearTitle', [year]) } else if (datePanelType === 'year') { y = yearList.length ? `${yearList[0].year} - ${yearList[yearList.length - 1].year}` : '' } else { y = getI18n('vxe.datePicker.yearTitle', [year]) m = month ? getI18n(`vxe.input.date.m${month}`) : '-' } } return { y, m } }) const computeFirstDayOfWeek = computed(() => { const { startDay } = props return XEUtils.toNumber(startDay) as VxeDatePanelPropTypes.StartDay }) const computeWeekDatas = computed(() => { const weeks: number[] = [] const isDatePanelType = computeIsDatePanelType.value if (isDatePanelType) { let sWeek = computeFirstDayOfWeek.value weeks.push(sWeek) for (let index = 0; index < 6; index++) { if (sWeek >= 6) { sWeek = 0 } else { sWeek++ } weeks.push(sWeek) } } return weeks }) const computeDateHeaders = computed(() => { const isDatePanelType = computeIsDatePanelType.value if (isDatePanelType) { const weekDatas = computeWeekDatas.value return weekDatas.map((day) => { return { value: day, label: getI18n(`vxe.input.date.weeks.w${day}`) } }) } return [] }) const computeWeekHeaders = computed(() => { const isDatePanelType = computeIsDatePanelType.value if (isDatePanelType) { const dateHeaders = computeDateHeaders.value return [{ label: getI18n('vxe.input.date.weeks.w') }].concat(dateHeaders) } return [] }) const computeYearDatas = computed(() => { const yearList = computeYearList.value return XEUtils.chunk(yearList, 4) }) const computeQuarterList = computed(() => { const { quarterSize } = internalData const { selectMonth, currentDate } = reactData const quarters: VxeDatePanelDefines.DateQuarterItem[] = [] if (selectMonth && currentDate) { const currFullYear = currentDate.getFullYear() const currQuarter = getDateQuarter(currentDate) const firstYear = XEUtils.getWhatYear(selectMonth, 0, 'first') const selFullYear = firstYear.getFullYear() for (let index = -2; index < quarterSize - 2; index++) { const date = XEUtils.getWhatQuarter(firstYear, index) const itemFullYear = date.getFullYear() const itemQuarter = getDateQuarter(date) const isPrev = itemFullYear < selFullYear quarters.push({ date, isPrev, isCurrent: itemFullYear === selFullYear, isNow: itemFullYear === currFullYear && itemQuarter === currQuarter, isNext: !isPrev && itemFullYear > selFullYear, quarter: itemQuarter }) } } return quarters }) const computeQuarterDatas = computed(() => { const quarterList = computeQuarterList.value return XEUtils.chunk(quarterList, 2) }) const computeMonthList = computed(() => { const { monthSize } = internalData const { selectMonth, currentDate } = reactData const months: VxeDatePanelDefines.DateMonthItem[] = [] if (selectMonth && currentDate) { const currFullYear = currentDate.getFullYear() const currMonth = currentDate.getMonth() const selFullYear = XEUtils.getWhatYear(selectMonth, 0, 'first').getFullYear() for (let index = -4; index < monthSize - 4; index++) { const date = XEUtils.getWhatYear(selectMonth, 0, index) const itemFullYear = date.getFullYear() const itemMonth = date.getMonth() const isPrev = itemFullYear < selFullYear months.push({ date, isPrev, isCurrent: itemFullYear === selFullYear, isNow: itemFullYear === currFullYear && itemMonth === currMonth, isNext: !isPrev && itemFullYear > selFullYear, month: itemMonth }) } } return months }) const computeMonthDatas = computed(() => { const monthList = computeMonthList.value return XEUtils.chunk(monthList, 4) }) const computeDayList = computed(() => { const { selectMonth, currentDate } = reactData const days: VxeDatePanelDefines.DateDayItem[] = [] if (selectMonth && currentDate) { const dateHMSTime = computeDateHMSTime.value const weekDatas = computeWeekDatas.value const currFullYear = currentDate.getFullYear() const currMonth = currentDate.getMonth() const currDate = currentDate.getDate() const selFullYear = selectMonth.getFullYear() const selMonth = selectMonth.getMonth() const selDay = selectMonth.getDay() const prevOffsetDate = -weekDatas.indexOf(selDay) const startDayDate = new Date(XEUtils.getWhatDay(selectMonth, prevOffsetDate).getTime() + dateHMSTime) for (let index = 0; index < 42; index++) { const date = XEUtils.getWhatDay(startDayDate, index) const itemFullYear = date.getFullYear() const itemMonth = date.getMonth() const itemDate = date.getDate() const isPrev = date < selectMonth days.push({ date, isPrev, isCurrent: itemFullYear === selFullYear && itemMonth === selMonth, isNow: itemFullYear === currFullYear && itemMonth === currMonth && itemDate === currDate, isNext: !isPrev && selMonth !== itemMonth, label: itemDate }) } } return days }) const computeDayDatas = computed(() => { const dayList = computeDayList.value return XEUtils.chunk(dayList, 7) }) const computeWeekDates = computed(() => { const dayDatas = computeDayDatas.value const firstDayOfWeek = computeFirstDayOfWeek.value return dayDatas.map((list) => { const firstItem = list[0] const item: VxeDatePanelDefines.DateDayItem = { date: firstItem.date, isWeekNumber: true, isPrev: false, isCurrent: false, isNow: false, isNext: false, label: XEUtils.getYearWeek(firstItem.date, firstDayOfWeek) } return [item].concat(list) }) }) const computeHourList = computed(() => { const timeOpts = computeTimeOpts.value const { hours: hourOptions, hourDisabledMethod } = timeOpts const list: VxeDatePanelDefines.DateHourMinuteSecondItem[] = [] const isDateTimeType = computeIsDateTimeType.value if (isDateTimeType) { if (hourOptions && hourOptions.length) { hourOptions.forEach(item => { if (XEUtils.isNumber(item) || XEUtils.isString(item)) { const hour = XEUtils.toNumber(item) list.push({ value: hour, label: ('' + hour).padStart(2, '0'), disabled: !!(hourDisabledMethod && hourDisabledMethod({ hour })) }) } else if (item) { const hour = XEUtils.toNumber(item.value) list.push({ value: hour, label: ('' + (item.label || hour)).padStart(2, '0'), disabled: XEUtils.isBoolean(item.disabled) ? item.disabled : !!(hourDisabledMethod && hourDisabledMethod({ hour })) }) } }) } else { for (let index = 0; index < 24; index++) { const hour = index list.push({ value: hour, label: ('' + hour).padStart(2, '0'), disabled: !!(hourDisabledMethod && hourDisabledMethod({ hour })) }) } } } return list }) const computeMinuteList = computed(() => { const timeOpts = computeTimeOpts.value const { minutes: minuteOptions, minuteDisabledMethod } = timeOpts const list: VxeDatePanelDefines.DateHourMinuteSecondItem[] = [] const isDateTimeType = computeIsDateTimeType.value if (isDateTimeType) { if (minuteOptions && minuteOptions.length) { minuteOptions.forEach(item => { if (XEUtils.isNumber(item) || XEUtils.isString(item)) { const minute = XEUtils.toNumber(item) list.push({ value: minute, label: ('' + minute).padStart(2, '0'), disabled: !!(minuteDisabledMethod && minuteDisabledMethod({ minute })) }) } else if (item) { const minute = XEUtils.toNumber(item.value) list.push({ value: minute, label: ('' + (item.label || minute)).padStart(2, '0'), disabled: XEUtils.isBoolean(item.disabled) ? item.disabled : !!(minuteDisabledMethod && minuteDisabledMethod({ minute })) }) } }) } else { for (let index = 0; index < 60; index++) { const minute = index list.push({ value: minute, label: ('' + minute).padStart(2, '0'), disabled: !!(minuteDisabledMethod && minuteDisabledMethod({ minute })) }) } } } return list }) const computeHasTimeMinute = computed(() => { const { timeFormat } = props const dateValueFormat = computeDateValueFormat.value return !/HH/.test(timeFormat || dateValueFormat) || /mm/.test(timeFormat || dateValueFormat) }) const computeHasTimeSecond = computed(() => { const { timeFormat } = props const dateValueFormat = computeDateValueFormat.value return !/HH/.test(timeFormat || dateValueFormat) || /ss/.test(timeFormat || dateValueFormat) }) const computeSecondList = computed(() => { const timeOpts = computeTimeOpts.value const { seconds: secondOptions, secondDisabledMethod } = timeOpts const list: VxeDatePanelDefines.DateHourMinuteSecondItem[] = [] const isDateTimeType = computeIsDateTimeType.value if (isDateTimeType) { if (secondOptions && secondOptions.length) { secondOptions.forEach(item => { if (XEUtils.isNumber(item) || XEUtils.isString(item)) { const second = XEUtils.toNumber(item) list.push({ value: second, label: ('' + second).padStart(2, '0'), disabled: !!(secondDisabledMethod && secondDisabledMethod({ second })) }) } else if (item) { const second = XEUtils.toNumber(item.value) list.push({ value: second, label: ('' + (item.label || second)).padStart(2, '0'), disabled: XEUtils.isBoolean(item.disabled) ? item.disabled : !!(secondDisabledMethod && secondDisabledMethod({ second })) }) } }) } else { for (let index = 0; index < 60; index++) { const second = index list.push({ value: second, label: ('' + second).padStart(2, '0'), disabled: !!(secondDisabledMethod && secondDisabledMethod({ second })) }) } } } return list }) const updateModelValue = (modelValue: VxeDatePanelPropTypes.ModelValue | undefined) => { const { type } = props const dateValueFormat = computeDateValueFormat.value const inpDate = parseDateValue(modelValue, type, { valueFormat: dateValueFormat }) reactData.inputValue = inpDate reactData.inputLabel = inpDate dateOpenPanel() } const parseDate = (value: VxeDatePanelPropTypes.ModelValue, format: string) => { const { type, multiple } = props if (type === 'time') { return toStringTimeDate(value) } if (XEUtils.isArray(value)) { return XEUtils.toStringDate(value[0], format) } if (XEUtils.isString(value)) { return XEUtils.toStringDate(multiple ? XEUtils.last(value.split(',')) : value, format) } return XEUtils.toStringDate(value, format) } const dateRevert = () => { reactData.inputLabel = props.multiple ? computeDateMultipleLabel.value : reactData.datePanelLabel } const afterCheckValue = (inpVal: string) => { const { type } = props const { inputLabel, datetimePanelValue } = reactData const dateLabelFormat = computeDateLabelFormat.value if (inpVal) { let inpDateVal: VxeDatePanelPropTypes.ModelValue = parseDate(inpVal, dateLabelFormat as string) if (XEUtils.isValidDate(inpDateVal)) { if (type === 'time') { inpDateVal = XEUtils.toDateString(inpDateVal, dateLabelFormat) if (inputLabel !== inpDateVal) { handleChange(inpDateVal, { type: 'check' }) } reactData.inputLabel = inpDateVal } else { let isChange = false const firstDayOfWeek = computeFirstDayOfWeek.value if (type === 'datetime') { const dateValue = computeDateValue.value if (inpVal !== XEUtils.toDateString(dateValue, dateLabelFormat) || inpVal !== XEUtils.toDateString(inpDateVal, dateLabelFormat)) { isChange = true if (datetimePanelValue) { datetimePanelValue.setHours(inpDateVal.getHours()) datetimePanelValue.setMinutes(inpDateVal.getMinutes()) datetimePanelValue.setSeconds(inpDateVal.getSeconds()) } } } else { isChange = true } reactData.inputLabel = XEUtils.toDateString(inpDateVal, dateLabelFormat, { firstDay: firstDayOfWeek }) if (isChange) { dateChange(inpDateVal) } } } else { dateRevert() } } else { handleChange('', { type: 'check' }) } } const emitModel = (value: any) => { reactData.inputValue = value emit('update:modelValue', value) } const handleChange = (value: string, evnt: Event | { type: string }) => { const { type, modelValue, valueFormat } = props const dateValueFormat = computeDateValueFormat.value reactData.inputLabel = value if (hasTimestampValueType(valueFormat)) { const dateVal = parseDateValue(value, type, { valueFormat: dateValueFormat }) const timeNum = dateVal ? dateVal.getTime() : null emitModel(timeNum) if (modelValue !== timeNum) { dispatchEvent('change', { value: timeNum }, evnt as Event) } } else if (hasDateValueType(valueFormat)) { const dateVal = parseDateValue(value, type, { valueFormat: dateValueFormat }) emitModel(dateVal) if (modelValue && dateVal ? XEUtils.toStringDate(modelValue).getTime() !== dateVal.getTime() : modelValue !== dateVal) { dispatchEvent('change', { value: dateVal }, evnt as Event) } } else { emitModel(value) if (XEUtils.toValueString(modelValue) !== value) { dispatchEvent('change', { value }, evnt as Event) } } } const hidePanel = () => { return new Promise<void>(resolve => { reactData.visiblePanel = false internalData.hpTimeout = setTimeout(() => { reactData.isAniVisible = false resolve() }, 350) }) } const dateParseValue = (val?: VxeDatePanelPropTypes.ModelValue) => { const { type } = props const dateLabelFormat = computeDateLabelFormat.value const dateValueFormat = computeDateValueFormat.value const firstDayOfWeek = computeFirstDayOfWeek.value const dateObj = parseDateObj(val, type, { valueFormat: dateValueFormat, labelFormat: dateLabelFormat, firstDay: firstDayOfWeek }) reactData.datePanelValue = dateObj.value reactData.datePanelLabel = dateObj.label } /** * 值变化时处理 */ const changeValue = () => { const isDatePanelType = computeIsDatePanelType.value const { inputLabel } = reactData if (isDatePanelType) { dateParseValue(inputLabel) reactData.inputLabel = props.multiple ? computeDateMultipleLabel.value : reactData.datePanelLabel } } /** * 检查初始值 */ const initValue = () => { const { modelValue } = props const isDatePanelType = computeIsDatePanelType.value updateModelValue(modelValue) if (isDatePanelType) { changeValue() } } const dateCheckMonth = (date: Date) => { const firstDayOfWeek = computeFirstDayOfWeek.value const weekNum = XEUtils.getYearWeek(date, firstDayOfWeek) const weekStartDate = XEUtils.getWhatWeek(date, 0, firstDayOfWeek, firstDayOfWeek) const month = XEUtils.getWhatMonth(weekNum === 1 ? XEUtils.getWhatDay(weekStartDate, 6) : date, 0, 'first') if (!XEUtils.isEqual(month, reactData.selectMonth)) { reactData.selectMonth = month } } const dateChange = (date: Date, isReload?: boolean) => { const { modelValue, multiple } = props const { datetimePanelValue } = reactData const isDateTimeType = computeIsDateTimeType.value const dateValueFormat = computeDateValueFormat.value const firstDayOfWeek = computeFirstDayOfWeek.value if (props.type === 'week') { const sWeek = XEUtils.toNumber(props.selectDay) as VxeDatePanelPropTypes.SelectDay date = XEUtils.getWhatWeek(date, 0, sWeek, firstDayOfWeek) } else if (isDateTimeType) { if (datetimePanelValue) { date.setHours(datetimePanelValue.getHours()) date.setMinutes(datetimePanelValue.getMinutes()) date.setSeconds(datetimePanelValue.getSeconds()) } } const inpVal = XEUtils.toDateString(date, dateValueFormat, { firstDay: firstDayOfWeek }) dateCheckMonth(date) if (multiple) { const overCount = computeOverCount.value // 如果为多选 if (isDateTimeType) { // 如果是datetime特殊类型 const dateListValue = isReload ? [] : [...computeDateListValue.value] const datetimeRest: Date[] = [] const eqIndex = XEUtils.findIndexOf(dateListValue, val => XEUtils.isDateSame(date, val, 'yyyyMMdd')) if (eqIndex === -1) { if (overCount) { // 如果超出最大多选数量 return } dateListValue.push(date) } else { dateListValue.splice(eqIndex, 1) } dateListValue.forEach(item => { if (item) { if (datetimePanelValue) { item.setHours(datetimePanelValue.getHours()) item.setMinutes(datetimePanelValue.getMinutes()) item.setSeconds(datetimePanelValue.getSeconds()) } datetimeRest.push(item) } }) handleChange(datetimeRest.map(date => XEUtils.toDateString(date, dateValueFormat)).join(','), { type: 'update' }) } else { const dateMultipleValue = isReload ? [] : computeDateMultipleValue.value // 如果是日期类型 if (dateMultipleValue.some(val => XEUtils.isEqual(val, inpVal))) { handleChange(dateMultipleValue.filter(val => !XEUtils.isEqual(val, inpVal)).join(','), { type: 'update' }) } else { if (overCount) { // 如果超出最大多选数量 return } handleChange(dateMultipleValue.concat([inpVal]).join(','), { type: 'update' }) } } } else { // 如果为单选 if (!XEUtils.isEqual(modelValue, inpVal)) { handleChange(inpVal, { type: 'update' }) } } } // 日期 const dateMonthHandle = (date: Date, offsetMonth: number) => { const firstDayOfWeek = computeFirstDayOfWeek.value const weekNum = XEUtils.getYearWeek(date, firstDayOfWeek) const weekStartDate = XEUtils.getWhatWeek(date, 0, firstDayOfWeek, firstDayOfWeek) const month = XEUtils.getWhatMonth(weekNum === 1 ? XEUtils.getWhatDay(weekStartDate, 6) : date, offsetMonth, 'first') reactData.selectMonth = month } const dateNowHandle = () => { const { type } = props const firstDayOfWeek = computeFirstDayOfWeek.value let currentDate = new Date() switch (type) { case 'week': currentDate = XEUtils.getWhatWeek(currentDate, 0, firstDayOfWeek) break case 'datetime': currentDate = new Date() reactData.datetimePanelValue = new Date() break default: currentDate = XEUtils.getWhatDay(Date.now(), 0, 'first') break } reactData.currentDate = currentDate dateMonthHandle(currentDate, 0) } const dateToggleYearTypeEvent = () => { reactData.datePanelType = 'year' } const dateToggleMonthTypeEvent = () => { let { datePanelType } = reactData if (datePanelType === 'month' || datePanelType === 'quarter') { datePanelType = 'year' } else { datePanelType = 'month' } reactData.datePanelType = datePanelType } const datePrevEvent = (evnt: Event) => { const { type } = props const { datePanelType, selectMonth, inputLabel } = reactData const { yearSize } = internalData const value = inputLabel const isDisabledPrevDateBtn = computeIsDisabledPrevDateBtn.value if (!isDisabledPrevDateBtn) { let viewDate if (type === 'year') { viewDate = XEUtils.getWhatYear(selectMonth, -yearSize, 'first') } else if (type === 'month' || type === 'quarter') { if (datePanelType === 'year') { viewDate = XEUtils.getWhatYear(selectMonth, -yearSize, 'first') } else { viewDate = XEUtils.getWhatYear(selectMonth, -1, 'first') } } else { if (datePanelType === 'year') { viewDate = XEUtils.getWhatYear(selectMonth, -yearSize, 'first') } else if (datePanelType === 'month') { viewDate = XEUtils.getWhatYear(selectMonth, -1, 'first') } else { viewDate = XEUtils.getWhatMonth(selectMonth, -1, 'first') } } reactData.selectMonth = viewDate dispatchEvent('date-prev', { viewType: datePanelType, viewDate, value, type }, evnt) } } const dateTodayMonthEvent = (evnt: Event) => { dateNowHandle() dateChange(reactData.currentDate, true) if (!props.multiple) { hidePanel() } dispatchEvent('date-today', { type: props.type }, evnt) } const dateNextEvent = (evnt: Event) => { const { type } = props const { datePanelType, selectMonth, inputLabel } = reactData const { yearSize } = internalData const value = inputLabel const isDisabledNextDateBtn = computeIsDisabledNextDateBtn.value if (!isDisabledNextDateBtn) { let viewDate if (type === 'year') { viewDate = XEUtils.getWhatYear(selectMonth, yearSize, 'first') } else if (type === 'month' || type === 'quarter') { if (datePanelType === 'year') { viewDate = XEUtils.getWhatYear(selectMonth, yearSize, 'first') } else { viewDate = XEUtils.getWhatYear(selectMonth, 1, 'first') } } else { if (datePanelType === 'year') { viewDate = XEUtils.getWhatYear(selectMonth, yearSize, 'first') } else if (datePanelType === 'month') { viewDate = XEUtils.getWhatYear(selectMonth, 1, 'first') } else { viewDate = XEUtils.getWhatMonth(selectMonth, 1, 'first') } } reactData.selectMonth = viewDate dispatchEvent('date-next', { viewType: datePanelType, value, type }, evnt) } } const isRangeDisabled = (item: { date: Date }) => { const dateStartTime = computeDateStartTime.value const dateEndTime = computeDateEndTime.value const { date } = item if (dateStartTime && dateStartTime.getTime() > date.getTime()) { return true } if (dateEndTime && dateEndTime.getTime() < date.getTime()) { return true } return false } const isDateDisabled = (item: { date: Date }) => { const { disabledMethod } = props const { datePanelType } = reactData const { date } = item if (disabledMethod) { return disabledMethod({ type: datePanelType, viewType: datePanelType, date, $datePanel: $xeDatePanel }) } return false } const hasAllDisabled = (item: { date: Date }) => { return isRangeDisabled(item) || isDateDisabled(item) } const dateSelectItem = (date: Date) => { const { type, multiple } = props const { datePanelType } = reactData if (type === 'month') { if (datePanelType === 'year') { reactData.datePanelType = 'month' dateCheckMonth(date) } else { dateChange(date) if (!multiple) { hidePanel() } } } else if (type === 'year') { dateChange(date) if (!multiple) { hidePanel() } } else if (type === 'quarter') { if (datePanelType === 'year') { reactData.datePanelType = 'quarter' dateCheckMonth(date) } else { dateChange(date) if (!multiple) { hidePanel() } } } else { if (datePanelType === 'month') { reactData.datePanelType = type === 'week' ? type : 'day' dateCheckMonth(date) } else if (datePanelType === 'year') { reactData.datePanelType = 'month' dateCheckMonth(date) } else { dateChange(date) if (type === 'datetime') { // 日期带时间 } else { if (!multiple) { hidePanel() } } } } } const dateSelectEvent = (item: VxeDatePanelDefines.DateYearItem | VxeDatePanelDefines.DateQuarterItem | VxeDatePanelDefines.DateMonthItem | VxeDatePanelDefines.DateDayItem) => { if (!hasAllDisabled(item)) { dateSelectItem(item.date) } } const dateMoveDay = (offsetDay: Date) => { if (!hasAllDisabled({ date: offsetDay })) { const dayList = computeDayList.value if (!dayList.some((item) => XEUtils.isDateSame(item.date, offsetDay, 'yyyyMMdd'))) { dateCheckMonth(offsetDay) } dateParseValue(offsetDay) } } const dateMoveYear = (offsetYear: Date) => { if (!hasAllDisabled({ date: offsetYear })) { const yearList = computeYearList.value if (!yearList.some((item) => XEUtils.isDateSame(item.date, offsetYear, 'yyyy'))) { dateCheckMonth(offsetYear) } dateParseValue(offsetYear) } } const dateMoveQuarter = (offsetQuarter: Date) => { if (!hasAllDisabled({ date: offsetQuarter })) { const quarterList = computeQuarterList.value if (!quarterList.some((item) => XEUtils.isDateSame(item.date, offsetQuarter, 'yyyyq'))) { dateCheckMonth(offsetQuarter) } dateParseValue(offsetQuarter) } } const dateMoveMonth = (offsetMonth: Date) => { if (!hasAllDisabled({ date: offsetMonth })) { const monthList = computeMonthList.value if (!monthList.some((item) => XEUtils.isDateSame(item.date, offsetMonth, 'yyyyMM'))) { dateCheckMonth(offsetMonth) } dateParseValue(offsetMonth) } } const dateMouseenterEvent = (item: VxeDatePanelDefines.DateYearItem | VxeDatePanelDefines.DateQuarterItem | VxeDatePanelDefines.DateMonthItem | VxeDatePanelDefines.DateDayItem) => { if (!hasAllDisabled(item)) { const { datePanelType } = reactData if (datePanelType === 'month') { dateMoveMonth(item.date) } else if (datePanelType === 'quarter') { dateMoveQuarter(item.date) } else if (datePanelType === 'year') { dateMoveYear(item.date) } else { dateMoveDay(item.date) } } } const dateMouseleaveEvent = () => { reactData.datePanelValue = null } const updateTimePos = (liElem: Element) => { if (liElem) { const height = (liElem as HTMLElement).offsetHeight const ulElem = liElem.parentNode as HTMLElement ulElem.scrollTop = (liElem as HTMLElement).offsetTop - height * 4 } } const dateTimeChangeEvent = (evnt: Event) => { const { datetimePanelValue } = reactData reactData.datetimePanelValue = datetimePanelValue ? new Date(datetimePanelValue.getTime()) : new Date() updateTimePos(evnt.currentTarget as HTMLLIElement) } const dateHourEvent = (evnt: MouseEvent, item: VxeDatePanelDefines.DateHourMinuteSecondItem) => { const { datetimePanelValue } = reactData if (!item.disabled) { if (datetimePanelValue) { datetimePanelValue.setHours(item.value) } dateTimeChangeEvent(evnt) } } const dateConfirmEvent = (evnt: Event) => { const { multiple } = props const { datetimePanelValue } = reactData const dateValue = computeDateValue.value const isDateTimeType = computeIsDateTimeType.value if (isDateTimeType) { const dateValueFormat = computeDateValueFormat.value if (multiple) { // 如果为多选 const dateMultipleValue = computeDateMultipleValue.value if (isDateTimeType) { // 如果是datetime特殊类型 const dateListValue = [...computeDateListValue.value] const datetimeRest: Date[] = [] dateListValue.forEach(item => { if (item) { if (datetimePanelValue) { item.setHours(datetimePanelValue.getHours()) item.setMinutes(datetimePanelValue.getMinutes()) item.setSeconds(datetimePanelValue.getSeconds()) } datetimeRest.push(item) } }) handleChange(datetimeRest.map(date => XEUtils.toDateString(date, dateValueFormat)).join(','), { type: 'update' }) } else { // 如果是日期类型 handleChange(dateMultipleValue.join(','), { type: 'update' }) } } else { dateChange(dateValue || reactData.currentDate) } } hidePanel() dispatchEvent('confirm', {}, evnt) } const dateMinuteEvent = (evnt: MouseEvent, item: VxeDatePanelDefines.DateHourMinuteSecondItem) => { const { datetimePanelValue } = reactData if (!item.disabled) { if (datetimePanelValue) { datetimePanelValue.setMinutes(item.value) } dateTimeChangeEvent(evnt) } } const dateSecondEvent = (evnt: MouseEvent, item: VxeDatePanelDefines.DateHourMinuteSecondItem) => { const { datetimePanelValue } = reactData if (!item.disabled) { if (datetimePanelValue) { datetimePanelValue.setSeconds(item.value) } dateTimeChangeEvent(evnt) } } const dateOpenPanel = () => { const { type, defaultDate, defaultTime } = props const isDateTimeType = computeIsDateTimeType.value const dateValue = computeDateValue.value if (['year', 'quarter', 'month', 'week'].indexOf(type) > -1) { reactData.datePanelType = type as VxeDatePanelDefines.DatePanelType } else { reactData.datePanelType = 'day' } reactData.currentDate = XEUtils.getWhatDay(Date.now(), 0, 'first') if (dateValue) { dateMonthHandle(dateValue, 0) dateParseValue(dateValue) } else { if (defaultDate) { // 面板默认日期仅支持解析 yyyy-MM-dd const defDate = parseDate(defaultDate, 'yyyy-MM-dd') if (XEUtils.isValidDate(defDate)) { dateMonthHandle(defDate, 0) } else { dateNowHandle() } } else { dateNowHandle() } } if (isDateTimeType) { let dtPanelValue = reactData.datePanelValue if (!dtPanelValue) { dtPanelValue = XEUtils.getWhatDay(Date.now(), 0, 'first') if (defaultTime) { const defTime = toStringTimeDate(defaultTime) if (XEUtils.isValidDate(defTime)) { dtPanelValue.setHours(defTime.getHours()) dtPanelValue.setMinutes(defTime.getMinutes()) dtPanelValue.setSeconds(defTime.getSeconds()) } } } reactData.datetimePanelValue = dtPanelValue nextTick(() => { const timeBodyElem = refInputTimeBody.value XEUtils.arrayEach(timeBodyElem.querySelectorAll('li.is--selected'), (elem) => { updateTimePos(elem) }) }) } } const dispatchEvent = (type: ValueOf<VxeDatePanelEmits>, params: Record<string, any>, evnt: Event | null) => { emit(type, createEvent(evnt, { $datePanel: $xeDatePanel }, params)) } const datePanelMethods: DatePanelMethods = { dispatchEvent, getModelValue () { return reactData.inputValue }, setPanelDate (date) { if (date) { dateCheckMonth(date) } }, getPanelDate () { return reactData.selectMonth }, checkValue (value) { afterCheckValue(value) }, confirmByEvent (evnt) { dateConfirmEvent(evnt) } } Object.assign($xeDatePanel, datePanelMethods) const renderDateLabel = (item: VxeDatePanelDefines.DateYearItem | VxeDatePanelDefines.DateQuarterItem | VxeDatePanelDefines.DateMonthItem | VxeDatePanelDefines.DateDayItem, label: string | number) => { const { festivalMethod } = props const labelVNs: VNode[] = [] if (festivalMethod) { const { datePanelType } = reactData const festivalRest = festivalMethod({ type: datePanelType, viewType: datePanelType, date: item.date, $datePanel: $xeDatePanel }) const festivalItem = festivalRest ? (XEUtils.isString(festivalRest) ? { label: festivalRest } : festivalRest) : {} const extraItem = festivalItem.extra ? (XEUtils.isString(festivalItem.extra) ? { label: festivalItem.extra } : festivalItem.extra) : null labelVNs.push( h('div', { class: ['vxe-date-panel--label', { 'is-notice': festivalItem.notice }] }, extraItem && extraItem.label ? [ h('div', { class: 'vxe-date-panel--label--number' }, `${label}`), h('div', { class: ['vxe-date-panel--label--extra', extraItem.important ? 'is-important' : '', extraItem.className], style: extraItem.style }, XEUtils.toValueString(extraItem.label)) ] : `${label}`) ) const festivalLabel = festivalItem.label if (festivalLabel) { // 默认最多支持3个节日重叠 const festivalLabels = XEUtils.toValueString(festivalLabel).split(',') labelVNs.push( h('div', { class: ['vxe-date-panel--festival', festivalItem.important ? 'is-important' : '', festivalItem.className], style: festivalItem.style }, [ festivalLabels.length > 1 ? h('div', { class: ['vxe-date-panel--festival--overlap', `overlap--${festivalLabels.length}`] }, festivalLabels.map(label => h('div', label.substring(0, 3)))) : h('div', { class: 'vxe-date-panel--festival--label' }, festivalLabels[0].substring(0, 3)) ]) ) } } return labelVNs } const renderDateDayTable = () => { const { multiple } = props const { datePanelType, datePanelValue } = reactData const dateValue = computeDateValue.value const dateHeaders = computeDateHeaders.value const dayDatas = computeDayDatas.value const dateListValue = computeDateListValue.value const overCount = computeOverCount.value const matchFormat = 'yyyyMMdd' return [ h('div', { class: ['vxe-date-panel--view-wrapper', `type--${datePanelType}`] }, [ h('div', { class: 'vxe-date-panel--view-header' }, [ h('div', { class: 'vxe-date-panel--view-row' }, dateHeaders.map((item) => { return h('div', { class: 'vxe-date-panel--view-item', style: { width: `${100 / dateHeaders.length}%` } }, [ h('div', { class: 'vxe-date-panel--view-item-inner' }, [ h('div', { class: 'vxe-date-panel--view-item-label' }, item.label) ]) ]) })) ]), h('div', { class: 'vxe-date-panel--view-body' }, dayDatas.map((rows) => { return h('div', { class: 'vxe-date-panel--view-row', style: { height: `${100 / dayDatas.length}%` } }, rows.map((item) => { const isSelected = multiple ? dateListValue.some(val => XEUtils.isDateSame(val, item.date, matchFormat)) : XEUtils.isDateSame(dateValue, item.date, matchFormat) return h('div', { class: ['vxe-date-panel--view-item', { 'is--prev': item.isPrev, 'is--current': item.isCurrent, 'is--now': item.isNow, 'is--next': item.isNext, 'is--range-disabled': isRangeDisabled(item), 'is--disabled': isDateDisabled(item), 'is--selected': isSelected, 'is--over': overCount && !isSelected, 'is--hover': !overCount && XEUtils.isDateSame(datePanelValue, item.date, matchFormat) }], style: { width: `${100 / rows.length}%` }, onClick: ()