赞
踩
在做后台系统的项目中,日期组件用的是比较频繁的,但是一些常用的组件库没那么全。特别使用选择日期范围组件就格外明显。
根据 element-ui(vue2)
进行完善、改造,功能有:
把element
中的date-picker
源码复制一份node_modules\element-ui\packages\date-picker
,然后在main.js
中使用
import DatePicker from './components/date-picker';
Vue.component('DatePicker', DatePicker);
<!--使用 -->
<DatePicker
v-model="value"
type="week"
format="yyyy 第 WW 周"
placeholder="选择周">
</DatePicker>
这个时候会报错,代码无法解析jsx
语法错误。原因是这个源码中包含时间选择器,这个组件中引入滚动条ElScrollbar
所以会报错,这里只需要日期选择器,所以注释掉这几行代码不会影响功能,可以自行处理这个问题。
注释掉 src/components/date-picker/src/basic/time-spinner.vue line105 和 line109
// import ElScrollbar from 'element-ui/packages/scrollbar';
// components: { ElScrollbar },
注释掉 src/components/date-picker/src/panel/time-select.vue line22 和 line77
// import ElScrollbar from 'element-ui/packages/scrollbar';
// components: { ElScrollbar },
如果有eslint
代码检查,需要把src/components/date-picker
加入到忽略文件中不然会报出很多代码格式的错误和警告 。
在src/components/date-picker/src/basic/date-table.vue
中props
中接收一个参数showWeekNumber
这个就是用来显示周数,只是父组件没有传入。
修改src\components\date-picker\src\panel\date.vue
showWeekNumber
绑定到DateTable
组件中<date-table
v-show="currentView === 'date'"
@pick="handleDatePick"
:selection-mode="selectionMode"
:first-day-of-week="firstDayOfWeek"
:showWeekNumber="showWeekNumber"
:value="value"
:default-value="defaultValue ? new Date(defaultValue) : null"
:date="date"
:cell-class-name="cellClassName"
:disabled-date="disabledDate">
</date-table>
showWeekNumber
值跟随模式变化代码路径 watch > selectionMode
selectionMode(newVal) {
if (newVal === 'month') {
//省略
} else if(newVal === 'week'){
this.showWeekNumber = true
}
}
修改src\components\date-picker\src\basic\date-table.vue
可以在
main.js
中引用import locale from 'element-ui/lib/locale/lang/zh-CN'
重新改写里面的内容即可
<!-- 第11行 -->
<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[showWeekNumber ? 2 : 1]) }"
使用周选择器时,选择后的日期默认是星期一,在西方国家周日则为星期一,所以想要调整可以是使用官方提供的方法
:picker-options="{firstDayOfWeek:1}"
即可。
if (this.showWeekNumber) {
row[0] = { type: 'week', text: getWeekNumber(nextDate(startDate, i * 7 + 1)) };
}
<style lang="scss" scoped> .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; } } } } } </style>
value-format
描述: 周选择器设置value-format
后value格式是正确了,但是控制台报错,组件不回显。
原因: 从报错信息可以看出来,都是显示获取操作时间函数,因为现在的值是字符串当然没有这些方法,官方没有对周模式进行兼容。
修改src\components\date-picker\src\picker.vue
经过问题排查发现,在这个文件中有一个计算属性parsedValue
用于处理value
值,所以从写此方法。549行。
parsedValue() { const yearStartIndex = this.valueFormat ? this.valueFormat.indexOf('yyyy') : -1; const weekStartIndex = this.valueFormat ? this.valueFormat.indexOf('WW') : -1; const weekStartIndex2 = this.valueFormat ? this.valueFormat.indexOf('W') : -1; if (this.value && this.type === 'week' && yearStartIndex > -1 && (weekStartIndex > -1 || weekStartIndex2 > -1)) { const year = parseInt(this.value.substring(yearStartIndex, yearStartIndex + 4)) const week = parseInt(this.value.substring((weekStartIndex > -1 ? weekStartIndex : weekStartIndex2)).replace(/(\d{1,2}).*/g, '$1')) const firstWeekDayOfYear = new Date(year, 0, 1).getDay() let firstThursday = null if (firstWeekDayOfYear <= 4) { firstThursday = new Date(year, 0, 1 + (4 - firstWeekDayOfYear)) } else { firstThursday = new Date(year, 0, 1 + (11 - firstWeekDayOfYear)) } const weekOfThursday = new Date(firstThursday.getTime() + (1000 * 60 * 60 * 24 * 7 * (week - 1))) return weekOfThursday }else{ if (!this.value) return this.value; // component value is not set if (this.type === 'time-select') return this.value; // time-select does not require parsing, this might change in next major version const valueIsDateObject = isDateObject(this.value) || (Array.isArray(this.value) && this.value.every(isDateObject)); if (valueIsDateObject) { return this.value; } if (this.valueFormat) { return parseAsFormatAndType(this.value, this.valueFormat, this.type, this.rangeSeparator) || this.value; } // NOTE: deal with common but incorrect usage, should remove in next major version // user might provide string / timestamp without value-format, coerce them into date (or array of date) return Array.isArray(this.value) ? this.value.map(val => new Date(val)) : new Date(this.value); } }
重写后支持value-format
属性
思路: 通过这个src\components\date-picker\src\picker\date-picker.js
文件可以发现element
做了几个模式然后通过不同的type获取不同的日期面板,所以可以把日模式日期选择改造一下作为一个新模式。
为了不影响现有功能,把代码复制一份进行改造。
weekrange
模式src\components\date-picker\src\panel\date-range.vue
命名为week-range.vue
src\components\date-picker\src\picker\date-picker.js
中新增weekrange
模式import WeekRange from '../panel/week-range'
const getPanel = function(type) {
if (type === 'daterange' || type === 'datetimerange') {
return DateRangePanel;
} else if (type === 'monthrange') {
return MonthRangePanel;
}else if(type === 'weekrange'){
return WeekRange
}
return DatePanel;
};
src\components\date-picker\src\picker.vue
const DEFAULT_FORMATS = {
weekrange: 'yyyywWW'
};
const HAVE_TRIGGER_TYPES = [
'weekrange'
];
const TYPE_VALUE_RESOLVER_MAP = { weekrange: { formatter(value, format) { if (Array.isArray(value) && value.length === 2) { return [WEEK_FORMATTER(value[0], format), WEEK_FORMATTER(value[1], format)]; } return ''; }, parser(value, format) { if(Array.isArray(value) && value.length === 2){ return [TYPE_VALUE_RESOLVER_MAP.date.parser(value[0], format),TYPE_VALUE_RESOLVER_MAP.date.parser(value[1], format)]; } return [] }, }, } const WEEK_FORMATTER = function (value, format) { let week = getWeekNumber(value); let month = value.getMonth(); const trueDate = new Date(value); if (week === 1 && month === 11) { trueDate.setHours(0, 0, 0, 0); trueDate.setDate(trueDate.getDate() + 3 - ((trueDate.getDay() + 6) % 7)); } let date = formatDate(trueDate, format); date = /WW/.test(date) ? date.replace(/WW/, week < 10 ? '0' + week : week) : date.replace(/W/, week); return date; };
在src\components\date-picker\src\picker.vue
这个文件中,最外层有个样式是通过type
生成的,所以生成的el-date-editor--weekrange
没有这个样式,所以文本框就很短。
<style lang="scss" scoped>
.el-date-editor--weekrange.el-input__inner{
width: 350px;
}
</style>
修改:src\components\date-picker\src\panel\week-range.vue
在date-table
中添加showWeekNumber=true
,记得两个都要添加
value-format
根据上文的处理方法,现在是数组数据处理。
//提取处理方法
parseWeekValue(year,week){
const firstWeekDayOfYear = new Date(year, 0, 1).getDay()
let firstThursday = null
if (firstWeekDayOfYear <= 4) {
firstThursday = new Date(year, 0, 1 + (4 - firstWeekDayOfYear))
} else {
firstThursday = new Date(year, 0, 1 + (11 - firstWeekDayOfYear))
}
return new Date(firstThursday.getTime() + (1000 * 60 * 60 * 24 * 7 * (week - 1)))
}
//改造计算属性中的parsedValue方法 parsedValue() { const yearStartIndex = this.valueFormat ? this.valueFormat.indexOf('yyyy') : -1; const weekStartIndex = this.valueFormat ? this.valueFormat.indexOf('WW') : -1; const weekStartIndex2 = this.valueFormat ? this.valueFormat.indexOf('W') : -1; if (this.value && this.type === 'week' && yearStartIndex > -1 && (weekStartIndex > -1 || weekStartIndex2 > -1)) { const year = parseInt(this.value.substring(yearStartIndex, yearStartIndex + 4)) const week = parseInt(this.value.substring((weekStartIndex > -1 ? weekStartIndex : weekStartIndex2)).replace(/(\d{1,2}).*/g, '$1')) return this.parseWeekValue(year,week) }else if(Array.isArray(this.value) && this.type === 'weekrange' && yearStartIndex > -1 && (weekStartIndex > -1 || weekStartIndex2 > -1)){ return this.value.map(date=>{ const year = parseInt(date.substring(yearStartIndex, yearStartIndex + 4)) const week = parseInt(date.substring((weekStartIndex > -1 ? weekStartIndex : weekStartIndex2)).replace(/(\d{1,2}).*/g, '$1')) return this.parseWeekValue(year,week) }) }else{ if (!this.value) return this.value; // component value is not set if (this.type === 'time-select') return this.value; // time-select does not require parsing, this might change in next major version const valueIsDateObject = isDateObject(this.value) || (Array.isArray(this.value) && this.value.every(isDateObject)); if (valueIsDateObject) { return this.value; } if (this.valueFormat) { return parseAsFormatAndType(this.value, this.valueFormat, this.type, this.rangeSeparator) || this.value; } // NOTE: deal with common but incorrect usage, should remove in next major version // user might provide string / timestamp without value-format, coerce them into date (or array of date) return Array.isArray(this.value) ? this.value.map(val => new Date(val)) : new Date(this.value); } },
思路:在单个周选择器中发现
el-date-table
这个层级上启用is-week-mode
,在el-date-table__row
样式行中加上current
这个类名就可以高亮显示
复制src\components\date-picker\src\basic\date-table.vue
命名为week-range-table.vue
在week-range.vue
中引用这个组件
is-week-mode
src\components\date-picker\src\basic\week-range-table.vue
第一行,以为这是复制出的一份,不做处理直接写死
<table cellspacing="0" cellpadding="0" class="el-date-table is-week-mode" @click="handleClick" @mousemove="handleMouseMove"></table>
current
这个样式由isWeekActive
这个方法控制,所以需要重写逻辑。很简单,传入行,判断当前行有没有start或者end。
:class="{ current: isWeekActive(row) }"
isWeekActive(row) {
return row.find(v=>v.start) || row.find(v=>v.end)
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。