100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > Vant组件库封装可翻页日历组件

Vant组件库封装可翻页日历组件

时间:2024-04-22 16:42:56

相关推荐

Vant组件库封装可翻页日历组件

前言

我们在进行VUE开发的时候有的时候会使用到VantUI组件库:

https://vant-contrib.gitee.io/vant/v2/#/zh-CN/home#jie-shao

Vant 是一个轻量、可靠的移动端组件库,于 年开源。

目前 Vant 官方提供了 Vue 2 版本、Vue 3 版本和微信小程序版本,并由社区团队维护 React 版本和支付宝小程序版本。

但是近期在使用Vant组件库(下方所有Vant组件库均指代Vant2.x)中的日历时发现了一个bug:

BUG描述

需求是获取到当前日历的展示月份,然后使用按钮对日历进行增加或者减少操作,从而使得视图能跳转到增加或减小到的月份,如这样:

但是在调用官方api来获取当前进入视图的月份的信息的时候发现了一个致命问题:

就上图而言,可以看出,当用户首次打开日历(注意,是首次)的时候,往下滑动,确实会调用官方api,month-show进行传参显示(打印在控制台里面),但是回滑的时候却并不会触发month-show这个api。就比如用户自上而下从2月滑到了5月,其中2、3、4、5月份的信息都会调用month-show这个api,但是从5月往回滑却不会触发month-show这个api。

一句话,只有当月份首次进入视图的时候才会触发month-show这个api,非首次进入视图并不会触发month-show这个api。这显然和官方的描述是相悖的:

调用的demo的源码:

<template><div class="main"><button @click="show = true">点击弹出Vant日历</button><van-calendar v-model="show" @confirm="onConfirm" @month-show="dateShow"/></div></template><script>import Vue from "vue";import { Calendar } from "vant";Vue.use(Calendar);export default {name: "demoPage",data() {return {date: "",show: false};},mounted() {},methods: {formatDate(date) {return `${date.getMonth() + 1}/${date.getDate()}`;},onConfirm(date) {this.show = false;this.date = this.formatDate(date);},dateShow(date){console.info(date)}}};</script><style scoped>.main {width: 100%;height: 500px;}</style>

问题分析

到现在可以看出并不是开发者调用出了问题,而是Vant的日历组件的源码就有问题,算是个原生bug了。于是在不懈查找下,查到了vant中的日历对应month-show这个api的源码:

对应路径:\node_modules\vant\es\calendar\index.js

可以看出,在代码中的逻辑是遍历每个月份然后判断是否显隐,如果计算得知是应该展示的,则调用

this.$emit('month-show');

这个方法将满足要求的月份的信息和数据传输出去。

包括了代码的这一部分,总体上构成了对当前日历是否显隐的逻辑判断。

具体是哪里计算错误,就需求为导向而言就没必要深究了。只是知道对于Vue2.x(至少截止到2.12.54版本,bug依旧存在)而言,想修复这个bug只能对源码进行修改。

BUG解决思路

既然,使用视图判断不行,那就直接取日历的副标题,也就是subTitle,判断当其改变的时候,自动触发month-show方法实现类似于日历月份主体进入视图触发的效果。

但是从该图可以看出,日历的主体明明是6月份,但是副标题却显示的是5月份,这又是一个bug。。。不过此bug可以将传出的月份进行或增或减来校准一下。

当currentMonth(也就是subTitle对应的副标题)数据改变的时候,从month-show传出该数据。

if (currentMonth && (this.subtitle !== currentMonth.title)){this.subtitle = currentMonth.title;this.$emit('month-show', {date: currentMonth.date,title: currentMonth.title});}

同时删除该部分代码:

注意: 修改node_modules源码后记得重新跑进程启动。

效果测试

可以看到修复效果良好,只不过可能得对输出的月份进行增减来进行校准。

需求实现全部代码:

calendar.vue:

<template><div class="calendar"><div class="date" @click="handleCalendarShow"><span>{{displayDate[0]}}</span><span>-</span><span>{{displayDate[1]}}</span><img :src="calIcon" alt="calendar" /></div><van-calendarref="calendar":default-date="date"v-model="show":show-subtitle="false"type="range"@confirm="onConfirm"@month-show="dateChange":min-date="maxDateRange[0]":max-date="maxDateRange[1]"><template v-slot:title><div class="timeSelect"><img :src="multiArrow" @click="scrollToDate('down','year')" class="left" /><img :src="signalArrow" @click="scrollToDate('down','month')" class="left" /><p>{{titleDate.title}}</p><img :src="signalArrow" @click="scrollToDate('up','month')" /><img :src="multiArrow" @click="scrollToDate('up','year')" /></div></template></van-calendar></div></template><script>import Vue from "vue";import moment from "moment";import { Calendar, Toast } from "vant";import { defaultDate, maxDateRange, dateFormat } from "./config";// 日历图标import calIcon from "./calIcon.png";import signalArrow from "./signalArrow.png";import multiArrow from "./multiArrow.png";Vue.use(Calendar);Vue.use(Toast);export default {name: "demoPage",data() {return {// 日历副标题titleDate: {date: defaultDate[1],title: "2月"},// 日期格式dateFormat,//默认时间范围defaultDate,//当前选择时间date: defaultDate,// 日期选择范围maxDateRange,// 日历弹窗是否展示show: false,calIcon,signalArrow,multiArrow};},computed: {//用于展示的时间displayDate() {return [moment(this.date[0]).format(dateFormat),moment(this.date[1]).format(dateFormat)];}},mounted() {},methods: {//日历弹窗方法handleCalendarShow() {this.show = true;setTimeout(() => {//设置日历转到最新日期的展示界面}, 300);this.$refs.calendar.scrollToDate(new Date());},//选择日期触发方法onConfirm(date) {this.show = false;this.date = date;},//重置时间选择为默认时间reset() {this.date = this.defaultDate;this.$refs.calendar.reset(this.defaultDate);},//日历视图滚动时更新日历副标题数据dateChange(data) {let transDate = moment(data.date).add(1, "months");if (transDate <= maxDateRange[1] && transDate >= maxDateRange[0]) {if (moment(transDate).diff(moment(maxDateRange[0]), "months") < 1) {transDate = moment(data.date);}const yearNum = transDate.format("YYYY");const monthNum = parseInt(transDate.format("MM"));const newDate = {date: new Date(transDate.format()),title: yearNum + "年" + monthNum + "月"};this.titleDate = newDate;}},//视图滚动到指定日期的视图scrollToDate(type, dateType) {let transDate = "";if (type == "up") {transDate = moment(this.titleDate.date).add(1, dateType);}if (type == "down") {transDate = moment(this.titleDate.date).add(-1, dateType);}const leftDiffMonths = moment(transDate).diff(moment(maxDateRange[1]), "months");const rightDiffMonths = moment(maxDateRange[0]).diff(moment(transDate),"months");// 控制翻页范围,超出就提示if (leftDiffMonths <= -13 || rightDiffMonths < -11) {Toast.fail("已超出最大可选范围");return null;}const yearNum = transDate.format("YYYY");const monthNum = parseInt(transDate.format("MM"));const newDate = {date: new Date(transDate.format()),title: yearNum + "年" + monthNum + "月"};this.titleDate = newDate;this.$refs.calendar.scrollToDate(new Date(newDate.date));}},watch: {date: {handler() {this.$emit("dateChange", this.displayDate);},immediate: true,deep: true}}};</script><style lang="less" scoped>.calendar {width: 100%;display: flex;justify-content: space-evenly;align-items: center;box-sizing: border-box;padding: 10.4px 10.4px 10.4px 0;color: #989898;position: relative;.date {width: 100%;height: 28.6px;position: relative;border: 1px solid #e0e0e0;border-radius: 5px;display: flex;align-items: center;span {margin-left: 19.5px;}img {position: absolute;right: 5px;width: 23.4px;height: 23.4px;}}.timeSelect {width: 100%;box-sizing: border-box;display: flex;align-items: center;justify-content: space-between;padding: 0 15px;img {width: 19.5px;height: 19.5px;touch-action: none;}.left {transform: rotateY(180deg);}}/deep/ .van-calendar__header-title {height: auto;}/deep/ .van-popup__close-icon {display: none;}/deep/.van__popup--bottom {height: 70%;}}</style>

config.js:

import moment from 'moment';//时间格式export const dateFormat ="YYYY-MM-DD";//当前时间const today = new Date();//默认起始时间const defaultStartDay = moment().add(-1, "years");//时间筛选框默认时间export const defaultDate = [new Date(defaultStartDay), today];//日期选择限制export const maxDateRange = [new Date(defaultStartDay), today];

父组件调用:

<template><div class="main"><div class="calendarArea"><Calendar ref="calendar" @dateChange="dateChange" /></div></div></template><script>import Calendar from "./component/index.vue";export default {name: "demoPage",components:{Calendar},data() {return {};},mounted() {},methods:{dateChange(dates){console.info(dates)},// 重置日期reset(){this.$refs.calendar.reset()}}};</script><style lang="less" scoped>.main{width: 100%;height: 100%;.calendarArea{width:70%;}}</style>

最终效果:

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。