Element-ui中的datepicker填坑记录
1. 业务背景
中后台项目中经常会使用到各种日期组件,这次业务需求涉及到的是日期组件和Echarts组合在一起,Echarts根据日期维度来画图,如下所示
element-ui库本身有提供日期范围选择和月份范围选择,但是对于周范围选择和年范围选择只能满足于单个日期面板选择也就是type=week或者type=year
2. 框架库的局限性与ui库的调研
项目本身是基于vue2+element-ui+js进行开发的,考虑到工作排期本来打算用现有轮子,网上查到的满足UI库比如很典型的就是antd(基于react)、antdv(基于vue3)
- 日期选择框 DatePicker - Ant Design (antgroup.com)
- DatePicker - Ant Design Vue (antdv.com)
- DateFullRange - HeyUI: A high quality UI Toolkit based on Vue.js
- Introduction | V-Calendar (vcalendar.io)
基于现有比较成熟的UI库要么是成熟架构的最新版本(vue3、react18)、或者是用ts所编写的、暂时还不考虑引入方式、打包体积等,使用的成本比较大,所以还是没办法对element-ui源码动了手
3. Element-ui-datepicker二次改造
-
将element-ui中Packages中datepicker中的包拷贝一份放到本地,如下所示
拷贝到本地以后记得要改组件名称
-
增加周范围日期面板(type=weekrange)与年范围日期面板(type=yearrange),从业务场景思考,周范围日期面板跟日期范围面板是一致的,年范围日期面板跟月范围日期面板也是类似的,所以我们就直接分别拷贝一份date-range(custom-date-picker/panel/date-range.js)与month-range(custom-date-picker/panel/month-range.js),拷贝完后,在date-picker.js(custom-date-picker/picker/date-pick.js)里面要根据不同的type来挂载panel面板,如下所示
-
周日期面板增加周数
/** * 代码路径:custom-date-picker/panel/week-range.vue * 传入showWeekNumber为true显示周数 */ <week-range-table selection-mode="range" :date="leftDate" :default-value="defaultValue" :min-date="minDate" :max-date="maxDate" :range-state="rangeState" :disabled-date="disabledDate" :cell-class-name="cellClassName" @changerange="handleChangeRange" :first-day-of-week="firstDayOfWeek" :showWeekNumber="true" @pick="handleRangePick"> </week-range-table> /** * 代码路径:custom-date-picker/basic/week-range-table.vue * 传入showWeekNumber为true是为了在日期面板当中反显周数 */ <tr> <th v-if="showWeekNumber">周数</th> <th v-for="(week, key) in WEEKS" :key="key">{{ t('el.datepicker.weeks.' + week) }}</th> </tr> // :class="{ current: isWeekActive(row) }" 控制周数高亮显示 isWeekActive(row) { return row.find(v=>v.start) || row.find(v=>v.end) },
// 高亮样式 .el-date-table.is-week-mode tr{ &.el-date-table__row { @mixin week () { margin-left: 2px; margin-right: 2px; border-top-left-radius: 0; border-bottom-left-radius: 0; } &.current td.week div { font-weight: bold; background: #fff; color: #fff; span { background: #409EFF; border-radius: 2px; } } &:hover td { &.week div { @include week; } &:nth-of-type(2) div { margin-left: 5px; border-top-left-radius: 15px; border-bottom-left-radius: 15px; } } td { &.week { cursor: unset; div { @include week; } } } } }
-
年日期面板基础面板的改造
// 原有格式 <tr> <td class="available" :class="getCellStyle(startYear + 0)"> <a class="cell">{{ startYear }}</a> </td> <td class="available" :class="getCellStyle(startYear + 1)"> <a class="cell">{{ startYear + 1 }}</a> </td> <td class="available" :class="getCellStyle(startYear + 2)"> <a class="cell">{{ startYear + 2 }}</a> </td> <td class="available" :class="getCellStyle(startYear + 3)"> <a class="cell">{{ startYear + 3 }}</a> </td> </tr> <tr> <td class="available" :class="getCellStyle(startYear + 4)"> <a class="cell">{{ startYear + 4 }}</a> </td> <td class="available" :class="getCellStyle(startYear + 5)"> <a class="cell">{{ startYear + 5 }}</a> </td> <td class="available" :class="getCellStyle(startYear + 6)"> <a class="cell">{{ startYear + 6 }}</a> </td> <td class="available" :class="getCellStyle(startYear + 7)"> <a class="cell">{{ startYear + 7 }}</a> </td> </tr> <tr> <td class="available" :class="getCellStyle(startYear + 8)"> <a class="cell">{{ startYear + 8 }}</a> </td> <td class="available" :class="getCellStyle(startYear + 9)"> <a class="cell">{{ startYear + 9 }}</a> </td> <td></td> <td></td> </tr> /** * 代码路径:custom-date-picker/basic/year-table.vue * 根据月份面板改造后 */ <tr v-for="(row, key) in rows" :key="key"> <td :class="getCellStyle(cell)" v-for="(cell, key) in row" :key="key"> <div> <span class="cell">{{ cell.text }}</span> </div> </td> </tr> // rows同月份复制过来的稍作改变 const index = this.startYear + i * 4 + j; // 得到每一个单元格的年份,注意startYear从year-range.vue传入,左右面板起始年范围不同 // 同样的getCellStyle、getYearOfCell、markRange、handleMouseMove、handleYearTableClick都基于月份面板稍加改造 // 假定一个年份面板显示的年份周期为10年例如2020-2029,下一个面板就是2030-2039 leftLabel() { const startYear = this.leftStartYear + ' ' + this.t('el.datepicker.year') const endYear = this.leftStartYear + 9 + ' ' + this.t('el.datepicker.year') return `${startYear}-${endYear}`; }, rightLabel() { const startYear = this.rightStartYear + ' ' + this.t('el.datepicker.year') const endYear = this.rightStartYear + 9 + ' ' + this.t('el.datepicker.year') return `${startYear}-${endYear}`; }, // 左右箭头控制年份面板展示 leftPrevYear() { // this.leftDate = prevYear(this.leftDate); this.leftDate = new Date((Math.floor(this.leftDate.getFullYear() / 10) - 1) *10, 1, 1) this.rightDate = new Date((Math.floor(this.rightDate.getFullYear() / 10)) *10, 1, 1) if (!this.unlinkPanels) { this.rightDate = prevYear(this.rightDate); } }, rightNextYear() { if (!this.unlinkPanels) { this.leftDate = nextYear(this.leftDate); } this.leftDate = new Date((Math.floor(this.leftDate.getFullYear() / 10) + 1) *10, 1, 1) this.rightDate = new Date((Math.floor(this.rightDate.getFullYear() / 10) + 1) *10, 1, 1) // this.rightDate = nextYear(this.rightDate); },
效果图如下(年份样式置灰的问题后续有时间再研究下继续补充)
基于工作中的业务场景简单记录一下,有不足之处,还请大家斧正!
转载自:https://juejin.cn/post/7352075590474973222