背景
背景
:ElementUI
的DatePicker
组件是不能设置时区的,组件显示的时间所使用的时区是打开页面的客户端所在的系统所使用的时区。需求
:我在本地打开页面,使用DatePicker
组件筛选日志,组件中超过今天的时间置灰不可选。这个“今天”
所使用的时区是特定的时区(比如美国纽约)。譬如
:本地(中国)现在是2020-08-03 08:51:29,而美国纽约现在是2020-08-02 20:51:29,若DatePicker
组件设置的时区是America/New_York
,那么组件中则需要置灰3号以及之后的日期了(如图2)。
图1
那么现在来看看我是怎么实现这个需求的吧^_^(就这?)
看我
我不能直接修改node_modules\element-ui
下的DatePicker
组件代码呀,因为若是项目挪到其他环境,运行npm install
,拿到的DatePicker
代码还是它原来的代码,并不是你修改过的代码。
于是,我找到node_modules\element-ui\packages\date-picker
这个文件夹,右键点击date-picker
选择copy
,然后来到项目名\src\components
(这里选择你放组件的目录,不一定跟我的一样),右键点击components
选择paste
,这样就把DatePicker
的组件代码拷贝到我们的组件目录下了。
我们可以在DatePicker
组件的基础上实现一个自己的DatePicker
,这样你就可以想怎么改就怎么改,简直就是为所欲为了哈哈。
对了,涉及到时区数据和时区转换,我用了
moment-timezone
包,所以你需要预先装好这个包:npm install moment-timezone -S
Step1 自定义DatePicker
组件的引入和使用
- 打开
\src\components\date-picker\src\picker\date-picker.js
//省略若干代码...
export default {
mixins: [Picker],
name: 'MyDatePicker', // 这里原来是ElDatePicker,现在修改为MyDatePicker
//省略若干代码...
}
- 打开
\src\main.js
//省略若干代码...
import MyDatePicker from '@/components/date-picker'
Vue.use(MyDatePicker)
- 组件中可以这样用
(test.vue)
<template>
<my-date-pickerv-model="dates"type="daterange":clearable="false"start-placeholder="Begin Date"end-placeholder="End Date":default-time="['00:00:00', '23:59:59']":timezone="getTimeZone()":picker-options="{disabledDate: time => {return time.getTime() > today.getTime()}}"
>
my-date-picker>
template>
Step2 超过“今天”的日期都置灰
我们是通过:picker-options="{disabledDate: time => {return time.getTime() > today.getTime()}}"
这一行代码来实现这个功能的。
关键在于today
这个Date对象,若是time > today
则置灰。比如按照America/New_York
时区,现在应该是2020-08-02 20:51:29
,也就是说只要大于2020-08-02 23:59:59
的时间,都要置灰。于是
<my-date-pickerv-model="dates"type="daterange":clearable="false"start-placeholder="Begin Date"end-placeholder="End Date":default-time="['00:00:00', '23:59:59']":timezone="getTimeZone()":picker-options="{disabledDate: time => {return time.getTime() > today.getTime()}}"
>my-date-picker>template><script>import moment from 'moment-timezone'export default {name: 'MyCompo',
data () {return {today: ''
}
},
mounted () {// this.getTimeZone() 获取你想要使用的时区,这里我获取到的是`America/New_York`let str = moment().tz(this.getTimeZone()).set({'hour': 23, 'minute': 59, 'second': 59}).format('YYYY/MM/DD HH:mm:ss')this.today = new Date(str)
}
}script>
效果如图:
Emmm,置灰没问题了,但是好像还有点不OK。3
被高亮了(“今天”会高亮显示),按照America/New_York
时区,“今天”应该是2号才对。继续……
Step3 “今天”高亮
我们右键点击“3”
选择检查
,看一下dom元素
可以看到,它之所以会高亮,就是因为它使用了today
类。
接下来我们康康代码中在哪里添加的today
类,组件目录下搜索today
:
搜出来有3个文件,分别对应于日面板
、月面板
、年面板
,由于时区只会影响到日
,不会影响到月
和年
,所以我们只需要看date-table.vue
就可以了。
我把关键的几行代码摘出来:
const now = getDateTimestamp(new Date());
const isToday = time === now;
if (isToday) {
cell.type = 'today';
}
if ((cell.type === 'normal' || cell.type === 'today') && !cell.disabled) {
classes.push('available');
if (cell.type === 'today') {
classes.push('today');
}
} else {
classes.push(cell.type);
}
可以看到关键在于now
,若是time === now
,则isToday
为true
,则cell.type
的值为today
,那么classes.push(cell.type)
等价于classes.push('today')
,即添加了today
类。
也就是说,now
要和时区
相关。按照America/New_York
时区,now
应该是2020-08-02
,这样才会高亮2
号,而不是3
号。
注意,date-table.vue
是组件内部代码,所以代码里用到的时区信息应该是在使用组件的时候由timezone
属性传入,这样才能使组件和外部代码的耦合度最低。
OK,那么我们来看下,date-table.vue
文件里的代码要如何拿到这里传进来的
timezone
属性值。
Step4 传递timezone属性值
重点来了,到这步我们不得不稍微读一下源码了。
- 从入口文件开始:
\src\components\date-picker\index.js
import DatePicker from './src/picker/date-picker';
/* istanbul ignore next */
DatePicker.install = function install(Vue) {
Vue.component(DatePicker.name, DatePicker);
};
export default DatePicker;
可以看到入口文件主要就是加载了
DatePicker
模块,为它加了一个安装函数install
之后把它暴露出去。所以重点是DatePicker
模块。
\src\components\date-picker\src\picker\date-picker.js
import Picker from '../picker';
import DatePanel from '../panel/date';
import DateRangePanel from '../panel/date-range';
import MonthRangePanel from '../panel/month-range';
const getPanel = function(type) {...}
export default {
mixins: [Picker],
name: 'MyDatePicker',
props: {...},
watch: {
type(type) {
if (this.picker) {
this.unmountPicker();
this.panel = getPanel(type);
this.mountPicker();
} else {
this.panel = getPanel(type);
}
}
},
created() {
this.panel = getPanel(this.type);
}
}
可以看到这个模块
mixins
了Picker
,我们再接着看Picker
。
\src\components\date-picker\src\picker.vue
date-picker.js
mixins 了picker.vue
,两者合起来可看成是一个组件,这个组件也就是我们暴露出去的MyDatePicker
组件,也就是说,我们通过传进来的
timezone
属性值可以在picker.vue
的props
接收到。于是picker.vue
修改后的代码如下:
<template>
...
template>
<script>// 省略若干importimport moment from 'moment-timezone'// 省略若干代码export default {props: {// 省略若干代码
timezone: {type: String,default: moment.tz.guess() // 没有传则使用本地的时区
}
},// 省略若干代码
}script>
MyDatePicker
组件根据不同的type
选择不同的子组件(DatePanel
、DateRangePanel
、MonthRangePanel
等)进行挂载,我们可以在它挂载的时候把timezone
传给子组件:
// picker.vue
// ...