UNPKG

magiccube-vue3

Version:

vue3-js版组件库

248 lines (217 loc) 8.25 kB
import { computed } from 'vue' import * as utils from '../../utils/common' // 列表中周期表头 const WEEK_TITLE = [ { key: 'week', string: '日', value: 0 }, { key: 'week', string: '一', value: 1 }, { key: 'week', string: '二', value: 2 }, { key: 'week', string: '三', value: 3 }, { key: 'week', string: '四', value: 4 }, { key: 'week', string: '五', value: 5 }, { key: 'week', string: '六', value: 6 }, ] const DAY_TIME = 86400000 /** * 日历控件 核心模块 * 日历列表 * 根据传入的日期进行日历渲染 * 数据格式为Array (单选、多选、range都是用数组表示) * 点击后将选中的数据emit */ const Calendar = { name: 'McCalendar', props: { modelValue: Array, /** 支持两种类型 default和range,其中default包含单选和多选 */ type: String, /** * 日历列表视图显示的月份 * 如果不传 则为当前月 */ viewDate: String, multi: Boolean, /** 设置可选择的日历范围 可单独设置 */ enableStart: String, enableEnd: String, }, emits: ['change', 'update:modelValue'], setup(props, { emit }) { const model = computed({ get() { return props.modelValue || [] }, set(value) { emit('update:modelValue', value) } }) // 获取日历列表中 该月的 “天”元素的属性 const getAllDate = (date = '', selected = [], enStart, enEnd) => { const { year, month } = getDate(date) const { start, end, size } = getDaysInMonth(year, month) const fmtSelected = selected.map(n => utils.dateFormat(n, 'YYYY/MM/DD')) const today = utils.dateFormat(new Date(), 'YYYY/MM/DD') const arr = [] /** * 设置可选择日期范围 */ const _s = enStart ? new Date(enStart).getTime() : 0 const _e = enEnd ? new Date(enEnd).getTime() : 99999999999999999999n /** * 获取 range 状态下 所包含的日期状态 */ const selected_1 = fmtSelected[0] ? new Date(fmtSelected[0]).getTime() : '' const selected_2 = fmtSelected[1] ? new Date(fmtSelected[1]).getTime() : '' for (let i = 0; i < size; i++) { const d = new Date(start + DAY_TIME * i) const _t = d.getTime() const isDisabled = _t < _s || _t > _e const isInclude = getInclude(selected_1, selected_2, _t) arr.push({ isDisabled, isInclude, string: d.getDate(), value: _t, week: d.getDay(), isChecked: getSelectStatus(fmtSelected, d), isRangeStart: getSelectStatus([fmtSelected[0]], d), isRangeEnd: getSelectStatus([fmtSelected[1]], d), isToday: today === utils.dateFormat(d, 'YYYY/MM/DD'), }) } return arr } // 分解 字符串 日期值 -> 日期格式 const getDate = (date = '') => { const _d = date ? new Date(date) : new Date() const year = _d.getFullYear() const month = _d.getMonth() + 1 const day = _d.getDate() const week = _d.getDay() return { year, month, day, week } } // 计算一月中有多少天 const getDaysInMonth = (year, month) => { const _d = new Date(`${year}/${month}/1`) const start = _d.getTime() _d.setMonth(month) const end = _d.getTime() const diff = end - start return { start, end, size: diff / DAY_TIME } } // range模式中 判断是否在选中范围内 const getInclude = (selected_1, selected_2, value) => { if (selected_1 && selected_2) { return value >= selected_1 && value <= selected_2 } else { return false } } // 获取 “天” 的选中状态 const getSelectStatus = (selected, item) => { return selected.some(n => n === utils.dateFormat(item, 'YYYY/MM/DD')) } // 日历开始(上月)补充元素 const getFillLastDate = (days = []) => { if (!days?.length) return [] const first = days[0] const times = first.week const arr = [] for (let i = 1; i <= times; i++) { const d = new Date(first.value - DAY_TIME * i) arr.push({ string: d.getDate(), value: d.getTime(), isDisabled: true }) } return arr.reverse() } // 日历尾部(下月)补充元素 const getFillNextDate = (days = []) => { if (!days?.length) return [] const last = days[days.length - 1] const times = 6 - last.week const arr = [] for (let i = 1; i <= times; i++) { const d = new Date(last.value + DAY_TIME * i) arr.push({ string: d.getDate(), value: d.getTime(), isDisabled: true }) } return arr } // 获取日历列表(全部) const days = computed(() => { /** 本月日期 */ const current = getAllDate(props.viewDate, model.value, props.enableStart, props.enableEnd) // /** 月初填充 */ const fillLast = getFillLastDate(current) // /** 月末填充 */ const fillNext = getFillNextDate(current) return [...WEEK_TITLE, ...fillLast, ...current, ...fillNext] }) const handleClick = (e, item) => { e.stopPropagation() if (item.isDisabled) return let _arr = model.value const str = utils.dateFormat(item.value, 'YYYY/MM/DD') if (props.type === 'range') { switch (_arr.length) { case 2: { _arr = [str] break } case 1: { _arr.push(str) emit('change', _arr) break } default: { _arr.push(str) break } } } else if (props.multi) { const idx = _arr.findIndex(n => n === str) if (idx > -1) { _arr.splice(idx, 1) } else { _arr.push(str) } } else { _arr = [str] emit('change', _arr) } model.value = _arr } return () => ( <div class="mc-date__calendar"> { days.value.map((item) => ( <div class={[ 'mc-date__calendar--cell', { active: item.isChecked, today: item.isToday, disabled: item.isDisabled, include: props.type === 'range' && item.isInclude, start: props.type === 'range' && item.isRangeStart, end: props.type === 'range' && item.isRangeEnd, } ]} onClick={(e) => handleClick(e, item)}> <span> {item.string} </span> </div> )) } </div> ) } } export { Calendar, Calendar as default }