主要功能:
1.日期选择器输入日期自动格式化;
2.独立选择起止日期(两个选择器不再绑定同时选择);
3.近一周/一月/一年/今年 等快捷日期选项;
4.根据传入参数设置元素样式(行内/块级元素以及分隔符)
效果图:
环境:Vue2.2+,Element-UI
实现思路:
1.自动格式化
el-date-picker组件本身没有输入事件的绑定,并且只支持通过format属性设置唯一一种输入格式,输入后会通过事件监听进行校验,这个事件在el-date-picker内部的el-input组件上,因此在el-date-picker组件的input事件上写方法进行格式化是没有用的,不过,可以通过获取el-input底层的input元素,绑定blur/keydown/keyup事件监听器,实现触发自定义的格式化方法,然后令事件向上冒泡。
然后通过获取el-date-picker的组件实例,调用关闭日期选择面板的方法,模拟实现正常的选择日期效果。
实现:为日期选择器外层套一层div元素,并设置id=“myDatePicker”,再为日期选择器添加focus事件,绑定@focus=setDateInputEvent('myDatePicker',value),为内部的input元素绑定事件监听器。
html部分:
<template>
<div id="myDatePicker" :class="inline ? 'inline-picker' : 'block-picker'">
<el-date-picker
v-model="value[0]"
type="date"
placeholder="开始日期"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
ref="beginDatePicker"
:picker-options="pickerOptions"
@change="handleChange()"
@focus="setDateInputEvent('myDatePicker',value)"
style="margin-right: 10px;">
</el-date-picker>
<span>{{ this.seperater }}</span>
<el-date-picker
v-model="value[1]"
type="date"
placeholder="结束日期"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
ref="endDatePicker"
:picker-options="pickerOptions"
@change="handleChange()"
@focus="setDateInputEvent('myDatePicker',value)"
style="margin-left: 10px;">
</el-date-picker>
</div>
</template>
js部分:
disposeData: function(str){ // 这个方法可以将在日期选择器中手动输入的内容自动识别并转化为yyyy-MM-dd格式,支持通过正则表达式自定义添加可识别的格式
// yyyyMMdd 格式转化
if (/^\d{8}$/.test(str)) {
// 将 yyyyMMdd 格式转换为 yyyy-MM-dd 格式
return str.replace(/^(\d{4})(\d{2})(\d{2})$/, '$1-$2-$3');
}
// yyyy-MM-dd 格式转化
if (/^\d{4}-\d{2}-\d{2}$/.test(str)) {
// 已经是 yyyy-MM-dd 格式,直接返回
return str;
}
// 如果格式不匹配,抛出错误
throw new Error('Invalid date format');
},
setDateInputEvent: function(e,data){ // 手动输入日期的格式转化器实现
const beginDatePicker = this.$refs.beginDatePicker;
const endDatePicker = this.$refs.endDatePicker;
const _this = this;
const beginData = document.getElementById(e).getElementsByTagName("input")[0];
const endData = document.getElementById(e).getElementsByTagName("input")[1];
beginData.addEventListener("blur", inTapEvent);
beginData.addEventListener("keyup", inEnterEvent);
endData.addEventListener("blur", outTapEvent);
endData.addEventListener("keyup", outEnterEvent);
function inEnterEvent(event){
if(event.key === 'Enter'){
beginData.value = disposeData(beginData.value);
Vue.set(data, 0, beginData.value);
beginDatePicker.hidePicker();//调用组件实例方法关掉选择面板
}
}
function outEnterEvent(event){
if(event.key === 'Enter'){
endData.value = disposeData(endData.value);
Vue.set(data, 1, endData.value);
endDatePicker.hidePicker();//调用组件实例方法关掉选择面板
}
}
function inTapEvent() {
beginData.value = disposeData(beginData.value);
Vue.set(data, 0, beginData.value);
}
function outTapEvent() {
endData.value = disposeData(endData.value);
Vue.set(data, 1, endData.value);
}
return data;
},
2.独立选择日期
这只是个人的需求,不过,若想在日期选择器选择后做出处理,需要避免因为其中某一个选择器内容为空导致的错误,例如:
1.初始化时两个选择器都为空,选择一个后另一个的值仍为空;
2.为日期选择器添加了clearable属性,清除了已经选好的日期导致值为空;
3.change/input等事件处理方法出错导致的空值/错值传入
3.快捷日期选项
由于两个日期选择器其实是相互独立的两个组件(只是值绑定了同一个数组的不同位置),因此需要在选择快捷选项后可以同时对两个选择器绑定的值进行操作。
在el-date-picker的picker-options设置中,若将其放在data return内,this是无法指向vue实例的。
解决方法参考资料:element ui 时间选择器pickeroption里面this的指向问题 - wamm521 - 博客园 (cnblogs.com)
在computed中,提前将vue组件实例绑定到参数_this,就可以实现在pickerOptions内调用组件实例,从而修改数据。
js部分:
computed: {
pickerOptions(){ // 日期选择器的快捷选项,可以手动修改和添加自定义的快捷选项
let _this = this;
return {
shortcuts: [{
text: "最近一周",
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 1000 * 3600 * 24 * 7);
_this.$emit('update:value',[_this.formatDate(start,'yyyy-MM-dd'),_this.formatDate(end,'yyyy-MM-dd')]);
picker.$emit('pick');
}
}, {
text: "最近1月",
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
_this.$emit('update:value',[_this.formatDate(start,'yyyy-MM-dd'),_this.formatDate(end,'yyyy-MM-dd')]);
picker.$emit('pick');
}
}, {
text: "最近3月",
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
_this.$emit('update:value',[_this.formatDate(start,'yyyy-MM-dd'),_this.formatDate(end,'yyyy-MM-dd')]);
picker.$emit('pick');
}
}, {
text: "最近1年",
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 365);
_this.$emit('update:value',[_this.formatDate(start,'yyyy-MM-dd'),_this.formatDate(end,'yyyy-MM-dd')]);
picker.$emit('pick');
}
}, {
text: "今年以来",
onClick(picker) {
const end = new Date();
const start = new Date(end.getFullYear(), 0, 1);
_this.$emit('update:value',[_this.formatDate(start,'yyyy-MM-dd'),_this.formatDate(end,'yyyy-MM-dd')]);
picker.$emit('pick');
}
}]
}
}
},
methods:{
formatDate: function(date, format) {
const pad = (num) => num.toString().padStart(2, '0');
const year = date.getFullYear();
const month = pad(date.getMonth() + 1);
const day = pad(date.getDate());
const hour = pad(date.getHours());
const minute = pad(date.getMinutes());
const second = pad(date.getSeconds());
return format
.replace('yyyy', year)
.replace('MM', month)
.replace('dd', day)
.replace('HH', hour)
.replace('mm', minute)
.replace('ss', second);
},
// 其他方法...
}
4.根据传入参数设置元素样式(行内/块级元素以及分隔符)
在props中绑定需要的参数:
props: {
value: Array, // 日期参数,格式为['beginDate','endDate']
seperater: { // 分隔符
type: String,
default: '~'
},
inline: { // 设置这个组件的样式是否为行内元素
type: String,
default: false
}
// 还可以传入其他参数
// propName1: typeName
// propName2: typeName
},
在style部分设置几个可以切换的样式类:
<style>
.inline-picker{
display: inline-flex;
justify-content: flex-end;
align-items: center;
}
.block-picker{
display: flex;
justify-content: flex-end;
align-items: center;
}
</style>
HTML部分中进行样式的选择:
<div id="myDatePicker" :class="inline ? 'inline-picker' : 'block-picker'">
也可以写一个computed属性来分配样式类
完整Vue代码
github项目仓库(有两种版本的组件,分别支持Vue2.2+版本以及Vue2.3+版本,使用方式有所不同,在文档内有示例):
Atlas1y/DatePicker (github.com)
如有错误,欢迎指出。