黔驴技穷 — 山穷水复疑无路 柳暗花明又一村

一直想做闺蜜圈app日历的滑动切换效果,然鹅,目前使用的控件是mr calendar。https://ext.dcloud.net.cn/plugin?id=7251

之所以用这个控件,是因为在测试了目前市场上的所有控件,能达到使用效果的,或者说效果最好的就是这个控件了,支持自定义样式,所以实际使用效果还算ok。

不过在使用的过程中,也出现了很多问题,插件其实并没有完全满足自己的需求。在开发的过程中对插件进行了一些修改和完善,之前提到过一些问题,包括但是不限于:

Uniapp 日历组件在 iOS 上的 bug

以及:

日历控件bug太多了,小程序也有问题

不过好在最后这些问题都是解决掉了,但是,对于日历滑动却一直没有解决。如果单独的使用日历的滑动效果是没问题的。日历插件的滑动效果是使用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就算是修复了。其实之前也想过这个问题,不过对于渲染逻辑没想清楚,所以也没下手。

至此,滑动问题完美解决,下个版本就可以安排上啦。

看来,要解决这个也没那么难,这不就柳暗花明又一村了~~

☆版权☆

* 网站名称:obaby@mars
* 网址:https://h4ck.org.cn/
* 个性:https://oba.by/
* 本文标题: 《黔驴技穷 — 山穷水复疑无路 柳暗花明又一村》
* 本文链接:https://h4ck.org.cn/2024/06/17385
* 短链接:https://oba.by/?p=17385
* 转载文章请标明文章来源,原文标题以及原文链接。请遵从 《署名-非商业性使用-相同方式共享 2.5 中国大陆 (CC BY-NC-SA 2.5 CN) 》许可协议。


You may also like

17 comments

  1. Level 2
    Google Chrome 100 Google Chrome 100 Windows Server 2003 Windows Server 2003 cn上海市 联通

    姐姐太帅了,虽然我看不懂,但是做事情那种专业程度就很迷人。

    1. 公主 Queen 
      Google Chrome 120 Google Chrome 120 Windows 10 Windows 10 cn山东省临沂市 联通

      这个问题困扰了自己好几个月了,一直在不间断的调试,找bug,也一度想放弃这个功能,或者是换其他的插件,但是看了下,效果依然不够理想。所以还是继续坚持改。
      好在最后,bug原因还是定位到了。

  2.   Level 4
    Microsoft Edge 126 Microsoft Edge 126 Windows 11 Windows 11 cn山东省济南市 移动

    当时电脑安装了个ios模拟器,结果没有独显,卡的要嗝屁

  3.  Level 5
    Microsoft Edge 126 Microsoft Edge 126 GNU/Linux GNU/Linux cn广东省珠海市 电信

    没懂,你是这个控件的源码要自己修改才能解决么?我看了下这个日历控件的介绍,好多都不支持,打x的多

  4.  Level 5
    IBrowse r IBrowse r Android 12 Android 12 cn河南省 移动/数据上网公共出口

    我感觉对于闺蜜圈写的各种文章汇总一下,可以出你的第二本书了《闺蜜觉醒~从零开发APP完整始末》。

  5. Level 5
    Microsoft Edge 126 Microsoft Edge 126 Windows 10 Windows 10 us美国加利福利亚州洛杉矶

    《论丝袜对灵妹妹开发的效能提升》

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注