一直想做闺蜜圈app日历的滑动切换效果,然鹅,目前使用的控件是mr calendar。https://ext.dcloud.net.cn/plugin?id=7251
之所以用这个控件,是因为在测试了目前市场上的所有控件,能达到使用效果的,或者说效果最好的就是这个控件了,支持自定义样式,所以实际使用效果还算ok。
不过在使用的过程中,也出现了很多问题,插件其实并没有完全满足自己的需求。在开发的过程中对插件进行了一些修改和完善,之前提到过一些问题,包括但是不限于:
以及:
不过好在最后这些问题都是解决掉了,但是,对于日历滑动却一直没有解决。如果单独的使用日历的滑动效果是没问题的。日历插件的滑动效果是使用swiper实现的:
<swiper class="mr-calendar__container__content" vertical :current="currentIndex" :duration="duration" @animationfinish="animationfinish" skip-hidden-item-layout circular> <swiper-item class="mr-calendar__container__content__item" v-for="(item, itemIndex) in showMonthList" :key="itemIndex"> <view class="mr-calendar__container__content__item__bg">{{item.month}}</view> <view v-for="(row, index) in item.days" :key="index" class="mr-calendar__container__content__item__row"> <view class="mr-calendar__container__content__item__col" :class="{ 'is-range': type === 'range' }" v-for="day in row" :key="day.format"> <mr-calendar-item :day="day" :cell-class="cellClass" :type="type" :selected-value="selectedValue" :selection="selection" @click="onDayClick"> </mr-calendar-item> </view> </view> </swiper-item> </swiper>
滑动实现逻辑:
animationfinish(e) { const oldDate = this.currentDate; const index = e.detail.current; this.currentIndex = index; this.currentDate = this.showMonthList[index].id; this.setShowMonthList(index); this.$emit('date-change', this.currentDate, oldDate); },
然而,这个日历组件在动态修改自定义加载的日历数据之后,不会直接渲染。需要主动调用插件的fresh方法,例如下面的样子:
monthChange(e){ console.log('monthChange',e) this.customDate = [{ cellClass: 'custom-cell', date: '2024-07-24', top: [{ class: 'custom-cell-top-1', text: '①' }, // { // class: 'custom-cell-top-2', // text: '6' // } ] }, { cellClass: 'custom-cell', date: '2024-07-24', top: [{ class: 'custom-cell-top-1', text: '①' }, // { // class: 'custom-cell-top-2', // text: '6' // } ] }, { cellClass: 'custom-cell', date: '2024-07-21', top: [{ class: 'custom-cell-top-1', text: '①' }, { class: 'custom-cell-top-2', text: '6' } ] }, { cellClass: 'custom-cell', date: '2024-07-20', top: [{ class: 'custom-cell-top-1', text: '①' }, // { // class: 'custom-cell-top-2', // text: '6' // } ] }] // setTimeout(()=>{ this.$refs.calendar.refresh(); // },1000) // this.$set(this.) }
然鹅,就是this.$refs.calendar.refresh();这行代码导致刷新的时候日历组件出错了。变成了混乱过状态。
但是,不调用这个方法,添加的数据就不会显示。原日历组件没有月份跳转的功能,
插件的月份跳转就是通过滑动实现的,由于之前在测试的时候滑动一直有问题,我自己进行了修改添加了两个button进行月份调整,就是顶部的两个单箭头。
这两个单箭头的实现是通过下面的方法:
toMonth(month) { // console.log('toMonth'); if (!this.currentDate) return; let oldDate = this.currentDate; let backDate = this.currentDate; let tempCurrentDate = null; if (typeof this.currentDate === 'string') { let tempCurrentDateString = this.currentDate; if (tempCurrentDateString.length < 8) { // console.log('short'); tempCurrentDateString = tempCurrentDateString + '/01'; tempCurrentDate = new EDate([tempCurrentDateString]); backDate = new EDate([tempCurrentDateString]); } } if (month === 0) { const currentDate = new EDate().format('YYYY/MM'); if (currentDate === this.currentDate) return; this.currentDate = currentDate; backDate = this.currentDate; this.setShowMonthList(this.currentIndex); } else { backDate = EDate.modify(backDate, { m: month }) // backDate = this.currentDate = EDate.modify(tempCurrentDate, { m: month }).format('YYYY/MM'); // this.currentDate = dateFormat(EDate.modify(tempCurrentDate, { // m: month // }),'yyyy/MM'); this.setShowMonthList(this.currentIndex); } // this.$emit('date-change', this.currentDate, oldDate); console.log('’mr calendar', 'month change current index= ', this.currentIndex) this.$emit('month-change', backDate, oldDate); },
实际上上面的方法在使用的过程中也没任何的问题,同样调用refresh方法也不会出现错位的情况。但是滑动就会出问题,并且滑动方法中直接调用toMonth方法来切换,一样会出现错的情况。开始以为是currentIndex的问题,但是监视了一下currentIndex的变化和修改,不管调用或者不调用refresh方法,索引值都是一样的,没有看出任何的不同来。昨晚折腾到八点多,一度想放弃了。包括showMonthList的监视,也没看出任何问题。最后一度想放弃了,主要是实在没有头绪,也不知道该从哪里下手,大概的情况知道,但是怎么修复完全是一脸懵逼,作为一个非专业前端,做的功能全部都靠抄抄抄,自己写代码实在是能力有限啊,来自战五渣的痛,在这一刻真的是黔驴技穷了,完全不知道该怎么继续了。
然而,就这么放弃总不是自己的风格,今天上午决定再挣扎一番,之前连汇编代码复杂算法都搞得定,这tm都有源代码的高级语言还能把自己难住了,姐姐我就不信了!!
今天上午找研发聊了一下,说修改属性页面不刷新的问题,他提供了个方法,直接调用this.$set 来对属性赋值,但是这个方法测试后依然无效。
实现代码里有一段是:
this.currentIndex = index; const [beforeIndex, currentIndex, afterIndex] = [ [2, 0, 1], [0, 1, 2], [1, 2, 0] ][this.currentIndex]; const before = this.showMonthList.find(item => item.id === beforeDate) || this.getMonthDays( beforeDate); const current = this.showMonthList.find(item => item.id === currentDate) || this.getMonthDays( currentDate); const after = this.showMonthList.find(item => item.id === afterDate) || this.getMonthDays(afterDate); this.showMonthList[beforeIndex] = before; this.showMonthList[currentIndex] = current; this.showMonthList[afterIndex] = after;
这个东西,其实我一直没搞明白是要干嘛,聊了下才反应过来,对应的是当前的showMonthList的索引,不管在任何时间,所有的月份都是三个数组,在滑动的过程中索引值就会根据上面的代码进行变化。到这里,我忽然想到问题的关键是什么了,通过toMonth进行月份切换和滑动切换对应的月份数组可能是不一样的。
完善监控代码,对showMonthList修改前后的数据进行对比。
watch: { visible(val) { clearTimeout(this.watchTimer); setTimeout(() => { this.active = val }, 100); if (this.visibleSync) { clearTimeout(this.closeTimer) } if (val) { this.visibleSync = val; this.initValue(); } else { this.watchTimer = setTimeout(() => { this.showMonthList = []; this.currentIndex = 1; this.visibleSync = val }, 300) } }, currentIndex(newVal, oldVal) { console.log(`currentIndex changed from ${oldVal} to ${newVal}`); // 在这里可以执行更多的逻辑 }, showMonthList(newVal, oldVal) { console.log(`showMonthList changed from ${oldVal} to ${newVal}`); // 在这里可以执行更多的逻辑 console.log('----------------old------------------') oldVal.forEach((item) => { console.log(item.id); }); console.log('----------------new------------------') newVal.forEach((item) => { console.log(item.id); }); console.log('----------------end------------------') }, },
这时候有观察了一下数据监视的变化,在不调用刷新的情况下,通过toMonth方法调用的showMonthList月份都是连续的,比如[2024/02,2024/03,2024/04],[2024/03,2024/04,2024/05]
然而,滑动切换,showMonthList的月份不在连续了:[2024/10,2024/11,2024/09]
那么此时refresh里面的代码就有问题了:
setShowMonthList(index, refresh) { if (!this.currentDate) return; const currentDate = this.currentDate; const beforeDate = EDate.modify(`${this.currentDate}/01`, { m: -1 }).format('YYYY/MM'); // ios 预览下 new Date('2019/02')返回null const afterDate = EDate.modify(`${this.currentDate}/01`, { m: +1 }).format('YYYY/MM'); if (!this.showMonthList.length || refresh) { const before = this.getMonthDays(beforeDate); const current = this.getMonthDays(currentDate); const after = this.getMonthDays(afterDate); this.showMonthList = [before, current, after]; } else { this.currentIndex = index; const [beforeIndex, currentIndex, afterIndex] = [ [2, 0, 1], [0, 1, 2], [1, 2, 0] ][this.currentIndex]; const before = this.showMonthList.find(item => item.id === beforeDate) || this.getMonthDays( beforeDate); const current = this.showMonthList.find(item => item.id === currentDate) || this.getMonthDays( currentDate); const after = this.showMonthList.find(item => item.id === afterDate) || this.getMonthDays(afterDate); this.showMonthList[beforeIndex] = before; this.showMonthList[currentIndex] = current; this.showMonthList[afterIndex] = after; } },
上述代码通过refresh if (!this.showMonthList.length || refresh) 参数判断是否要刷新,在刷新的情况下直接重建了这三个月的数据。然而,这三个月的数据是根据上面的代码:
const currentDate = this.currentDate; const beforeDate = EDate.modify(`${this.currentDate}/01`, { m: -1 }).format('YYYY/MM'); // ios 预览下 new Date('2019/02')返回null const afterDate = EDate.modify(`${this.currentDate}/01`, { m: +1 }).format('YYYY/MM');
来实现的,这个月份是通过顺序创建出来的,那么在滑动的时候,因为showMonthList是乱序的,而刷新导致强制重建了顺序的数据,这就直接bug了。想明白了这点,那么要修复也就终于有头绪了。
在刷新的时候,如果showMonthList为空直接重建,如果存在showMonthList的情况,应该是将showMonthList里面的月份进行数据渲染回填,这才是正确的逻辑,修复后的代码:
// 滑动刷新 重新生成的数据,与原来的数据不一致 导致数据出错 setShowMonthList(index, refresh) { if (!this.currentDate) return; let currentDate = this.currentDate; let beforeDate = EDate.modify(`${this.currentDate}/01`, { m: -1 }).format('YYYY/MM'); // ios 预览下 new Date('2019/02')返回null let afterDate = EDate.modify(`${this.currentDate}/01`, { m: +1 }).format('YYYY/MM'); if (!this.showMonthList.length || refresh) { //处理刷新逻辑 如果存在列表 按照旧列表刷新数据内容 刷新旧列表,否则创建 if (this.showMonthList.length>0){ beforeDate = this.showMonthList[0].id currentDate = this.showMonthList[1].id afterDate = this.showMonthList[2].id } const before = this.getMonthDays(beforeDate); const current = this.getMonthDays(currentDate); const after = this.getMonthDays(afterDate); this.showMonthList = [before, current, after]; } else { this.currentIndex = index; const [beforeIndex, currentIndex, afterIndex] = [ [2, 0, 1], [0, 1, 2], [1, 2, 0] ][this.currentIndex]; const before = this.showMonthList.find(item => item.id === beforeDate) || this.getMonthDays( beforeDate); const current = this.showMonthList.find(item => item.id === currentDate) || this.getMonthDays( currentDate); const after = this.showMonthList.find(item => item.id === afterDate) || this.getMonthDays(afterDate); this.showMonthList[beforeIndex] = before; this.showMonthList[currentIndex] = current; this.showMonthList[afterIndex] = after; } },
现在再次调用数据刷新,是真的是刷新数据,不是重建数据了,这个bug就算是修复了。其实之前也想过这个问题,不过对于渲染逻辑没想清楚,所以也没下手。
至此,滑动问题完美解决,下个版本就可以安排上啦。
看来,要解决这个也没那么难,这不就柳暗花明又一村了~~
17 comments
一直想做闺蜜圈app日历的【华东】效果
你这也太快了,fixed
姐姐太帅了,虽然我看不懂,但是做事情那种专业程度就很迷人。
这个问题困扰了自己好几个月了,一直在不间断的调试,找bug,也一度想放弃这个功能,或者是换其他的插件,但是看了下,效果依然不够理想。所以还是继续坚持改。
好在最后,bug原因还是定位到了。
厉害,收藏学习了
改bug就像升级打怪,赢一次就可以开心很久😎
老开心了
当时电脑安装了个ios模拟器,结果没有独显,卡的要嗝屁
佩服
没懂,你是这个控件的源码要自己修改才能解决么?我看了下这个日历控件的介绍,好多都不支持,打x的多
x不x先不说,主要是打✅的可能也是一堆bug
Bug 先放一放,这丝袜不错,我都想穿
买一双试试,嘻嘻
嗯!你说得对!我也感觉应该这么做!
我感觉对于闺蜜圈写的各种文章汇总一下,可以出你的第二本书了《闺蜜觉醒~从零开发APP完整始末》。
精力有限,哈哈哈。
《论丝袜对灵妹妹开发的效能提升》