UNPKG

@d.blizen/datepicker-nash-travel

Version:

A vue component of datepicker, includes DatePicker, DatetimePicker, DateRangePicker, TimePicker

530 lines (454 loc) 14 kB
import { compareDates, DateInfoBase, DateInfoBase1, DateStr, fillTo, getMonthByStep, getMonthLen, parseDate, parseTime, TimeInfo, TimeStr, } from '@livelybone/date-generator' export const dateReg = /^((\d{4})-?(\d{1,2})?-?(\d{1,2})?)/ export const timeReg = /^((\d{1,2}):?(\d{1,2})?:?(\d{1,2})?)/ export const AllTypes = ['year', 'month', 'date', 'time'] as const export const AllTimeTypes = ['hour', 'minute', 'second'] as const export type TAllTypes = typeof AllTypes extends Readonly<(infer A)[]> ? A : never export type TAllTimeTypes = typeof AllTimeTypes extends Readonly<(infer A)[]> ? A : never export function sliceUtilEqual<T extends any>(arr: T[], val: T) { const index = Object.keys(arr).find(i => arr[+i] === val) if (!index) return arr return arr.slice(0, +index + 1) } // flag: // -1 - 如果 d1 小于等于 d2,返回 true // 1 - 如果 d1 大于等于 d2,返回 true // 0 - 如果 d1 等于 d2,返回 true export function dateCompare< T extends Partial<DateInfoBase1>, U extends Partial<DateInfoBase1> >( date: T | DateStr | undefined | null, targetDate: U | DateStr | undefined | null, flag = 1, type: Exclude<TAllTypes, 'time'> = 'date', ) { const $date = typeof date === 'string' ? parseDate(date) : date ? { ...date } : undefined const $targetDate = typeof targetDate === 'string' ? parseDate(targetDate) : targetDate ? { ...targetDate } : undefined if (!$date) return false if (!$targetDate) return true if (type === 'year') { $date.month = '01' $date.date = '01' $targetDate.month = '01' $targetDate.date = '01' } else if (type === 'month') { $date.date = '01' $targetDate.date = '01' } const compare = compareDates($date as any, $targetDate as any) return flag === 0 ? compare === 0 : compare * flag >= 0 } // flag: // -1 - 如果 t1 小于等于 t2,返回 true // 1 - 如果 t1 大于等于 t2,返回 true // 0 - 如果 t1 等于 t2,返回 true export function timeCompare( time?: TimeInfo | string, targetTime?: TimeInfo | string, flag = 1, type: TAllTimeTypes = 'second', ) { const $time = typeof time === 'string' ? parseTime(time) : time ? { ...time } : undefined const $targetTime = typeof targetTime === 'string' ? parseTime(targetTime) : targetTime ? { ...targetTime } : undefined if (!$time) return false if (!$targetTime) return true if (type === 'hour') { $time.minute = '00' $time.second = '00' $targetTime.minute = '00' $targetTime.second = '00' } else if (type === 'minute') { $time.second = '00' $targetTime.second = '00' } const get = (t: TimeInfo) => +`${fillTo(2, t.hour)}${fillTo(2, t.minute)}${fillTo(2, t.second)}` const compare = get($time) - get($targetTime) return flag === 0 ? compare === 0 : compare * flag >= 0 } export function datetimeCompare( datetime: (DateInfoBase & Partial<TimeInfo>) | undefined, targetDatetime: (DateInfoBase & TimeInfo) | undefined, flag = 1, type: TAllTimeTypes = 'second', ) { if (!datetime) return false if (!targetDatetime) return true if (flag === 0) return ( dateCompare(datetime, targetDatetime, flag) && (datetime.hour === undefined || timeCompare(datetime as TimeInfo, targetDatetime, flag)) ) const compare = dateCompare(datetime, targetDatetime, flag) if (!compare) return false const compareReverse = dateCompare(datetime, targetDatetime, -flag) if (compareReverse) return ( datetime.hour === undefined || timeCompare(datetime as TimeInfo, targetDatetime, flag, type) ) return true } function $fillTo(width: number, num?: string | number) { return num !== undefined ? fillTo(width, num) : '' } export function getTenYears(val: DateInfoBase) { if (!val) return '' const tenYear = Math.floor(+val.year / 10 - 0.5) return `${tenYear * 10 + 1} - ${(tenYear + 1) * 10}` } export function createEmptyTimeObj() { return { year: '', month: '', date: '', hour: '', minute: '', second: '' } } export function createNowTimeObj() { const now = new Date() return { year: now.getFullYear(), month: now.getMonth() + 1, date: now.getDate(), hour: now.getHours(), minute: now.getMinutes(), second: now.getSeconds(), } } export function formatDateObj(val: DateInfoBase1) { return { year: (val.year && fillTo(4, val.year)) || '', month: (val.month && fillTo(2, val.month)) || '', date: (val.date && fillTo(2, val.date)) || '', } } export function formatTimeObj( val: { [key in keyof TimeInfo]: number | string }, ) { return { hour: (val.hour && fillTo(2, val.hour)) || '', minute: (val.minute && fillTo(2, val.minute)) || '', second: (val.second && fillTo(2, val.second)) || '', } } export function formatDate(d: DateInfoBase, type: TAllTypes = 'date') { if (!d) return '' const arr = [] const year = d.year && $fillTo(4, d.year) if (year) { arr.push(year) const month = d.month && $fillTo(2, d.month) if (month) { arr.push(month) const date = d.date && $fillTo(2, d.date) if (date) arr.push(date) } } return arr.slice(0, sliceUtilEqual(AllTypes as any, type).length).join('-') } export function formatTime(t: TimeInfo, type: TAllTimeTypes = 'second') { if (!t) return '' const arr = [] const hour = t.hour && $fillTo(2, t.hour) if (hour) { arr.push(hour) const minute = t.minute && $fillTo(2, t.minute) if (minute) { arr.push(minute) const second = t.second && $fillTo(2, t.second) if (second) arr.push(second) } } return arr .slice(0, sliceUtilEqual(AllTimeTypes as any, type).length) .join(':') } function mergePropExceptEmpty(o1: any, o2: any) { return Object.keys({ ...o1, ...o2 }).reduce( (pre, k) => ({ ...pre, [k]: (o2 && o2[k]) || (o1 && o1[k]) }), o1, ) } export function dealDateLimit( min: DateStr | undefined, max: DateStr | undefined, ) { const error: string[] = [] const minDate = (min && parseDate(min)) || undefined if (min && !minDate) error.push(`Prop min(${min}) is invalid`) const maxDate = (max && parseDate(max)) || undefined if (max && !maxDate) error.push(`Prop max(${max}) is invalid`) return { minDate: formatDateObj( mergePropExceptEmpty({ year: '0000', month: '01', date: '01' }, minDate), ), maxDate: formatDateObj( mergePropExceptEmpty({ year: '9999', month: '12', date: '31' }, maxDate), ), error: error.join(';'), } } export function dealTimeLimit( min: TimeStr | undefined, max: TimeStr | undefined, ) { const error: string[] = [] const minTime = (min && parseTime(min)) || undefined if (min && !minTime) error.push(`Prop min(${min}) is invalid`) const maxTime = (max && parseTime(max)) || undefined if (max && !maxTime) error.push(`Prop max(${max}) is invalid`) return { minTime: formatTimeObj( mergePropExceptEmpty({ hour: '00', minute: '00', second: '00' }, minTime), ), maxTime: formatTimeObj( mergePropExceptEmpty({ hour: '23', minute: '59', second: '59' }, maxTime), ), error: error.join(';'), } } export function dealDatetimeLimit( min: string | undefined, max: string | undefined, selectedDate?: DateInfoBase, ) { const minArr = (min || '').trim().split(/\s+/) const maxArr = (max || '').trim().split(/\s+/) const { minDate, maxDate } = dealDateLimit(minArr[0], maxArr[0]) const compare = { toMin: minDate && selectedDate ? compareDates(selectedDate, minDate) : 1, toMax: maxDate && selectedDate ? compareDates(selectedDate, maxDate) : -1, } const { minTime: $minTime, maxTime: $maxTime } = dealTimeLimit( minArr[1], maxArr[1], ) const error: string[] = [] const minDatetime = minDate ? { ...minDate, ...$minTime } : undefined if (min && !minDatetime) error.push(`Prop min(${min}) is invalid`) const maxDatetime = maxDate ? { ...maxDate, ...$maxTime } : undefined if (max && !maxDatetime) error.push(`Prop min(${max}) is invalid`) const minTime = (() => { if (compare.toMin === 0) return $minTime if (compare.toMin < 0) return { hour: '23', minute: '59', second: '59' } return { hour: '00', minute: '00', second: '00' } })() const maxTime = (() => { if (compare.toMax === 0) return $maxTime if (compare.toMax > 0) return { hour: '00', minute: '00', second: '00' } return { hour: '23', minute: '59', second: '59' } })() return { minArr, maxArr, minDate, maxDate, minTime, maxTime, minDatetime, maxDatetime, error: error.join(';'), } } export function getNextMonthFirstDate( dateInfo?: DateInfoBase1, ): typeof dateInfo extends undefined ? undefined : DateInfoBase { if (!dateInfo) return undefined as any const month = getMonthByStep(dateInfo, 1) return { ...month, date: fillTo(2, 1) } } export function getPrevMonthLastDate( dateInfo?: DateInfoBase1, ): typeof dateInfo extends undefined ? undefined : DateInfoBase { if (!dateInfo) return undefined as any const month = getMonthByStep(dateInfo, -1) return { ...month, date: fillTo(2, getMonthLen(month.year, month.month)) } } export function getCurrMonthLastDate( dateInfo?: DateInfoBase1, ): typeof dateInfo extends undefined ? undefined : DateInfoBase { if (!dateInfo) return undefined as any return formatDateObj({ ...dateInfo, date: fillTo(2, getMonthLen(dateInfo.year, dateInfo.month)), }) } export function getPrevYearLastDate(year: string | number) { if (!year) return undefined return { year: fillTo(4, +year - 1), month: '12', date: '31' } } export function getCurrYearLastDate(year: string | number) { if (!year) return undefined return { year: fillTo(4, +year), month: '12', date: '31' } } export function getPrevTenYearLastDate(year: string | number) { if (!year) return undefined return { year: fillTo(4, (Math.ceil(+year / 10) - 1) * 10), month: '12', date: '31', } } export function getCurrTenYearLastDate(year: string | number) { if (!year) return undefined return { year: fillTo(4, Math.ceil(+year / 10) * 10), month: '12', date: '31', } } export function getNextYearFirstDate(year: string | number) { if (!year) return undefined return { year: fillTo(4, +year + 1), month: '01', date: '01' } } export function getNextTenYearFirstDate(year: string | number) { if (!year) return undefined return { year: fillTo(4, Math.ceil(+year / 10) * 10 + 1), month: '01', date: '01', } } export function getFirstMaxDate( val: DateInfoBase1, type: Exclude<TAllTypes, 'time'> = 'date', ) { if (!val) return undefined if (type === 'year') return getPrevTenYearLastDate(val.year) if (type === 'month') return getPrevYearLastDate(val.year) return getPrevMonthLastDate(val) } export function getLastMinDate( val: DateInfoBase1, type: Exclude<TAllTypes, 'time'> = 'date', ) { if (!val) return undefined if (type === 'year') return getNextTenYearFirstDate(val.year) if (type === 'month') return getNextYearFirstDate(val.year) return getNextMonthFirstDate(val) } export function dateValidator( val?: string | DateInfoBase1, type: Exclude<TAllTypes, 'time'> = 'date', min: any = '', max: any = '', ) { if (!val) return '' const date = typeof val === 'string' ? parseDate(val) : val const checkProps = sliceUtilEqual(AllTypes as any, type) if (!date || !checkProps.every(k => date[k])) return `Value(${val}) is invalid` if ( !( (!min || dateCompare(date, min, 1, type)) && (!max || dateCompare(date, max, -1, type)) ) ) { return `Value${val} is out of range` } return '' } export function timeValidator( val?: string | TimeInfo, type: TAllTimeTypes = 'second', min: any = '', max: any = '', ) { if (!val) return '' const time = typeof val === 'string' ? parseTime(val) : val const checkProps = sliceUtilEqual(AllTimeTypes as any, type) if (!time || !checkProps.every(k => time[k])) return `Value(${val}) is invalid` if ( !( (!min || timeCompare(time, min, 1, type)) && (!max || timeCompare(time, max, -1, type)) ) ) { return `Value${val} is out of range` } return '' } export function parseDatetime(val: string) { const $val = val || '' if (!$val.trim()) return null const arr = $val.trim().split(/\s+/) return { ...parseDate(arr[0]), ...parseTime(arr[1]), } } export function datetimeValidator( val?: any, type: TAllTimeTypes = 'second', min: any = '', max: any = '', ) { if (!val) return '' const time = typeof val === 'string' ? parseDatetime(val) : val const checkProps = AllTypes.slice(0, 3).concat( sliceUtilEqual(AllTimeTypes as any, type) as any[], ) if (!time || !checkProps.every(k => time[k])) return `Value(${val}) is invalid` if ( !( (!min || datetimeCompare(time, min, 1, type)) && (!max || datetimeCompare(time, max, -1, type)) ) ) { return `Value${val} is out of range` } return '' } export function isInSameTenYear(v1: DateInfoBase1, v2: DateInfoBase1) { return dateCompare(v1, v2, 0, 'year') } export function isInSameYear(v1: DateInfoBase1, v2: DateInfoBase1) { return dateCompare(v1, v2, 0, 'year') } export function isInSameMonth(v1: DateInfoBase1, v2: DateInfoBase1) { return dateCompare(v1, v2, 0, 'month') } export function isInSamePage( [v1, v2]: [DateInfoBase1, DateInfoBase1], type?: Exclude<TAllTypes, 'time'>, ) { if (type === 'year') return isInSameTenYear(v1, v2) if (type === 'month') return isInSameYear(v1, v2) return isInSameMonth(v1, v2) } export function formatDatetime(val: any, type?: TAllTimeTypes) { let str = '' const d = formatDate(val) str += d if (!str) return '' const t = formatTime(val, type) return t ? `${str} ${t}` : '' }