湘潭网站建设 搜索磐石网络,本网站正在建设中,电子商务网站包括,wordpress静态分离背景
在使用element-plus开发项目过程中#xff0c;需要填入人员的生卒日期#xff0c;经观察#xff0c;对于大部分人来说#xff0c;这类日期通常是农历日期#xff0c;然而我们在系统建设过程中#xff0c;对于日期字段#xff0c;约定成俗的都会使用公历日期#…背景
在使用element-plus开发项目过程中需要填入人员的生卒日期经观察对于大部分人来说这类日期通常是农历日期然而我们在系统建设过程中对于日期字段约定成俗的都会使用公历日期这就存在一个问题用户只记得自己的农历日期那么在录入生卒日期的时候往往就需要通过其他工具查找到农历对应的公历日期才能正确的录入系统中并且录入系统后只能看到公历日期不能直观的将农历日期反馈到用户所以可能日期录入错误也不能迅速的发现并修正于是从实际需求出发对element-plus组件库中的DatePicker组件进行自定义在弹窗选择日期面板中引入农历日期的显示方便用户操作减少错误发生。
组件设计
通过对element-plus组件库官方文档DatePicker 日期选择器 | Element Plus (element-plus.org)的查阅DatePicker组件提供了一个默认的插槽用于支持对弹出框内容的自定义因此我们需要借助此插槽来添加农历日期的显示。
根据日常使用惯例大部分的日历工具都是上面显示公历日期下面显示对应的农历日期如果日期是传统节日或者节气的还会显示对应的节日或节气名称因此我们需要在自定义组件中增加属性showFestival用于控制是否显示节日、showJieQi用于控制是否显示节气如果都不显示那么全都统一显示为农历日期天数。
我们知道农历日期和公历日期是存在差异的差异大的时候可能会相差一个月以上然而日期选择组件的弹窗面板空间有限因此我们需要将农历的月份融入日期中也就是每个月的第一天显示当前农历月份对于农历日期用户往往还会注重当前年份的天干与地支他们可以根据天干地支来进一步核实是否为当前年份因此我们还需要增加一个属性showLunarTip用于控制显示当前日期的完整农历日期如二〇二四年二月廿五 【甲辰(龙)年】这样用户可以直观的看出当前日期正不正确当然出于对用户体验的改善我们希望自定义组件更加人性化比如有时希望鼠标悬停到对应日期上就马上弹出tip显示完整的农历日期信息有时候我希望鼠标悬停1秒以上才显示农历日期减少对日期选择的干扰因此我们再增加一个属性lunarTipShowAfter用于控制完整农历日期的弹出触发时常。
最终效果 工具选择
毋庸置疑要显示公历对应的具体农历日期肯定会存在日期间的换算农历相对公历来说规律性比较复杂要完全自己实现公历转对应的农历工作量较大因此我们优先选择三方工具来完成两种历法的换算。
通过对几个工具库的对比我最终选择了lunar (6tail.cn)工具库它提供了丰富的接口满足绝大部分场景下的使用需求工具的强大性请看官方文档介绍。
代码实现
因为项目使用vue3typescript开发因此自定义组件也是在此环境下完成。我们需要的是对原组件DatePicker的增强封装因此我们的自定义组件需要保留绝大部分原组件的功能。
下面直接贴出自定义组件的实现代码
templateel-date-picker v-modeldateValue v-bind$propstemplate #defaultdateCellel-tooltip:disabled!showLunarTip:show-afterlunarTipShowAfter:contentgetLunarDateStr(dateCell.date)placementbottomdiv :classgetDateClass(dateCell)span classsolar-text{{ dateCell.date.getDate() }}/spanspan classlunar-tex{{ getLunarDay(dateCell.date) }}/span/div/el-tooltip/template/el-date-picker
/templatescript setup langts
import { JieQi, Solar } from lunar-typescript
import { propTypes } from /utils/propTypes
import { isEmpty } from /utils/is
import { datePickerProps } from element-plus
import type { DateCell } from element-plus/es/components/date-picker/src/date-picker.type
// 带农历日期显示的选择组件
defineOptions({ name: LunarDatePicker })const emit defineEmits([update:modelValue])const props defineProps({...datePickerProps,showFestival: propTypes.bool.def(true), // 是否显示节日showJieQi: propTypes.bool.def(true), // 是否显示节气showLunarTip: propTypes.bool.def(true), // 是否使用 tooltip 显示农历日期lunarTipShowAfter: propTypes.number.def(0) // 在触发后多久使用 tooltip 显示农历日期单位毫秒
})const dateValue: Reftypeof props.modelValue reftypeof props.modelValue()watch(() props.modelValue,(val: typeof props.modelValue) {dateValue.value val},{immediate: true}
)watch(() dateValue.value,(val) {emit(update:modelValue, val)}
)/*** 获取当前日期显示样式* param dateCell 单元格日期信息*/
const getDateClass (dateCell: DateCell) {let cla date-wrapperif (dateCell.type today) {cla today}if (dateCell.isCurrent || dateCell.isSelected || dateCell.start || dateCell.end) {cla active} else if (dateCell.inRange) {cla in-range}if (dateCell.disabled) {cla disabled-date}return cla
}/*** 获取农历 day 显示文字*/
const getLunarDay (date) {const solarDate Solar.fromDate(date)const lunarDate solarDate.getLunar()// 每月第一天显示月数if (lunarDate.getDay() 1) {return lunarDate.getMonthInChinese() 月}// 显示节日if (props.showFestival) {const festivals lunarDate.getFestivals()if (!isEmpty(festivals)) {return festivals[0]}}// 显示节气if (props.showJieQi) {const currJieQi: JieQi lunarDate.getCurrentJieQi() as JieQiif (currJieQi currJieQi?.getName()) {return currJieQi?.getName()}}return lunarDate.getDayInChinese()
}/*** 根据日历获取农历日期包含年份干支和生肖*/
const getLunarDateStr (date: Date): string {const solarDate Solar.fromDate(date)const lunarDate solarDate.getLunar()return ${lunarDate.getYearInChinese()}年${lunarDate.getMonthInChinese()}月${lunarDate.getDayInChinese()} 【${lunarDate.getYearInGanZhi()}(${lunarDate.getYearShengXiao()})年】
}
/scriptstyle langscss scoped
.date-wrapper {position: relative;display: flex;align-items: center;flex-direction: column;padding: 4px 0;line-height: 18px;text-align: center;.solar-text {font-size: 14px;}.lunar-text {white-space: nowrap;}
}.today {font-weight: 700;color: var(--el-color-primary);
}.active {color: #fff;background-color: var(--el-datepicker-active-color);border-radius: 5px;
}.in-range {background-color: var(--el-datepicker-inrange-bg-color);
}.disabled-date {cursor: not-allowed;
}
/style
相关代码
引入历法换算工具
npm i lunar-typescriptpropTypes 工具代码
import { VueTypeValidableDef, VueTypesInterface, createTypes, toValidableType } from vue-types
import { CSSProperties } from vuetype PropTypes VueTypesInterface {readonly style: VueTypeValidableDefCSSProperties
}
const newPropTypes createTypes({func: undefined,bool: undefined,string: undefined,number: undefined,object: undefined,integer: undefined
}) as PropTypesclass propTypes extends newPropTypes {static get style() {return toValidableType(style, {type: [String, Object]})}
}export { propTypes }is 工具代码
// copy to vben-adminconst toString Object.prototype.toStringexport const is (val: unknown, type: string) {return toString.call(val) [object ${type}]
}export const isDef T unknown(val?: T): val is T {return typeof val ! undefined
}export const isUnDef T unknown(val?: T): val is T {return !isDef(val)
}export const isObject (val: any): val is Recordany, any {return val ! null is(val, Object)
}export const isEmpty T unknown(val: T): val is T {if (val null) {return true}if (isArray(val) || isString(val)) {return val.length 0}if (val instanceof Map || val instanceof Set) {return val.size 0}if (isObject(val)) {return Object.keys(val).length 0}return false
}export const isDate (val: unknown): val is Date {return is(val, Date)
}export const isNull (val: unknown): val is null {return val null
}export const isNullAndUnDef (val: unknown): val is null | undefined {return isUnDef(val) isNull(val)
}export const isNullOrUnDef (val: unknown): val is null | undefined {return isUnDef(val) || isNull(val)
}export const isNumber (val: unknown): val is number {return is(val, Number)
}export const isPromise T any(val: unknown): val is PromiseT {return is(val, Promise) isObject(val) isFunction(val.then) isFunction(val.catch)
}export const isString (val: unknown): val is string {return is(val, String)
}export const isFunction (val: unknown): val is Function {return typeof val function
}export const isBoolean (val: unknown): val is boolean {return is(val, Boolean)
}export const isRegExp (val: unknown): val is RegExp {return is(val, RegExp)
}export const isArray (val: any): val is Arrayany {return val Array.isArray(val)
}export const isWindow (val: any): val is Window {return typeof window ! undefined is(val, Window)
}export const isElement (val: unknown): val is Element {return isObject(val) !!val.tagName
}export const isMap (val: unknown): val is Mapany, any {return is(val, Map)
}export const isServer typeof window undefinedexport const isClient !isServerexport const isUrl (path: string): boolean {const reg /(((^https?:(?:\/\/)?)(?:[-:\\$,\w])?[A-Za-z0-9.-](?::\d)?|(?:www.|[-:\\$,\w])[A-Za-z0-9.-])((?:\/[\~%\/.\w-_]*)?\??(?:[-\%.\w_]*)#?(?:[\w]*))?)$/return reg.test(path)
}export const isDark (): boolean {return window.matchMedia((prefers-color-scheme: dark)).matches
}// 是否是图片链接
export const isImgPath (path: string): boolean {return /(https?:\/\/|data:image\/).*?\.(png|jpg|jpeg|gif|svg|webp|ico)/gi.test(path)
}export const isEmptyVal (val: any): boolean {return val || val null || val undefined
}
相关组件库版本
组件版本vue^3.3.7element-plus2.4.1lunar-typescript^1.7.5typescript5.2.2vue-types^5.1.1