效果如下图:
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/0dfe53c359e64149bcb91244b9bce910.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/b1dfe485615e45928ced8f4463a4979c.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/75c144389b984a1e9a5b9246c9a09cf1.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/de14384e77cb483caa39c011a54bdb6a.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/1cd3465e1407436fa69d84689d610d9b.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/d55a29fe1fbb4b22939f6266c907e88a.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/ada7a26bf5214ce1bad2ed5be8b79e12.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/d2d3bed4966b46fea7463c6c31f545c3.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/85e18910519949ba826bfd17a5930d0c.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/dc0623d56ac543c099007795ce2d287b.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/99e6c7ab1f734cadafdedd3c7c049096.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/5943b2240347437fb40bb62bfad58e60.png)
APIs
Calendar
参数 | 说明 | 类型 | 默认值 |
---|
display | 日历展示方式,面板/卡片 | ‘panel’ | ‘card’ | ‘panel’ |
mode | 初始模式 | ‘month’ | ‘year’ | ‘month’ |
header | 自定义日历头部内容 | string | slot | undefined |
startDayOfWeek | 一周的开始是星期几,0-6 ,0 是周一 | DayOfWeek | 0 |
dateStrip | 日历面板默认会显示六周的日期,当最后一周的日期不包含当月日期时,是否去掉 | boolean | true |
dateFormat | 自定义日期展示格式 | (date: number, timestamp: number) => string | undefined |
weekFormat | 自定义星期展示格式 (defaultWeek: DefaultWeek, week: number, timestamp: number) => string | undefined | |
monthFormat | 自定义月展示格式 | (month: number, timestamp: number) => string | undefined |
disabledDate | 不可选择的日期 | (timestamp: number) => boolean | undefined |
valueFormat | 被选中日期的格式,默认为时间戳;参考 format | string | undefined |
value v-model | 当前被选中的日期的时间戳 | string | number | undefined |
DayOfWeek Type
名称 | 值 |
---|
DayOfWeek | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
DefaultWeek Type
名称 | 值 |
---|
DefaultWeek | ‘一’ | ‘二’ | ‘三’ | ‘四’ | ‘五’ | ‘六’ | ‘日’ |
DateItem Type
名称 | 说明 | 类型 | 默认值 |
---|
type | 类型 | ‘date’ | undefined |
dateObject | 日期对象 | { date: number, month: number, year: number } | undefined |
timestamp | 时间戳 | number | undefined |
inCurrentMonth | 是否在当前月 | boolean | undefined |
isCurrentDate | 是否为今天 | boolean | undefined |
MonthItem Type
名称 | 说明 | 类型 | 默认值 |
---|
type | 类型 | ‘month’ | undefined |
monthObject | 月份对象 | { month: number, year: number } | undefined |
timestamp | 时间戳 | number | undefined |
isCurrent | 是否为当前月 | boolean | undefined |
Slots
名称 | 说明 | 类型 |
---|
header | 自定义日历头部内容 | v-slot:header |
week | 自定义周展示 | v-slot:week=“{ defaultWeek, week, timestamp }” |
dateValue | 自定义日期展示 | v-slot:dateValue=“{ type, dateObject, timestamp, inCurrentMonth, isCurrentDate }” |
dateContent | 自定义日期内容展示 | v-slot:dateContent=“{ type, dateObject, timestamp, inCurrentMonth, isCurrentDate }” |
monthValue | 自定义月份展示 | v-slot:dateValue=“{ type, monthObject, timestamp, isCurrent }” |
monthContent | 自定义月份内容展示 | v-slot:dateContent=“{ type, monthObject, timestamp, isCurrent }” |
Events
名称 | 说明 | 类型 |
---|
change | 日期变化时的回调 | (date: string | number, dateOrMonth: DateItem[‘dateObject’] | MonthItem[‘monthObject’]) => void |
panelChange | 日期面板变化的回调 | (date: string | number, info: { year: number, month?: number }, mode: ‘month’ | ‘year’) => void |
select | 选择日期回调,包含来源信息 | (date: string | number, source: ‘date’ | ‘month’) => void |
创建日历组件ColorPicker.vue
其中引入使用了以下组件和工具函数:
- Vue3选择器(Select)
- Vue3单选框(Radio)
<script setup lang="ts">
import { ref, watch, computed } from 'vue'
import {addDays,addMonths,addYears,format,getDate,getDay,getMonth,getTime,getYear,isSameDay,isSameMonth,parse,startOfDay,startOfMonth,startOfYear
} from 'date-fns'
import Select from 'components/select'
import Radio from 'components/radio'
import { useSlotsExist } from 'components/utils'
import type { SelectOption, RadioOption } from 'vue-amazing-ui'
export type DayOfWeek = 0 | 1 | 2 | 3 | 4 | 5 | 6
export type DefaultWeek = '一' | '二' | '三' | '四' | '五' | '六' | '日'
export interface Props {display?: 'panel' | 'card' mode?: 'month' | 'year' header?: string startDayOfWeek?: DayOfWeek dateStrip?: boolean dateFormat?: (date: number, timestamp: number) => string weekFormat?: (defaultWeek: DefaultWeek, week: number, timestamp: number) => string monthFormat?: (month: number, timestamp: number) => string disabledDate?: (timestamp: number) => boolean valueFormat?: string value?: string | number
}
const props = withDefaults(defineProps<Props>(), {display: 'panel',mode: 'month',header: undefined,startDayOfWeek: 0,dateStrip: true,dateFormat: undefined,weekFormat: undefined,monthFormat: undefined,disabledDate: undefined,valueFormat: undefined,value: undefined
})
export interface DateItem {type: 'date'dateObject: {date: numbermonth: numberyear: number}timestamp: numberinCurrentMonth: booleanisCurrentDate: boolean
}
export interface MonthItem {type: 'month'monthObject: {month: numberyear: number}timestamp: numberisCurrent: boolean
}
const now = ref(Date.now())
const yearOptions = ref<SelectOption[]>([])
const monthOptions = ref<SelectOption[]>(getMonthOptions())
const modeOptions = ref<RadioOption[]>([{label: '月',value: 'month'},{label: '年',value: 'year'}
])
const calendarYear = ref<number>(getYear(now.value))
const calendarMonth = ref<number>(getMonth(now.value) + 1)
const calendarMode = ref<Props['mode']>(props.mode)
const calendarDates = ref<Array<DateItem[]>>([])
const selectedValue = ref<string | number>()
const calendarMonths = ref<Array<MonthItem[]>>([])
const slotsExist = useSlotsExist(['header'])
const emits = defineEmits(['update:value', 'change', 'panelChange', 'select'])
const defaultWeeks = computed(() => {const origin: DefaultWeek[] = ['一', '二', '三', '四', '五', '六', '日']const result: DefaultWeek[] = []result.push(origin[props.startDayOfWeek])let startDay = (props.startDayOfWeek + 1) % 7while (startDay !== props.startDayOfWeek) {result.push(origin[startDay])startDay = (startDay + 1) % 7}return result
})
const selectedValueTimestamp = computed(() => {if (!selectedValue.value) returnif (typeof selectedValue.value === 'string') {return parse(selectedValue.value, props.valueFormat as string, new Date()).getTime()}return selectedValue.value
})
const startOfMonthTimestamp = computed(() => {const firstDayOfMonth = new Date(calendarYear.value, calendarMonth.value - 1, 1)return firstDayOfMonth.getTime()
})
const showHeader = computed(() => {return slotsExist.header || props.header
})
watch(selectedValueTimestamp,(to) => {if (to) {calendarYear.value = getYear(to)calendarMonth.value = getMonth(to) + 1}},{immediate: true}
)
watch(() => props.mode,(to) => {calendarMode.value = to}
)
watch(() => [props.startDayOfWeek, props.dateStrip, calendarMonth.value, calendarYear.value],() => {calendarDates.value = getCalendarDates()},{immediate: true,deep: true}
)
watch(calendarYear,() => {yearOptions.value = getYearOptions()calendarMonths.value = getCalendarMonths()},{immediate: true}
)
watch(() => [props.valueFormat, props.value],() => {if (props.value) {if (props.valueFormat === undefined || props.valueFormat === 'T') {selectedValue.value = Number(format(startOfDay(props.value).getTime(), props.valueFormat || 'T'))} else {selectedValue.value = format(startOfDay(props.value).getTime(), props.valueFormat)}}},{deep: true,immediate: true}
)
function getYearOptions(): SelectOption[] {const options = []const startYear = calendarYear.value - 10const endYear = calendarYear.value + 10for (let y = startYear; y < endYear; y++) {options.push({label: `${y}年`,value: y})}return options
}
function getMonthOptions(): SelectOption[] {const options = []for (let m = 1; m <= 12; m++) {options.push({label: `${m}月`,value: m})}return options
}
function getDateItem(time: number, monthTs: number, currentTs: number): DateItem {return {type: 'date',dateObject: {date: getDate(time),month: getMonth(time),year: getYear(time)},timestamp: getTime(time),inCurrentMonth: isSameMonth(time, monthTs),isCurrentDate: isSameDay(currentTs, time)}
}
function getDates(monthTs: number, currentTs: number, startDay: DayOfWeek, strip: boolean = false): DateItem[] {const displayMonth = getMonth(monthTs)let displayMonthIterator = getTime(startOfMonth(monthTs))let lastMonthIterator = getTime(addDays(displayMonthIterator, -1))const calendarDays: DateItem[] = []let protectLastMonthDateIsShownFlag = !stripwhile (getDay(lastMonthIterator) !== startDay || protectLastMonthDateIsShownFlag) {calendarDays.unshift(getDateItem(lastMonthIterator, monthTs, currentTs))lastMonthIterator = getTime(addDays(lastMonthIterator, -1))protectLastMonthDateIsShownFlag = false}while (getMonth(displayMonthIterator) === displayMonth) {calendarDays.push(getDateItem(displayMonthIterator, monthTs, currentTs))displayMonthIterator = getTime(addDays(displayMonthIterator, 1))}const endIndex = strip ? (calendarDays.length <= 28 ? 28 : calendarDays.length <= 35 ? 35 : 42) : 42while (calendarDays.length < endIndex) {calendarDays.push(getDateItem(displayMonthIterator, monthTs, currentTs))displayMonthIterator = getTime(addDays(displayMonthIterator, 1))}return calendarDays
}
function getCalendarDates(): DateItem[][] {const dates = getDates(startOfMonthTimestamp.value, Date.now(), props.startDayOfWeek, props.dateStrip)const chunkSize = 7const chunkCount = dates.length / chunkSizeconst result: DateItem[][] = []for (let i = 0; i < chunkCount; i++) {result.push(dates.slice(i * chunkSize, (i + 1) * chunkSize))}return result
}
function getMonthItem(monthTs: number, currentTs: number): MonthItem {return {type: 'month',monthObject: {month: getMonth(monthTs),year: getYear(monthTs)},timestamp: getTime(monthTs),isCurrent: isSameMonth(currentTs, monthTs)}
}
function getMonths(yearAnchorTs: number, currentTs: number): MonthItem[] {const calendarMonths: MonthItem[] = []const yearStart = startOfYear(yearAnchorTs)for (let i = 0; i < 12; i++) {calendarMonths.push(getMonthItem(getTime(addMonths(yearStart, i)), currentTs))}return calendarMonths
}
function getCalendarMonths(): MonthItem[][] {const months = getMonths(startOfMonthTimestamp.value, Date.now())const chunkSize = 3const chunkCount = months.length / chunkSizeconst result: MonthItem[][] = []for (let i = 0; i < chunkCount; i++) {result.push(months.slice(i * chunkSize, (i + 1) * chunkSize))}return result
}
function getDateStr(date: DateItem): string {return format(date.timestamp, 'yyyy-MM-dd')
}
function getMonthStr(month: MonthItem): string {return format(month.timestamp, 'yyyy-MM')
}
function getDateFormat(date: number, timestamp: number): string {if (props.dateFormat === undefined) {return `${date}`} else {return props.dateFormat(date, timestamp)}
}
function getWeekFormat(defaultWeek: DefaultWeek, week: number, timestamp: number): string {if (props.weekFormat === undefined) {return defaultWeek} else {return props.weekFormat(defaultWeek, week, timestamp)}
}
function getMonthFormat(month: number, timestamp: number): string {if (props.monthFormat === undefined) {return `${month}月`} else {return props.monthFormat(month, timestamp)}
}
function onDateSelected(date: DateItem): void {if (selectedValueTimestamp.value !== date.timestamp) {if (date.dateObject.month + 1 !== calendarMonth.value) {calendarMonth.value = date.dateObject.month + 1}if (props.valueFormat === undefined || props.valueFormat === 'T') {selectedValue.value = date.timestamp} else {selectedValue.value = format(date.timestamp, props.valueFormat)}emits('update:value', selectedValue.value)emits('select', selectedValue.value, 'date')emits('change', selectedValue.value, date.dateObject)}
}
function getSelectedValueStartOfMonthTimeStamp(): number | undefined {if (!selectedValue.value) returnif (typeof selectedValue.value === 'string') {const selectedValueTimestamp = parse(selectedValue.value, props.valueFormat as string, new Date()).getTime()return startOfMonth(selectedValueTimestamp).getTime()}return startOfMonth(selectedValue.value).getTime()
}
function onMonthSelected(month: MonthItem): void {if (getSelectedValueStartOfMonthTimeStamp() !== month.timestamp) {calendarMonth.value = month.monthObject.month + 1if (selectedValue.value) {const offsetYears = month.monthObject.year - getYear(selectedValueTimestamp.value as number)const offsetMonths = month.monthObject.month - getMonth(selectedValueTimestamp.value as number)const newDate = addMonths(addYears(selectedValueTimestamp.value as number, offsetYears), offsetMonths)if (props.valueFormat === undefined || props.valueFormat === 'T') {selectedValue.value = Number(format(newDate, props.valueFormat || 'T'))} else {selectedValue.value = format(newDate, props.valueFormat)}}emits('update:value', selectedValue.value)emits('select', selectedValue.value, 'month')emits('change', selectedValue.value, month.monthObject)}
}
function onPanelChange(): void {if (calendarMode.value === 'month') {emits('panelChange',selectedValue.value,{ year: calendarYear.value, month: calendarMonth.value },calendarMode.value)} else {emits('panelChange', selectedValue.value, { year: calendarYear.value }, calendarMode.value)}
}
</script>
<template><div class="m-calendar" :class="`calendar-${display}`"><div class="calendar-header-wrap"><div v-if="showHeader" class="calendar-header-content"><slot name="header">{{ header }}</slot></div><div class="calendar-header-actions"><Selectclass="calendar-year-select":size="display === 'card' ? 'small' : 'middle'":options="yearOptions":max-display="8"v-model="calendarYear"@change="onPanelChange"/><Selectv-if="calendarMode === 'month'"class="calendar-month-select":size="display === 'card' ? 'small' : 'middle'":options="monthOptions":max-display="8"v-model="calendarMonth"@change="onPanelChange"/><Radioclass="calendar-mode-radio":button-size="display === 'card' ? 'small' : 'middle'":options="modeOptions"v-model:value="calendarMode"button@change="onPanelChange"/></div></div><div tabindex="0" class="calendar-display-wrap"><div v-if="calendarMode === 'month'" class="calendar-date-panel"><div class="calendar-body"><table class="calendar-table"><thead><tr><th v-for="(defaultWeek, index) in defaultWeeks" :key="index"><slotname="week":defaultWeek="defaultWeek":week="index":timestamp="calendarDates[0][index].timestamp">{{ getWeekFormat(defaultWeek, index, calendarDates[0][index].timestamp) }}</slot></th></tr></thead><tbody><tr v-for="(weekDates, weekIndex) in calendarDates" :key="weekIndex"><tdclass="calendar-date-cell":class="{'date-cell-disabled': disabledDate && disabledDate(dateItem.timestamp),'date-cell-in-view': dateItem.inCurrentMonth,'date-cell-today': dateItem.isCurrentDate,'date-cell-selected': selectedValueTimestamp && selectedValueTimestamp === dateItem.timestamp}"v-for="(dateItem, index) in weekDates":key="index":title="getDateStr(dateItem)"><div class="date-cell-inner" @click="onDateSelected(dateItem)"><div class="date-value"><slot name="dateValue" v-bind="dateItem">{{getDateFormat(dateItem.dateObject.date, dateItem.timestamp)}}</slot></div><div class="date-content"><slot name="dateContent" v-bind="dateItem"></slot></div></div></td></tr></tbody></table></div></div><div v-if="calendarMode === 'year'" class="calendar-month-panel"><div class="calendar-body"><table class="calendar-table"><tbody><tr v-for="(rowMonths, rowIndex) in calendarMonths" :key="rowIndex"><tdclass="calendar-date-cell date-cell-in-view":class="{'date-cell-today': monthItem.isCurrent,'date-cell-selected':selectedValueTimestamp && startOfMonth(selectedValueTimestamp).getTime() === monthItem.timestamp}"v-for="(monthItem, index) in rowMonths":key="index":title="getMonthStr(monthItem)"><div class="date-cell-inner" @click="onMonthSelected(monthItem)"><div class="date-value"><slot name="monthValue" v-bind="monthItem">{{getMonthFormat(monthItem.monthObject.month + 1, monthItem.timestamp)}}</slot></div><div class="date-content"><slot name="monthContent" v-bind="monthItem"></slot></div></div></td></tr></tbody></table></div></div></div></div>
</template>
<style lang="less" scoped>
.m-calendar {color: rgba(0, 0, 0, 0.88);font-size: 14px;line-height: 1.5714285714285714;list-style: none;background: #ffffff;border-radius: 4px;.calendar-header-wrap {padding: 12px 0;.calendar-header-content {margin-bottom: 10px;:deep(svg) {fill: currentColor;}}.calendar-header-actions {display: flex;justify-content: flex-end;.calendar-month-select {margin-left: 8px;}.calendar-mode-radio {margin-left: 8px;}}}.calendar-display-wrap {background: #ffffff;outline: none;.calendar-date-panel,.calendar-month-panel {display: flex;flex-direction: column;width: auto;.calendar-body {padding: 8px 0;.calendar-table {display: table;margin: 0;width: 100%;table-layout: fixed;border-collapse: collapse;tr {border: none;background-color: transparent;}th,td {position: relative;min-width: 24px;font-size: inherit;font-weight: normal;padding: 0;border: none;}th {text-align: inherit;height: auto;line-height: 18px;width: 36px;color: rgba(0, 0, 0, 0.88);vertical-align: middle;background: transparent;}.calendar-date-cell {color: rgba(0, 0, 0, 0.25);cursor: pointer;.date-cell-inner {position: relative;z-index: 2;min-width: 24px;line-height: 24px;}&:hover:not(.date-cell-selected) {.date-cell-inner {background: rgba(0, 0, 0, 0.04);}}}.date-cell-disabled {color: rgba(0, 0, 0, 0.25);pointer-events: none;}.date-cell-in-view:not(.date-cell-disabled):not(.date-cell-selected) {color: rgba(0, 0, 0, 0.88);}}}}}
}
.calendar-panel {.calendar-header-wrap {.calendar-header-actions {.calendar-year-select {min-width: 90px;}.calendar-month-select {min-width: 72px;}}}.calendar-display-wrap {width: 100%;text-align: end;.calendar-date-panel,.calendar-month-panel {.calendar-body {.calendar-table {th {padding-right: 12px;padding-bottom: 4px;}.calendar-date-cell {.date-cell-inner {display: block;width: auto;height: auto;margin: 0 4px;padding: 4px 8px 0;border: 0;border-top: 2px solid rgba(5, 5, 5, 0.06);border-radius: 0;transition: background 0.3s;.date-value {line-height: 24px;transition: color 0.3s;}.date-content {position: static;width: auto;height: 86px;overflow-y: auto;color: rgba(0, 0, 0, 0.88);line-height: 1.5714285714285714;text-align: start;}}}.date-cell-today {.date-cell-inner {border-color: #1677ff;.date-value {color: rgba(0, 0, 0, 0.88);}}}.date-cell-selected {.date-cell-inner {color: #1677ff;background: #e6f4ff;}}}}}}
}
.calendar-card {width: 300px;border: 1px solid rgb(217, 217, 217);border-radius: 4px;.calendar-header-wrap {padding-left: 8px;padding-right: 8px;.calendar-header-actions {.calendar-year-select {min-width: 86px;}.calendar-month-select {min-width: 70px;}}}.calendar-display-wrap {display: inline-flex;flex-direction: column;text-align: center;border-top: 1px solid rgba(5, 5, 5, 0.06);border-radius: 0 0 8px 8px;.calendar-date-panel,.calendar-month-panel {.calendar-body {.calendar-table {.calendar-date-cell {padding: 4px 0;&::before {position: absolute;top: 50%;left: 0;right: 0;z-index: 1;height: 24px;transform: translateY(-50%);transition: all 0.3s;content: '';pointer-events: none;}.date-cell-inner {display: inline-block;height: 24px;border-radius: 4px;transition:background 0.2s,color 0.2s;}}.date-cell-disabled {&::before {background: rgba(0, 0, 0, 0.04);}}.date-cell-today {.date-cell-inner {&::before {position: absolute;top: 0;right: 0;bottom: 0;left: 0;z-index: 1;border: 1px solid #1677ff;border-radius: 4px;content: '';}}}.date-cell-disabled.date-cell-today {.date-cell-inner {&::before {border-color: rgba(0, 0, 0, 0.25);}}}.date-cell-selected {.date-cell-inner {color: #fff;background: #1677ff;}}}}}.calendar-date-panel {.calendar-body {.calendar-table {.calendar-date-cell {height: 40px;}}}}.calendar-month-panel {.calendar-body {.calendar-table {.calendar-date-cell {height: 64px;.date-cell-inner {width: 60px;padding: 0 8px;}}}}}}
}
</style>
在要使用的页面引入
其中引入使用了以下组件:
- Vue3弹性布局( Flex)
- Vue3间距(Space)
- Vue3警告提示(Alert)
- Vue3全局提示(Message)
<script setup lang="ts">
import Calendar from './Calendar.vue'
import { ref, watchEffect } from 'vue'
import { CalendarOutlined } from '@ant-design/icons-vue'
import { format, subDays, addDays } from 'date-fns'
import type { CalendarDayOfWeek, CalendarDefaultWeek, CalendarDateItem, CalendarMonthItem } from 'components/index'
const date = ref(Date.now())
const cardDate = ref(Date.now())
const modeDate = ref(Date.now())
const customCardStyleDate = ref(Date.now())
const headerDate = ref(Date.now())
const startDayOfWeekDate = ref(Date.now())
const formatDate = ref(Date.now())
const noticeDate = ref(Date.now())
const slotDate = ref(Date.now())
const selectDate = ref(new Date('2030-10-06').getTime())
const disableDate = ref(Date.now())
const dateStr = ref(format(Date.now(), 'yyyy-MM-dd'))
const message = ref()
const displayOptions = [{label: 'panel',value: 'panel'},{label: 'card',value: 'card'}
]
const modeDisplay = ref('card')
const display = ref('panel')
const weekOptions = [{label: '周一',value: 0},{label: '周二',value: 1},{label: '周三',value: 2},{label: '周四',value: 3},{label: '周五',value: 4},{label: '周六',value: 5},{label: '周日',value: 6}
]
const startDayOfWeek = ref<CalendarDayOfWeek>(6)
watchEffect(() => {console.log('date', date.value)
})
watchEffect(() => {console.log('cardDate', cardDate.value)
})
watchEffect(() => {console.log('modeDate', modeDate.value)
})
watchEffect(() => {console.log('customCardStyleDate', customCardStyleDate.value)
})
watchEffect(() => {console.log('headerDate', headerDate.value)
})
watchEffect(() => {console.log('startDayOfWeekDate', startDayOfWeekDate.value)
})
watchEffect(() => {console.log('formatDate', formatDate.value)
})
watchEffect(() => {console.log('noticeDate', noticeDate.value)
})
watchEffect(() => {console.log('slotDate', slotDate.value)
})
watchEffect(() => {console.log('selectDate', selectDate.value)
})
watchEffect(() => {console.log('disableDate', disableDate.value)
})
watchEffect(() => {console.log('dateStr', dateStr.value)
})
function cardDateFormat(date: number, timestamp: number) {return String(date).padStart(2, '0')
}
function cardWeekFormat(defaultWeek: CalendarDefaultWeek, week: CalendarDayOfWeek) {return `周${defaultWeek}`
}
function cardMonthFormat(month: number, timestamp: number) {const chineseNumber = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二']return `${chineseNumber[month - 1]}月`
}
function panelDateFormat(date: number, timestamp: number) {return format(timestamp, 'do')
}
function panelWeekFormat(defaultWeek: CalendarDefaultWeek, week: CalendarDayOfWeek, timestamp: number) {return format(timestamp, 'EEEE')
}
function panelMonthFormat(month: number, timestamp: number) {return format(timestamp, 'MMM')
}
function disabledDate(timestamp: number): boolean {if (subDays(Date.now(), 4).getTime() < timestamp && timestamp < addDays(Date.now(), 3).getTime()) {return true}return false
}
function disabledWeekend(timestamp: number): boolean {return ['6', '7'].includes(format(timestamp, 'i'))
}
function onSelect(date: string | number, source: 'date' | 'month') {console.log('select', date, source)message.value.success(format(date, 'yyyy-MM-dd'))
}
function onChange(date: string | number,dateOrMonth: CalendarDateItem['dateObject'] | CalendarMonthItem['monthObject']
) {console.log('change', date, dateOrMonth)
}
function onPanelChange(date: string | number, info: { year: number; month?: number }, mode: 'month' | 'year') {console.log('panelChange', date, info, mode)
}
</script>
<template><div><h1>{{ $route.name }} {{ $route.meta.title }}</h1><h2 class="mt30 mb10">基本使用</h2><Calendar v-model:value="date" @change="onChange" @panelChange="onPanelChange" /><h2 class="mt30 mb10">卡片模式</h2><Calendar v-model:value="cardDate" display="card" @change="onChange" @panelChange="onPanelChange" /><h2 class="mt30 mb10">初始模式</h2><Flex vertical><Space align="center">display:<Radio :options="displayOptions" v-model:value="modeDisplay" button button-style="solid" /></Space><Calendar v-model:value="modeDate" mode="year" :display="modeDisplay" /></Flex><h2 class="mt30 mb10">自定义头部内容</h2><Space><Calendar v-model:value="headerDate" header="Custom header" display="card" @panelChange="onPanelChange" /><Calendar v-model:value="headerDate" display="card" @panelChange="onPanelChange"><template #header> <CalendarOutlined /> 日历 </template></Calendar></Space><h2 class="mt30 mb10">自定义一周的开始</h2><Flex vertical><Space align="center">startDayOfWeek:<Radio :options="weekOptions" v-model:value="startDayOfWeek" button button-style="solid" /></Space><Calendar v-model:value="startDayOfWeekDate" :start-day-of-week="startDayOfWeek" /></Flex><h2 class="mt30 mb10">自定义展示格式</h2><h3 class="mb10">使用 weekFormat / dateFormat / monthFormat 自定义日期/星期/月的展示格式</h3><Flex vertical><Space><Calendarheader="Date format"display="card"v-model:value="formatDate":date-format="cardDateFormat"@panelChange="onPanelChange"/><Calendarheader="Week format"display="card"v-model:value="formatDate":week-format="cardWeekFormat"@panelChange="onPanelChange"/><Calendarheader="Month format"mode="year"display="card"v-model:value="formatDate":month-format="cardMonthFormat"@panelChange="onPanelChange"/></Space><Calendarv-model:value="formatDate":date-format="panelDateFormat":week-format="panelWeekFormat":month-format="panelMonthFormat"@panelChange="onPanelChange"/></Flex><h2 class="mt30 mb10">通知事项日历</h2><Calendar v-model:value="noticeDate" @panelChange="onPanelChange"><template #dateValue="{ dateObject, timestamp }"><span v-if="[8, 12, 16].includes(dateObject.date)">{{ dateObject.date }}日</span></template><template #dateContent="{ dateObject, timestamp }"><template v-if="[8, 12, 16].includes(dateObject.date)"><Badge status="warning" text="This is warning event." /><Badge status="success" text="This is usual event." /><Badge color="volcano" text="This is volcano event" /></template></template><template #monthValue="{ monthObject, timestamp }"><template v-if="[1, 7].includes(monthObject.month)"><template v-if="monthObject.month === 1">二月</template><template v-if="monthObject.month === 7">八月</template></template></template><template #monthContent="{ monthObject, timestamp }"><template v-if="[1, 7].includes(monthObject.month)"><Badge status="warning" text="This is warning event." /><Badge status="success" text="This is usual event." /><Badge color="volcano" text="This is volcano event" /></template></template></Calendar><h2 class="mt30 mb10">自定义插槽</h2><Calendar v-model:value="slotDate" @panelChange="onPanelChange"><template #week="{ defaultWeek, week, timestamp }">{{ format(timestamp, 'EEE') }}</template><template #dateValue="{ dateObject, timestamp }"> {{ dateObject.date }}日 </template><template #monthValue="{ monthObject, timestamp }">{{ format(timestamp, 'MMMM') }}</template></Calendar><h2 class="mt30 mb10">选择功能</h2><Flex vertical><Alert type="info" :message="`You selected date: ${format(selectDate, 'yyyy-MM-dd')}`" /><Calendar v-model:value="selectDate" @select="onSelect" @panelChange="onPanelChange" /></Flex><h2 class="mt30 mb10">禁用日期</h2><Flex vertical><Space align="center">display:<Radio :options="displayOptions" v-model:value="display" button button-style="solid" /></Space><Space><Flex vertical><Alert type="info" message="禁用指定日期 (以今天为准的前三天和后三天)" /><Calendarv-model:value="disableDate":disabled-date="disabledDate":display="display"@select="onSelect"@panelChange="onPanelChange"/></Flex><Flex vertical><Alert type="info" message="禁用所有周末 (星期六 & 星期日)" /><Calendarv-model:value="disableDate":disabled-date="disabledWeekend":display="display"@select="onSelect"@panelChange="onPanelChange"/></Flex></Space></Flex><h2 class="mt30 mb10">自定义日期格式</h2><Flex vertical><Alert type="info" :message="`You selected date: ${dateStr}`" /><Calendar v-model:value="dateStr" value-format="yyyy-MM-dd" @panelChange="onPanelChange" /></Flex><Message ref="message" /></div>
</template>
<style lang="less" scoped>
.m-badge {width: 100%;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;
}
</style>