vxe-pc-ui
Version:
A vue based PC component library
1,331 lines (1,244 loc) • 50.6 kB
text/typescript
import { h, ref, Ref, computed, reactive, watch, PropType, onMounted } from 'vue'
import { defineVxeComponent } from '../../ui/src/comp'
import { VxeUI, createEvent, useSize, renderEmptyElement } from '../../ui'
import { getDateQuarter } from '../../date-panel/src/util'
import { toCssUnit } from '../../ui/src/dom'
import { isEnableConf } from '../../ui/src/utils'
import { errLog } from '../../ui/src/log'
import VxeButtonComponent from '../../button/src/button'
import XEUtils from 'xe-utils'
import type { VxeCalendarConstructor, VxeCalendarEmits, CalendarInternalData, CalendarReactData, CalendarMethods, VxeCalendarPropTypes, CalendarPrivateRef, VxeDatePanelDefines, ValueOf } from '../../../types'
const { menus, getConfig, getI18n } = VxeUI
export default defineVxeComponent({
name: 'VxeCalendar',
props: {
modelValue: [String, Number, Date] as PropType<VxeCalendarPropTypes.ModelValue>,
type: {
type: String as PropType<VxeCalendarPropTypes.Type>,
default: 'date'
},
className: String as PropType<VxeCalendarPropTypes.ClassName>,
size: {
type: String as PropType<VxeCalendarPropTypes.Size>,
default: () => getConfig().calendar.size || getConfig().size
},
multiple: Boolean as PropType<VxeCalendarPropTypes.Multiple>,
width: [String, Number] as PropType<VxeCalendarPropTypes.Width>,
height: [String, Number] as PropType<VxeCalendarPropTypes.Height>,
// date、week、month、quarter、year
minDate: {
type: [String, Number, Date] as PropType<VxeCalendarPropTypes.MinDate>,
default: () => getConfig().calendar.minDate
},
maxDate: {
type: [String, Number, Date] as PropType<VxeCalendarPropTypes.MaxDate>,
default: () => getConfig().calendar.maxDate
},
startDay: {
type: [String, Number] as PropType<VxeCalendarPropTypes.StartDay>,
default: () => getConfig().calendar.startDay
},
labelFormat: String as PropType<VxeCalendarPropTypes.LabelFormat>,
valueFormat: String as PropType<VxeCalendarPropTypes.ValueFormat>,
festivalMethod: {
type: Function as PropType<VxeCalendarPropTypes.FestivalMethod>,
default: () => getConfig().calendar.festivalMethod
},
disabledMethod: {
type: Function as PropType<VxeCalendarPropTypes.DisabledMethod>,
default: () => getConfig().calendar.disabledMethod
},
cellStyle: [Object, Function] as PropType<VxeCalendarPropTypes.CellStyle>,
menuConfig: Object as PropType<VxeCalendarPropTypes.MenuConfig>,
// week
selectDay: {
type: [String, Number] as PropType<VxeCalendarPropTypes.SelectDay>,
default: () => getConfig().calendar.selectDay
}
},
emits: [
'update:modelValue',
'change',
'cell-click',
'date-prev',
'date-today',
'date-next',
'view-change',
'cell-menu',
'menu-click'
] as VxeCalendarEmits,
setup (props, context) {
const { emit } = context
const xID = XEUtils.uniqueId()
const { computeSize } = useSize(props)
const reactData = reactive<CalendarReactData>({
selectValue: props.modelValue,
inputValue: props.modelValue,
datePanelValue: null,
datePanelLabel: '',
datePanelType: 'day',
selectMonth: null,
currentDate: null
})
const internalData: CalendarInternalData = {
yearSize: 12,
monthSize: 20,
quarterSize: 8
}
const refElem = ref() as Ref<HTMLDivElement>
const refMaps: CalendarPrivateRef = {
refElem
}
const $xeCalendar = {
xID,
props,
context,
reactData,
internalData,
getRefMaps: () => refMaps
} as unknown as VxeCalendarConstructor
const computeCalendarStyle = computed(() => {
const { height, width } = props
const stys: Record<string, string> = {}
if (width) {
stys.width = toCssUnit(width)
}
if (height) {
stys.height = toCssUnit(height)
}
return stys
})
const computeIsDisabled = computed(() => {
return false
})
const computeIsCalendarType = computed(() => {
return ['date', 'week', 'month', 'quarter', 'year'].indexOf(props.type) > -1
})
const computeDateStartTime = computed(() => {
return props.minDate ? XEUtils.toStringDate(props.minDate) : null
})
const computeDateEndTime = computed(() => {
return props.maxDate ? XEUtils.toStringDate(props.maxDate) : null
})
const computeSupportMultiples = computed(() => {
return ['date', 'week', 'month', 'quarter', 'year'].indexOf(props.type) > -1
})
const computeMenuOpts = computed(() => {
return Object.assign({}, getConfig().calendar.menuConfig, props.menuConfig)
})
const computeDateListValue = computed(() => {
const { multiple } = props
const { selectValue } = reactData
const isCalendarType = computeIsCalendarType.value
const dateValueFormat = computeDateValueFormat.value
if (multiple && selectValue && isCalendarType) {
return XEUtils.toValueString(selectValue).split(',').map(item => {
const date = parseDate(item, dateValueFormat)
if (XEUtils.isValidDate(date)) {
return date
}
return null
})
}
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 computeDateValueFormat = computed(() => {
const { valueFormat } = props
if (valueFormat) {
return valueFormat
}
return 'yyyy-MM-dd'
})
const computeDateValue = computed(() => {
const { selectValue } = reactData
const isCalendarType = computeIsCalendarType.value
const dateValueFormat = computeDateValueFormat.value
let val = null
if (selectValue && isCalendarType) {
const date = parseDate(selectValue, 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 selectMonth >= dateEndTime
}
return false
})
const computeDateHMSTime = computed(() => {
const dateValue = computeDateValue.value
return dateValue ? (dateValue.getHours() * 3600 + dateValue.getMinutes() * 60 + dateValue.getSeconds()) * 1000 : 0
})
const computeDateLabelFormat = computed(() => {
const { labelFormat } = props
const isCalendarType = computeIsCalendarType.value
const dateValueFormat = computeDateValueFormat.value
if (isCalendarType) {
return labelFormat || dateValueFormat || getI18n(`vxe.input.date.labelFormat.${props.type}`)
}
return null
})
const computeYearList = computed(() => {
const { selectMonth, currentDate } = reactData
const { yearSize } = internalData
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 isCalendarType = computeIsCalendarType.value
let y = ''
let m = ''
if (isCalendarType) {
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 = `${year}`
} else if (datePanelType === 'year') {
y = yearList.length ? `${yearList[0].year} - ${yearList[yearList.length - 1].year}` : ''
} else {
y = `${year}`
m = month ? getI18n('vxe.calendar.monthLabel', [month]) : '-'
}
}
return {
y: getI18n('vxe.calendar.yearLabel', [y]),
m
}
})
const computeFirstDayOfWeek = computed(() => {
const { startDay } = props
return XEUtils.toNumber(startDay) as VxeCalendarPropTypes.StartDay
})
const computeWeekDatas = computed(() => {
const weeks = []
const isCalendarType = computeIsCalendarType.value
if (isCalendarType) {
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 isCalendarType = computeIsCalendarType.value
if (isCalendarType) {
const weekDatas = computeWeekDatas.value
return weekDatas.map((day) => {
return {
value: day,
label: getI18n(`vxe.input.date.weeks.w${day}`)
}
})
}
return []
})
const computeWeekHeaders = computed(() => {
const isCalendarType = computeIsCalendarType.value
if (isCalendarType) {
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 { selectMonth, currentDate } = reactData
const { quarterSize } = internalData
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 { selectMonth, currentDate } = reactData
const { monthSize } = internalData
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 parseDate = (value: VxeCalendarPropTypes.ModelValue, format: string) => {
return XEUtils.toStringDate(value, format)
}
const handleChange = (value: string, evnt: Event | { type: string }) => {
reactData.inputValue = value
emit('update:modelValue', value)
if (XEUtils.toValueString(props.modelValue) !== value) {
dispatchEvent('change', { value }, evnt as any)
}
}
const dateParseValue = (value?: VxeCalendarPropTypes.ModelValue) => {
const { type } = props
const dateLabelFormat = computeDateLabelFormat.value
const dateValueFormat = computeDateValueFormat.value
const firstDayOfWeek = computeFirstDayOfWeek.value
let dValue: Date | null = null
let dLabel = ''
if (value) {
dValue = parseDate(value, dateValueFormat)
}
if (XEUtils.isValidDate(dValue)) {
dLabel = XEUtils.toDateString(dValue, dateLabelFormat, { firstDay: firstDayOfWeek })
// 由于年份和第几周是冲突的行为,所以需要特殊处理,判断是否跨年
if (dateLabelFormat && type === 'week') {
const firstWeekDate = XEUtils.getWhatWeek(dValue, 0, firstDayOfWeek, firstDayOfWeek)
if (firstWeekDate.getFullYear() < dValue.getFullYear()) {
const yyIndex = dateLabelFormat.indexOf('yyyy')
if (yyIndex > -1) {
const yyNum = Number(dLabel.substring(yyIndex, yyIndex + 4))
if (yyNum && !isNaN(yyNum)) {
dLabel = dLabel.replace(`${yyNum}`, `${yyNum - 1}`)
}
}
}
}
} else {
dValue = null
}
reactData.datePanelValue = dValue
reactData.datePanelLabel = dLabel
}
/**
* 值变化时处理
*/
const changeValue = () => {
const isCalendarType = computeIsCalendarType.value
const { inputValue } = reactData
if (isCalendarType) {
dateParseValue(inputValue)
reactData.inputValue = props.multiple ? computeDateMultipleLabel.value : reactData.datePanelLabel
}
}
/**
* 检查初始值
*/
const initValue = () => {
const isCalendarType = computeIsCalendarType.value
if (isCalendarType) {
changeValue()
}
}
const dateCheckMonth = (date: Date) => {
const month = XEUtils.getWhatMonth(date, 0, 'first')
if (!XEUtils.isEqual(month, reactData.selectMonth)) {
reactData.selectMonth = month
}
}
const dateChange = (date: Date) => {
const { modelValue, multiple } = props
const dateValueFormat = computeDateValueFormat.value
const firstDayOfWeek = computeFirstDayOfWeek.value
if (props.type === 'week') {
const sWeek = XEUtils.toNumber(props.selectDay) as VxeCalendarPropTypes.SelectDay
date = XEUtils.getWhatWeek(date, 0, sWeek, firstDayOfWeek)
}
const inpVal = XEUtils.toDateString(date, dateValueFormat, { firstDay: firstDayOfWeek })
dateCheckMonth(date)
reactData.selectValue = date
if (multiple) {
// 如果为多选
const dateMultipleValue = computeDateMultipleValue.value
// 如果是日期类型
if (dateMultipleValue.some(val => XEUtils.isEqual(val, inpVal))) {
handleChange(dateMultipleValue.filter(val => !XEUtils.isEqual(val, inpVal)).join(','), { type: 'update' })
} else {
handleChange(dateMultipleValue.concat([inpVal]).join(','), { type: 'update' })
}
} else {
// 如果为单选
if (!XEUtils.isEqual(modelValue, inpVal)) {
handleChange(inpVal, { type: 'update' })
}
}
}
const dateMonthHandle = (date: Date, offsetMonth: number) => {
reactData.selectMonth = XEUtils.getWhatMonth(date, offsetMonth, 'first')
}
const dateNowHandle = () => {
const currentDate = XEUtils.getWhatDay(Date.now(), 0, 'first')
reactData.currentDate = currentDate
dateMonthHandle(currentDate, 0)
}
const dateToggleYearTypeEvent = () => {
reactData.datePanelType = 'year'
}
const dateToggleMonthTypeEvent = (evnt: MouseEvent) => {
let { datePanelType } = reactData
if (datePanelType === 'month' || datePanelType === 'quarter') {
datePanelType = 'year'
} else {
datePanelType = 'month'
}
reactData.datePanelType = datePanelType
changeViewEvent(evnt)
}
const datePrevEvent = (evnt: Event) => {
const { type } = props
const { datePanelType, selectMonth } = reactData
const { yearSize } = internalData
const isDisabledPrevDateBtn = computeIsDisabledPrevDateBtn.value
if (!isDisabledPrevDateBtn) {
if (type === 'year') {
reactData.selectMonth = XEUtils.getWhatYear(selectMonth, -yearSize, 'first')
} else if (type === 'month' || type === 'quarter') {
if (datePanelType === 'year') {
reactData.selectMonth = XEUtils.getWhatYear(selectMonth, -yearSize, 'first')
} else {
reactData.selectMonth = XEUtils.getWhatYear(selectMonth, -1, 'first')
}
} else {
if (datePanelType === 'year') {
reactData.selectMonth = XEUtils.getWhatYear(selectMonth, -yearSize, 'first')
} else if (datePanelType === 'month') {
reactData.selectMonth = XEUtils.getWhatYear(selectMonth, -1, 'first')
} else {
reactData.selectMonth = XEUtils.getWhatMonth(selectMonth, -1, 'first')
}
}
dispatchEvent('date-prev', { type }, evnt)
changeViewEvent(evnt)
}
}
const dateTodayMonthEvent = (evnt: Event) => {
dateNowHandle()
if (!props.multiple) {
dateChange(reactData.currentDate)
}
dispatchEvent('date-today', { type: props.type }, evnt)
changeViewEvent(evnt)
}
const dateNextEvent = (evnt: Event) => {
const { type } = props
const { datePanelType, selectMonth } = reactData
const { yearSize } = internalData
const isDisabledNextDateBtn = computeIsDisabledNextDateBtn.value
if (!isDisabledNextDateBtn) {
if (type === 'year') {
reactData.selectMonth = XEUtils.getWhatYear(selectMonth, yearSize, 'first')
} else if (type === 'month' || type === 'quarter') {
if (datePanelType === 'year') {
reactData.selectMonth = XEUtils.getWhatYear(selectMonth, yearSize, 'first')
} else {
reactData.selectMonth = XEUtils.getWhatYear(selectMonth, 1, 'first')
}
} else {
if (datePanelType === 'year') {
reactData.selectMonth = XEUtils.getWhatYear(selectMonth, yearSize, 'first')
} else if (datePanelType === 'month') {
reactData.selectMonth = XEUtils.getWhatYear(selectMonth, 1, 'first')
} else {
reactData.selectMonth = XEUtils.getWhatMonth(selectMonth, 1, 'first')
}
}
dispatchEvent('date-next', { type }, evnt)
changeViewEvent(evnt)
}
}
const isDateDisabled = (item: { date: Date }) => {
const { disabledMethod } = props
const { datePanelType } = reactData
return disabledMethod && disabledMethod({ type: datePanelType, viewType: datePanelType, date: item.date, $calendar: $xeCalendar })
}
const changeViewEvent = (evnt: Event | null) => {
const { datePanelType } = reactData
const yearDatas = computeYearDatas.value
const quarterDatas = computeQuarterDatas.value
const monthDatas = computeMonthDatas.value
const weekDates = computeWeekDates.value
const dayDatas = computeDayDatas.value
const viewDates: Date[] = []
let dataList: { date: Date }[][] = []
switch (datePanelType) {
case 'year':
dataList = yearDatas
break
case 'quarter':
dataList = quarterDatas
break
case 'month':
dataList = monthDatas
break
case 'week':
dataList = weekDates
break
case 'day':
dataList = dayDatas
break
}
dataList.forEach(rows => {
rows.forEach(item => {
viewDates.push(item.date)
})
})
dispatchEvent('view-change', { viewType: datePanelType, viewDates }, evnt)
}
const dateSelectItem = (date: Date) => {
const { type } = props
const { datePanelType } = reactData
if (type === 'month') {
if (datePanelType === 'year') {
reactData.datePanelType = 'month'
dateCheckMonth(date)
changeViewEvent(null)
} else {
dateChange(date)
}
} else if (type === 'year') {
dateChange(date)
} else if (type === 'quarter') {
if (datePanelType === 'year') {
reactData.datePanelType = 'quarter'
dateCheckMonth(date)
changeViewEvent(null)
} else {
dateChange(date)
}
} else {
if (datePanelType === 'month') {
reactData.datePanelType = type === 'week' ? type : 'day'
dateCheckMonth(date)
changeViewEvent(null)
} else if (datePanelType === 'year') {
reactData.datePanelType = 'month'
dateCheckMonth(date)
changeViewEvent(null)
} else {
dateChange(date)
}
}
}
const dateClickEvent = (evnt: MouseEvent, item: VxeDatePanelDefines.DateYearItem | VxeDatePanelDefines.DateQuarterItem | VxeDatePanelDefines.DateMonthItem | VxeDatePanelDefines.DateDayItem) => {
const { type } = props
const { datePanelType } = reactData
const { date } = item
if (!isDateDisabled(item)) {
dateSelectItem(date)
dispatchEvent('cell-click', { date, type, viewType: datePanelType }, evnt)
}
}
const datContextmenuEvent = (evnt: MouseEvent, item: VxeDatePanelDefines.DateYearItem | VxeDatePanelDefines.DateQuarterItem | VxeDatePanelDefines.DateMonthItem | VxeDatePanelDefines.DateDayItem) => {
const { type } = props
const { datePanelType } = reactData
const { menuConfig } = props
const menuOpts = computeMenuOpts.value
if (menuConfig ? isEnableConf(menuOpts) : menuOpts.enabled) {
const { options, visibleMethod } = menuOpts
const { date } = item
if (!visibleMethod || visibleMethod({ $calendar: $xeCalendar, options, date, type, viewType: datePanelType })) {
if (VxeUI.contextMenu) {
VxeUI.contextMenu.openByEvent(evnt, {
options,
events: {
optionClick (eventParams) {
const { option } = eventParams
const gMenuOpts = menus.get(option.code)
const cmMethod = gMenuOpts ? gMenuOpts.calendarMenuMethod : null
const params = { menu: option, date, type, viewType: datePanelType, $event: evnt, $calendar: $xeCalendar }
if (cmMethod) {
cmMethod(params, evnt)
}
dispatchEvent('menu-click', params, eventParams.$event)
}
}
})
}
}
}
dispatchEvent('cell-menu', { date: item.date, type, viewType: datePanelType }, evnt)
}
const dateMoveDay = (offsetDay: Date) => {
if (!isDateDisabled({ 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 (!isDateDisabled({ 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 (!isDateDisabled({ 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 (!isDateDisabled({ 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 (!isDateDisabled(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 dateConfirmEvent = () => {
}
const dateOpenPanel = () => {
const { type } = props
const dateValue = computeDateValue.value
if (['year', 'quarter', 'month', 'week'].indexOf(type) > -1) {
reactData.datePanelType = type as 'year' | 'quarter' | 'month' | 'week'
} else {
reactData.datePanelType = 'day'
}
reactData.currentDate = XEUtils.getWhatDay(Date.now(), 0, 'first')
if (dateValue) {
dateMonthHandle(dateValue, 0)
dateParseValue(dateValue)
} else {
dateNowHandle()
}
}
const renderDateLabel = (item: VxeDatePanelDefines.DateYearItem | VxeDatePanelDefines.DateQuarterItem | VxeDatePanelDefines.DateMonthItem | VxeDatePanelDefines.DateDayItem, label: string | number) => {
const { festivalMethod } = props
if (festivalMethod) {
const { datePanelType } = reactData
const festivalRest = festivalMethod({ type: datePanelType, viewType: datePanelType, date: item.date, $calendar: $xeCalendar })
const festivalItem = festivalRest ? (XEUtils.isString(festivalRest) ? { label: festivalRest } : festivalRest) : {}
const extraItem = festivalItem.extra ? (XEUtils.isString(festivalItem.extra) ? { label: festivalItem.extra } : festivalItem.extra) : null
const labels = [
h('span', {
class: ['vxe-calendar--label', {
'is-notice': festivalItem.notice
}]
}, extraItem && extraItem.label
? [
h('span', {
class: 'vxe-calendar--label--number'
}, `${label || ''}`),
h('span', {
class: ['vxe-calendar--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(',')
labels.push(
h('span', {
class: ['vxe-calendar--festival', festivalItem.important ? 'is-important' : '', festivalItem.className],
style: festivalItem.style
}, [
festivalLabels.length > 1
? h('span', {
class: ['vxe-calendar--festival--overlap', `overlap--${festivalLabels.length}`]
}, festivalLabels.map(label => h('span', label.substring(0, 3))))
: h('span', {
class: 'vxe-calendar--festival--label'
}, festivalLabels[0].substring(0, 3))
])
)
}
return labels
}
return `${label || ''}`
}
const renderDateDayTable = () => {
const { multiple, cellStyle } = props
const { datePanelType, datePanelValue } = reactData
const dateValue = computeDateValue.value
const dateHeaders = computeDateHeaders.value
const dayDatas = computeDayDatas.value
const dateListValue = computeDateListValue.value
const matchFormat = 'yyyyMMdd'
return [
h('div', {
class: ['vxe-calendar--view-wrapper', `type--${datePanelType}`]
}, [
h('div', {
class: 'vxe-calendar--view-header',
style: {
height: `${100 / (dayDatas.length + 1)}%`
}
}, [
h('div', {
class: 'vxe-calendar--view-row'
}, dateHeaders.map((item) => {
return h('div', {
class: 'vxe-calendar--view-item',
style: {
width: `${100 / dateHeaders.length}%`
}
}, [
h('div', {
class: 'vxe-calendar--view-item-inner'
}, [
h('div', {
class: 'vxe-calendar--view-item-label'
}, item.label)
])
])
}))
]),
h('div', {
class: 'vxe-calendar--view-body'
}, dayDatas.map((rows) => {
return h('div', {
class: 'vxe-calendar--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-calendar--view-item', {
'is--prev': item.isPrev,
'is--current': item.isCurrent,
'is--now': item.isNow,
'is--next': item.isNext,
'is--disabled': isDateDisabled(item),
'is--selected': isSelected,
'is--hover': XEUtils.isDateSame(datePanelValue, item.date, matchFormat)
}],
style: Object.assign({}, XEUtils.isFunction(cellStyle) ? cellStyle({ type: datePanelType, viewType: datePanelType, date: item.date, $calendar: $xeCalendar }) : cellStyle, {
width: `${100 / rows.length}%`
}),
onClick: (evnt) => dateClickEvent(evnt, item),
onContextmenu: (evnt) => datContextmenuEvent(evnt, item),
onMouseenter: () => dateMouseenterEvent(item),
onMouseleave: dateMouseleaveEvent
}, [
h('div', {
class: 'vxe-calendar--view-item-inner'
}, renderDateLabel(item, item.label))
])
}))
}))
])
]
}
const renderDateWeekTable = () => {
const { multiple, cellStyle } = props
const { datePanelType, datePanelValue } = reactData
const dateValue = computeDateValue.value
const weekHeaders = computeWeekHeaders.value
const weekDates = computeWeekDates.value
const dateListValue = computeDateListValue.value
const matchFormat = 'yyyyMMdd'
return [
h('div', {
class: ['vxe-calendar--view-wrapper', `type--${datePanelType}`]
}, [
h('div', {
class: 'vxe-calendar--view-header',
style: {
height: `${100 / (weekDates.length + 1)}%`
}
}, [
h('div', {
class: 'vxe-calendar--view-row'
}, weekHeaders.map((item, rIndex) => {
return h('div', {
class: 'vxe-calendar--view-item',
style: {
width: `${rIndex ? 13 : 9}%`
}
}, [
h('div', {
class: 'vxe-calendar--view-item-inner'
}, [
h('div', {
class: 'vxe-calendar--view-item-label'
}, item.label)
])
])
}))
]),
h('div', {
class: 'vxe-calendar--view-body'
}, weekDates.map((rows) => {
const isSelected = multiple ? rows.some((item) => dateListValue.some(val => XEUtils.isDateSame(val, item.date, matchFormat))) : rows.some((item) => XEUtils.isDateSame(dateValue, item.date, matchFormat))
const isHover = rows.some((item) => XEUtils.isDateSame(datePanelValue, item.date, matchFormat))
const isNowWeek = rows.some(item => item.isNow)
return h('div', {
class: 'vxe-calendar--view-row',
style: {
height: `${100 / weekDates.length}%`
}
}, rows.map((item, rIndex) => {
return h('div', {
class: ['vxe-calendar--view-item', {
'is--prev': item.isPrev,
'is--current': item.isCurrent,
'is--now': rIndex ? item.isNow : isNowWeek,
'is--next': item.isNext,
'is--disabled': isDateDisabled(item),
'is--selected': isSelected,
'is--hover': isHover
}],
style: Object.assign({}, XEUtils.isFunction(cellStyle) ? cellStyle({ type: datePanelType, viewType: datePanelType, date: item.date, $calendar: $xeCalendar }) : cellStyle, {
width: `${rIndex ? 13 : 9}%`
}),
onClick: (evnt) => dateClickEvent(evnt, item),
onContextmenu: (evnt) => datContextmenuEvent(evnt, item),
onMouseenter: () => dateMouseenterEvent(item),
onMouseleave: dateMouseleaveEvent
}, [
h('div', {
class: 'vxe-calendar--view-item-inner'
}, renderDateLabel(item, item.label))
])
}))
}))
])
]
}
const renderDateMonthTable = () => {
const { multiple, cellStyle } = props
const { datePanelType, datePanelValue } = reactData
const dateValue = computeDateValue.value
const monthDatas = computeMonthDatas.value
const dateListValue = computeDateListValue.value
const matchFormat = 'yyyyMM'
return [
h('div', {
class: ['vxe-calendar--view-wrapper', `type--${datePanelType}`]
}, [
h('div', {
class: 'vxe-calendar--view-body'
}, monthDatas.map((rows) => {
return h('div', {
class: 'vxe-calendar--view-row',
style: {
height: `${100 / monthDatas.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-calendar--view-item', {
'is--prev': item.isPrev,
'is--current': item.isCurrent,
'is--now': item.isNow,
'is--next': item.isNext,
'is--disabled': isDateDisabled(item),
'is--selected': isSelected,
'is--hover': XEUtils.isDateSame(datePanelValue, item.date, matchFormat)
}],
style: Object.assign({}, XEUtils.isFunction(cellStyle) ? cellStyle({ type: datePanelType, viewType: datePanelType, date: item.date, $calendar: $xeCalendar }) : cellStyle, {
width: `${100 / rows.length}%`
}),
onClick: (evnt) => dateClickEvent(evnt, item),
onContextmenu: (evnt) => datContextmenuEvent(evnt, item),
onMouseenter: () => dateMouseenterEvent(item),
onMouseleave: dateMouseleaveEvent
}, [
h('div', {
class: 'vxe-calendar--view-item-inner'
}, renderDateLabel(item, getI18n(`vxe.input.date.months.m${item.month}`)))
])
}))
}))
])
]
}
const renderDateQuarterTable = () => {
const { multiple, cellStyle } = props
const { datePanelType, datePanelValue } = reactData
const dateValue = computeDateValue.value
const quarterDatas = computeQuarterDatas.value
const dateListValue = computeDateListValue.value
const matchFormat = 'yyyyq'
return [
h('div', {
class: ['vxe-calendar--view-wrapper', `type--${datePanelType}`]
}, [
h('div', {
class: 'vxe-calendar--view-body'
}, quarterDatas.map((rows) => {
return h('div', {
class: 'vxe-calendar--view-row',
style: {
height: `${100 / quarterDatas.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-calendar--view-item', {
'is--prev': item.isPrev,
'is--current': item.isCurrent,
'is--now': item.isNow,
'is--next': item.isNext,
'is--disabled': isDateDisabled(item),
'is--selected': isSelected,
'is--hover': XEUtils.isDateSame(datePanelValue, item.date, matchFormat)
}],
style: Object.assign({}, XEUtils.isFunction(cellStyle) ? cellStyle({ type: datePanelType, viewType: datePanelType, date: item.date, $calendar: $xeCalendar }) : cellStyle, {
width: `${100 / rows.length}%`
}),
onClick: (evnt) => dateClickEvent(evnt, item),
onContextmenu: (evnt) => datContextmenuEvent(evnt, item),
onMouseenter: () => dateMouseenterEvent(item),
onMouseleave: dateMouseleaveEvent
}, [
h('div', {
class: 'vxe-calendar--view-item-inner'
}, renderDateLabel(item, getI18n(`vxe.input.date.quarters.q${item.quarter}`)))
])
}))
}))
])
]
}
const renderDateYearTable = () => {
const { multiple, cellStyle } = props
const { datePanelType, datePanelValue } = reactData
const dateValue = computeDateValue.value
const yearDatas = computeYearDatas.value
const dateListValue = computeDateListValue.value
const matchFormat = 'yyyy'
return [
h('div', {
class: ['vxe-calendar--view-wrapper', `type--${datePanelType}`]
}, [
h('div', {
class: 'vxe-calendar--view-body'
}, yearDatas.map((rows) => {
return h('div', {
class: 'vxe-calendar--view-row',
style: {
height: `${100 / yearDatas.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-calendar--view-item', {
'is--prev': item.isPrev,
'is--current': item.isCurrent,
'is--now': item.isNow,
'is--next': item.isNext,
'is--disabled': isDateDisabled(item),
'is--selected': isSelected,
'is--hover': XEUtils.isDateSame(datePanelValue, item.date, matchFormat)
}],
style: Object.assign({}, XEUtils.isFunction(cellStyle) ? cellStyle({ type: datePanelType, viewType: datePanelType, date: item.date, $calendar: $xeCalendar }) : cellStyle, {
width: `${100 / rows.length}%`
}),
onClick: (evnt) => dateClickEvent(evnt, item),
onContextmenu: (evnt) => datContextmenuEvent(evnt, item),
onMouseenter: () => dateMouseenterEvent(item),
onMouseleave: dateMouseleaveEvent
}, [
h('div', {
class: 'vxe-calendar--view-item-inner'
}, renderDateLabel(item, item.year))
])
}))
}))
])
]
}
const renderDateTable = () => {
const { datePanelType } = reactData
switch (datePanelType) {
case 'week' :
return renderDateWeekTable()
case 'month' :
return renderDateMonthTable()
case 'quarter' :
return renderDateQuarterTable()
case 'year' :
return renderDateYearTable()
}
return renderDateDayTable()
}
const renderDatePanel = () => {
const { multiple } = props
const { datePanelType } = reactData
const isDisabledPrevDateBtn = computeIsDisabledPrevDateBtn.value
const isDisabledNextDateBtn = computeIsDisabledNextDateBtn.value
const selectDatePanelObj = computeSelectDatePanelObj.value
return [
h('div', {
class: 'vxe-calendar--header'
}, [
h('div', {
class: 'vxe-calendar--type-wrapper'
}, [
datePanelType === 'year'
? h(VxeButtonComponent, {
class: 'vxe-calendar--date-picker-label',
disabled: datePanelType === 'year',
content: selectDatePanelObj.y
})
: h('span', {
class: 'vxe-calendar--date-picker-btns'
}, [
h(VxeButtonComponent, {
class: 'vxe-calendar--date-picker-btn',
content: selectDatePanelObj.y,
onClick: dateToggleYearTypeEvent
}),
selectDatePanelObj.m
? h(VxeButtonComponent, {
class: 'vxe-calendar--date-picker-btn',
content: selectDatePanelObj.m,
onClick: dateToggleMonthTypeEvent
})
: renderEmptyElement($xeCalendar)
])
]),
h('div', {
class: 'vxe-calendar--btn-wrapper'
}, [
h(VxeButtonComponent, {
disabled: isDisabledPrevDateBtn,
icon: 'vxe-icon-caret-left',
onClick: datePrevEvent
}),
h(VxeButtonComponent, {
icon: 'vxe-icon-dot',
onClick: dateTodayMonthEvent
}),
h(VxeButtonComponent, {
disabled: isDisabledNextDateBtn,
icon: 'vxe-icon-caret-right',
onClick: dateNextEvent
}),
multiple && computeSupportMultiples.value
? h('span', {
class: 'vxe-calendar--btn vxe-calendar--confirm-btn'
}, [
h('button', {
class: 'vxe-calendar--confirm',
type: 'button',
onClick: dateConfirmEvent
}, getI18n('vxe.button.confirm'))
])
: null
])
]),
h('div', {
class: 'vxe-calendar--body'
}, renderDateTable())
]
}
const dispatchEvent = (type: ValueOf<VxeCalendarEmits>, params: Record<string, any>, evnt: Event | null) => {
emit(type, createEvent(evnt, { $input: $xeCalendar }, params))
}
const calendarMethods: CalendarMethods = {
dispatchEvent
}
Object.assign($xeCalendar, calendarMethods)
const renderVN = () => {
const { className, type } = props
const vSize = computeSize.value
const isDisabled = computeIsDisabled.value
const calendarStyle = computeCalendarStyle.value
return h('div', {
ref: refElem,
class: ['vxe-calendar', `type--${type}`, className, {
[`size--${vSize}`]: vSize,
'is--disabled': isDisabled
}],
style: calendarStyle
}, [
renderDatePanel()
])
}
$xeCalendar.renderVN = renderVN
watch(() => props.modelValue, (val) => {
reactData.inputValue = val
changeValue()
})
watch(() => props.type, () => {
// 切换类型是重置内置变量
Object.assign(reactData, {
selectValue: null,
inputValue: null,
datePanelValue: null,
datePanelLabel: '',
datePanelType: 'day',
selectMonth: null,
currentDate: null
})
initValue()
dateOpenPanel()
})
watch(computeDateLabelFormat, () => {
const isCalendarType = computeIsCalendarType.value
if (isCalendarType) {
dateParseValue(reactData.datePanelValue)
reactData.inputValue =