IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

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

    obaby发表于 2024-06-22 12:54:11
    love 0

    一直想做闺蜜圈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就算是修复了。其实之前也想过这个问题,不过对于渲染逻辑没想清楚,所以也没下手。

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

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



沪ICP备19023445号-2号
友情链接