日期选择器
用vue2全家桶写一个单页应用项目,写了一个简陋的日期选择器,如下图

图标什么的,不要在意这些细节…
原理
原理的话是这样子的
原始版本
布局
我的布局是:
.u-datepicker | 本体,限高,relative
.datepicker-panel up | 日历面板,absolute,up/down控制显示在上下方上
.datepicker-row | flex
.datepicker-cell*2 | 年/月 -
.datepicker-title
.datepicker-cell*2 | 年/月 +
.datepicker-grid | flex,flex-wrap
.datepicker-item*35
.datpicker-row
.datepicker-cell | 今天
如果用父元素text-align,子元素inline-block并消除字间距词间距的话兼容性会更好
初始化
传一个prop进来作为原始的日期,比如date,字符串(或者时间戳)都可以。
在this.created方法中格式化后转成时间戳存一下。年,月,日单独存下备用。
展示
获取当前月份的天数,当月一号的星期(xxx.getDate(),0-6),如果每周日算作当周第一天,获取到的星期正好就是1号位置前面空的格子数,然后加上月份里每一天的格子,一般会占用五行,少数情况下占用四行,比如2026年2月。总之凑35或者28个object,里面存上日期,月份,用v-for跑一下;
事件
布局和按钮功能参考别的日期选择器就可以了,比如年的大小变动和月的大小变动,去更改之前存储的值。而一旦点击了日,就更新存储的时间戳 …
边界判断
组件是可以通过this.$el 获取组件最外层元素的,获取下日历面板的height和top值,去跟window.innerHeight比较。超过底边范围的话就显示在上方。 注意 element.offsetTop是元素相对于父元素的高度,如果需要相对窗口的值,需要递归的去获取offsetParent.offsetTop直到null。
更新值
日期选择器毕竟是要选择日期的,需要更新旧的数据。如果是vue1的话,prop可以直接动态的修改。但是vue2的话,这样是不被允许的。可选的办法是自定义事件。组件可以用this.$emit('事件名'/, 参数们)的形式触发事件。父组件里需要留个方法接收事件和值,然后为了父组件可以找到对应的值,可以给组件加个prop,比如叫index之类的。
比如父组件里这么写
<datepicker
:date="xxx.start_day"
index="start_day"
v-on:ev_dateupdated="updateDate"
></datepicker>
$emit 除了第一个参数是作为事件名之外,后面的参数会依次传入事件处理方法
升级版
只是这样当然不够,可能还会需要一个最小值最大值,或者多个选择器需要联动
最小值,最大值
我是在第一版基础上直接加的这个功能。初始化的时候,把最小值和最大值也作为两个prop,比如 min,max,并设置默认值。同样格式化之后转成时间戳存储。对于选择的越界日期,把边界值作为选择结果。
更合理的办法应该是,在切换年月的时候判断是否会超过范围,超过的就不给切换了。对于年月没有超范围,但是判断所选日期超范围的可以舍弃选择保留旧值。
再优化一下的话是对每次的年月判断是否能够切换上下年,上下月,每个日期格子是否可以选择,不能的直接从视觉上标记出来,比如灰色。舍弃相应的操作。
多选择器联动
我是这么解决的。把最大最小值的初始化从created转移到updated中去。对要选择日期范围这种前值小于后值的情况,把前值作为后值的最小值,后值作为前值的最大值。这样每次选择日期之后,都会动态更新另一个选择器的可选范围。