前言

前些时间导师接了一个数字孪生的小项目,让我一个人完成前端三维隧道看板以及填报系统的构建。

技术路线:vue element admin + element ui + arcgis api for javascript。

最近项目接近尾声,我记录一下之前遇到一下项目过程中犯得一个幼稚的错误。

需求

前端实现一个日期滑块,要能选择开始和结束日期,通过滑动日期滑块选择某日期前所有模型的显隐,达到模拟施工进度的效果。

ps:模型属性没日期,需要将日期传给后端,然后后端返回模型的链接以及模型 id。

思路

说多了,继续回到标题。制作一个日期滑块,自然而然地想到了利用 elemnet ui 的datepiker组件和slider组件。Datepiker 负责选择起始日期,然后将选择的日期传给 slider,然后调用 slider 组件的 input 方法,将日期轴上变化的时间传给后端。So easy,看来今天能早点下班。

element-ui-slider组件

element-ui-datepiker组件

结果等我想传值的时候,突然发现 slider 的绑定值是 number 类型,但是一年 365 天的日期不连续,甚至偶尔还有 366 天出来捣乱,我该怎么将日期跟 number 关联上?最开始的想法是计算起始日期给和 slider 绑定的 value 计算硬算出当前日期,后来考虑到大小月平闰年的,属实不现实,我直呼做不了。

但是甲方直呼 arcgis api 有现成的sample啊,他们是怎么做到?我看了看确实有时间轴,也确实能过滤要素。但是他的日期是固定的啊,固定的时间轴谁不会啊?甲方表示不听不听,王八念经。

不过能力不够,我很抱歉,只能先做一个可以自由选择起始和终止月份的时间轴先应付着,把别的简单功能先完成,再回头做这个吧。甲方无奈之下,也只能暂时妥协。

按月份做时间轴滑块的思维过程就很流畅了,先计算起始月份和终止月份相差多少个月确定 slider 的最大值,然后根据 slider 的绑定值加上起始月份除以 12 求商并取余就能得到当前绑定值是哪年哪月了。

后来其他需求做的差不多,甲方表示时间轴的精度太低了,这样哪还有施工进度模拟的感觉,一个月有将近三十天,那就每个月取三个日期,1 号,11 号,21 号吧,效果看起来能好点。

啧,感觉他们的需求很合理啊,这么正常的需求我做不出来,肯定是我能力不够,就想去我去网上看看有没有现成的轮子。结果我检索能力也不行,找了一圈没找到合适的。

算了,我自己再想想,顺便去做了个核酸,喉咙被戳得挺不爽的,突然一时就思路清晰了,用时间戳啊,时间戳每天的时长固定的而且能跟日期相互转换。起始日期转为时间戳,然后加上 slider 绑定值的天数的时长,再转换为当前日期就完成了。这么简单的事,我一时居然想不到,果然是我还是经验不够能力不行。

代码

js
// Datepiker
pickDate() {
    if (this.dateselect != []) {
        // Datepiker的绑定值是一个数组,存着起始日期和终止日期
        const startDate = Date.parse(this.dateselect[0])
        const endDate = Date.parse(this.dateselect[1])
        // 计算起始日期和终止日期相差多少天,从而确定slider的最大值
        this.datemax = (endDate - startDate) / (1 * 24 * 60 * 60 * 1000)
        this.startdate = this.timepiker[0]
        this.enddate = this.timepiker[1]
    }
}
js
// Slider
formatTooltip(val) {
        // 格式化tooltip,使tooltip显示当前日期,val为当前slider绑定值
        const startdate = Date.parse(this.dateselect[0])
        const nowdate = new Date(startdate + 1 * 24 * 60 * 60 * 1000 * val)
        const Y = nowdate.getFullYear()
        const M =
            nowdate.getMonth() + 1 < 10 ?
            '0' + (nowdate.getMonth() + 1) :
            nowdate.getMonth() + 1
        const D = date.getDate() < 10 ? '0' + nowdate.getDate() : nowdate.getDate()
        const nowdate = `${Y}-${M}-${D}`
        //当前日期格式为"yyyy-MM-dd"
        return nowDate
    },

    changeModel() {
        // ToDo:传递this.sliderdate给后端,返回当前日期下,存在的模型的链接以及模型id。
    }

效果

选择起始和终止日期后,滑块会显示所有日期,拖动日期滑块即可控制对应模型的显示和隐藏。

效果如下图:

最终功能展示图

总结

时间戳是 js 里很基础的概念了,不过我之前的项目经历中少有实际应用场景,所以导致我只知道有时间戳这个概念,却忘记了合理运用。

这充分证明了我的开发经验并不充足,知识常用常新,要对那些不熟却很基础的概念需要做到心中有数,能灵活使用。

另外,月份为跨度的时间轴滑块其实也是有实际应用场景的,我也把它贴在最后,权当一个教训吧。

js
pickmonth() {
    if (this.timepiker != '') {
        let month1, month2
        month1 = this.timepiker[0].split('-')
        month2 = this.timepiker[1].split('-')
        month1 = parseInt(month1[0]) * 12 + parseInt(month1[1])
        month2 = parseInt(month2[0]) * 12 + parseInt(month2[1])
        this.levelmax = Math.abs(month2 - month1)
        this.startmonth = this.timepiker[0]
        this.endmonth = this.timepiker[1]
    }
}

formatTooltip(val) {
    if (this.timepiker != '') {
        let date = this.timepiker[1].split('-')
        let year = parseInt(date[0])
        let month = parseInt(date[1])
        if (month - ((this.levelmax - val) % 12) > 0) {
            year = year - Math.floor((this.levelmax - val) / 12)
            month = month - ((this.levelmax - val) % 12)
        } else if (month - ((this.levelmax - val) % 12) < 0) {
            year = year - Math.ceil((this.levelmax - val) / 12)
            month = month + 12 - ((this.levelmax - val) % 12)
        } else if (month - ((this.levelmax - val) % 12) == 0) {
            year = year - Math.ceil((this.levelmax - val) / 12)
            month = 12
        }
        return year + '-' + month
    }
}