给 layDate 来一场大革命

分享 未结 精帖
170 9194
岁月小偷
悬赏:80飞吻
更新于 2018-12-19
新增两个功能效果支持:
10,新增可以定义周n作为一周的开始(weekStart)[new]

11,支持this标记的背景为圆圈(circleMark)[new]
更新于2018年11月9日20:12:16
帮小伙伴实现了另外的一个快速选择的效果,同步的更新上去了,顺便的更新一下帖子,先附上主要的效果,下面会详细说明:
更新于2018年11月22日21:22:35
最近比较忙,答应社区小伙伴的新增一个季度选择的功能也是一拖再拖,忘了是谁了,说一声抱歉,今晚终于弄的差不多了赶紧补上这个功能同时做了一些调整,对源代码,把laydate的构造器也透出来,方便后面调优,还有如果因为laydate底层的一些逻辑导致的bug可以在laydatePro里面去修复,而不用过多的修改源码,虽然这种方式跟直接在源码上修改差不多了,但是等于是让laydatePro接手了,觉得会更加合理方便一些。举个例子就是如果你想修改Array的indexOf的实现,一般来说不是直接去js的底层修改而是通过Array.prototype.indexOf = fnNew这种方式去修改。
先上季度选择的效果:

具体的实现思路见下面的第9点
更新于2018年11月1日12:11:56
把layui更新到最新版本,并且提供了独立的laydate使用的方式还有因为需要修改一点源码所以提供了laydate相关的组件化使用,非组件化使用和独立版本,都在release文件下,自己找到合适的下载使用。
如题,是时候给laydate来一场大革命了,[哈哈] [哈哈] [哈哈] ,说的有点狂妄了,实际上目前laydate的已经足够应对很多的业务场景,各方面总的来说还都是足够好的。但是,足够好也不能说没有问题,比如,无法通过elem设置某一类节点的选择器来一次性渲染多个节点,再比如无法通过再次render一个已经存在的节点在更新里面的设置,要修改已经渲染过的节点里面的设置,这个目前基本非常麻烦而且有一些属性不是你麻烦不麻烦的问题是根本改不了,再比如想要加入自定义按钮,再比如想要加入快速选择,再比如想要...,再比如想要...,再比如想要...,相信或多或少都会遇到这样或那样的需求目前laydate实现不了的。那么接下来的改动算不算是对laydate的一次强化一次革命,那就见仁见智了。

目标:对源码laydate最小的改动,不会影响以前的逻辑代码的前提下加强laydate功能还有更方便的使用。
目前laydate.js只修改了render返回的数据中添加多一个属性,保存当前laydate的实例,然后提供过一个laydatePro.js供引入,下文说的修改都是在新引入的laydatePro里面处理,不会再对laydate做更多的修改,所以对以前的代码来说应该是没有什么不好的影响的。关于laydatePro里面都做了啥后面详细介绍。
gitee项目: https://gitee.com/sun_zoro/laydatePro 欢迎star、fork和提出您的宝贵意见或者有什么关于laydate的特殊需求。后面会尽量的

概要:
1,支持一次性render多个节点。
2,支持render一个已经render过的节点。
3,新增lay-data属性来设置当前节点的laydate的配置。
4,实现快速选择时间的功能。(quickSelect)
5,纯月份年份点击直接确定。(quickConfirm)
6,不完整的时分秒选择。(simpleModel & format)
7,分裂式时间范围选择。(rangeType & range)
8,新增快速选择的两种场景支持。(range & quickSelect)
9,新增季度选择(type:'quarter')
10,新增可以定义周n作为一周的开始(weekStart)[new]
11,支持this标记的背景为圆圈(circleMark)[new]


详细内容:
1:支持一次性render多个节点。
首先目前laydate.render的时候elem可以是一个字符串或者一个dom对象,当设置成字符串的时候,我们很正常的都会想是否可以同时render一些节点,但是实际上目前laydate的实现逻辑不支持这样子滴,如果页面出现多个匹配的节点,只有第一个节点能正常使用,其他的节点会出现闪退的现象,关于这个以前发过另外的帖子,建议不要设置成字符串的形式除非是id或者能确定找到的就是你想要render的那个节点,而且页面上通过这个elem去找也只能找到这么一个,不然还是建议赋值成dom对象的形式。如果有需要render一类节点,需要遍历这些节点然后一个一个render。
修改方案,在laydatePro插件里面重新定义laydate.render,详细看代码截图:
首先是引入laydatePro

然后laydatePro依赖laydate所以可以只uselaydatePro就可以使用laydate,当然两个都显式引入也不会有任何问题。接下来看看laydatePro里面的相关代码:

代码里面箭头处还有红色的框框那些就是关键的代码,尽量都加了注释了,这里就不一一说明了,看图或者直接看代码里面的注释描述,最主要的就是利用一个变量先把原来的逻辑处理给缓存起来,然后重写,在需要的时候再把以前的逻辑缓存调用一下,这样子就可以在不修改源码的基础上进行一些扩展的操作。
测试代码:

效果:

2:支持render一个已经render过的节点
这个也是一个比较常见的需求了,类似于对一个table进行重载,根据需求,经常会遇到业务场景换了,需要动态的改变某个节点打开的laydate的类型。然后目前再次render一个节点是无效果的,不会有任何作用,这个主要有两个因素,一个是render之后会在这个节点上加入一些属性,另外一个就是在render的时候发现如果已经存在这些属性,它是会直接pass的。那么修改的方案就是在上面提到的重写的入口函数里面把当前节点中如果存在这些属性先给干掉,后面执行table.render的时候才会跟第一次render一样的效果,。当然还有另外一个问题,就是实际每次render的时候laydate会加上很多的监听,比如点击document会校验点击的是不是当前的节点,会做一些操作,然后也会给当前的节点加上点击trigger事件,让你点击或者focus的时候能够渲染出来一个laydate视图,因为如果要支持重复render,等于就有一个“过时”实例的概念,就是针对这个节点以前render的时候生成的实例实际上已经没有任何作用了。一方面最好就是能够做到后面的事件监听到的时候不要再相应这些实例了,但是试了一些方法没管用,后来换了一个思路,相应还是有相应,但是在关键的视图渲染函数render里面做一个过期判断,如果是当前有效的实例才去继续走下去。关键代码如图:
因为优先考虑是尽量不要修改源码的逻辑但是又遇到不得不处理内部定义的那个Class构造器的prototype,最后决定在render的时候把当前的实例给返回了,laydate修改如下:

然后laydatePro里面相关的处理:

测试代码:

效果:

3: 新增lay-data属性来设置当前节点的laydate的配置。
如果有耐心看下去的同学到这里估计会考虑一个问题,同时渲染一组节点是方便了,但是如果这些节点之间的配置不是都一致的,那么一次性render之后如何保持他们各自的个性?这个就是为了处理上面的问题的,就是说你可以把配置存放到节点上,而不是只能在render的参数里面写,而且放在dom中属性的权限要高于render中的config。这个跟upload组件的逻辑上是一致的。
主要代码:

测试代码:

效果:


到这里基本是本次修改的一个分水岭,以上的修改基本上能够优化以前的两个比较主要的render的问题,但是这个只是本次修改的一小部分,接下来的才是重头戏,就是如何自己扩展更多的功能实现,来应对更多的奇奇怪怪需求或者目前还没有支持的功能。这里抛砖引玉的做一些案例,主要是提供一个实现思路,后面大家可以继续丰富,当然也可以回帖说出你想要实现的功能的描述,我有精力有能力的话就给实现在laydatePro里面,当然,最好的方式后面还会提供一个类似于注册功能一样的方式来在尽量不用再laydatePro里面修改的情况下去添加自己需要的逻辑处理,不过这个也需要一个过程。

4:实现快速选择时间的功能。
这个功能一些其他的日期控件估计大家有接触过,就是定义一些常用的时间点之类的可以快速的选择,如果里面没有想要的,可以切换到普通模式下去选择。目前laydate是没有这个功能的,按照自己的审美给拓展了一下效果如下:

初步的代码大概有以下几部分:

快速选择按钮点击之后的主要处理逻辑

在ready回调的时候调用一下laydatePro.init

测试的相关代码:

效果就是上面看到的,主要是提供一种实现的思路,在不改太多源码的基础上,如何去实现这些原本不支持的功能,比如上面的快速选择的内容,如果要修改源码的话一般是在视图的render方法里面根据配置去生成出来,这样子一打开就已经存在了,但是这个处理对于后面升级来说也是比较麻烦的,所以在不改动它的情况下,利用了它ready打开回调里面去生成需要的节点到特定的地方,加上对应的事件监听,在加上这个到目前为止对源码唯一的改动的地方的地方得到的这个laydate实例,充分的利用它所提供的一些方法来实现我们的需求,基本就是这个套路,剩下的就看对laydate的熟悉程度,越了解就能更好的利用。
好了砖也抛出去了,今天差不多就到这,有疑问或者有意见的小伙伴欢迎回帖,有更好的实现方式也请不吝分享给大家学学。明天有空再把后面的砖头多加几块,然后再完善完善。
5:纯月份年份点击直接确定。
继续搬砖,看到社区有同学问是否能够月份点击就选中确定了,不要再麻烦的去点一下确定按钮,那么如何再当前这个修改下实现,这里继续扩展一个属性quickConfirm,在单纯的月份选择或者年份的情况下支持点击快速确认。修改如下

然后想要快速选择的时候再render中配置:

效果:

6:不完整的时分秒选择
看到社区的另外一个问题,能不能只选一个小时,继续给例子扩展出来一个simpleModel的配置,本来想着根据format来就可以,但是考虑到不是所有的人都喜欢,比如有的人就喜欢format:'HH'然后还是现实让你选择时分秒,只不过选了之后只保留了小时这样子的效果,所以为了调和更多人的口味,加了一个配置项来决定要还是不要。
看效果下:
format: 'H时'

format: 'mm分ss秒'

关键的代码

最后是使用时候的配置代码:

7:分裂式时间范围选择。(rangeType & range)
可能名称咋听不是很直白,但是实际一看效果的话应该就知道是什么意思,回帖中有同学说期待把下面的时间范围的也提供一种实现的例子。
先看看原始的range的效果:

也就是说它是一个完整的结构,虽然也有不少同学刚开始使用的时候会有一个误区就是觉得开始是左边选择的,然后结束是右边选择的,然后就会有疑问说如果是同一个月的呢?或者这两个选择器能不能不连着动,都是连着的两个月之类的,实际日期选择是没有限定左右的,而且可以选择结束日期然后再点击开始日期,各方面来说这个range还是效果很好的,但是习惯了两个选择的laydate呢,还有一个更加重要的点就是laydate的range不支持不是一个闭合区间的,比如只设置开始时间,或者只设置结束时间,那么做成两个laydate的话这个问题应该可以很好的处理掉。
但是两个laydate的话最主要的问题就是如何做到一个laydate去影响另外一个laydate的min max了,这个我以前也发过类似额帖子大概的实现了,也有其他的社区的同学分享过,大致的思路就是在一个laydate的done回调中去修改另外一个laydate render返回的对象中的config中的min/max但是实际上没有想象的那么简单,因为这个返回的结构中config已经经过了init,不再是你render时候的那个设置方法了,这个需要有一定的了解才能改得对,但是好在在done回调中的第二个参数,就是一个当前这个选中的时间的obj的形式的数据了,所以可以直接给对方的min/max。但是还是翻车了,还有一个更加不容易被发现的细节是这个date还无法直接使用,因为laydate在进入这个回调的时候传过来的时候把month给加了1了,感觉是一次完全没有任何价值的操作个人认为,导致了后面要使用它还得注意自己减掉1。
那么有没有办法给封装一下,让我们后面更容易使用呢?来个整体效果图先。

然后再看看经过render之后里面laydate选择之间的联动效果。


那么这个使用的时候是多麻烦呢?代码如下:

就是这么清爽,添加多一个配置项就可以搞定,rangeType:'divide';就可以将原始的input“分裂”成两个彼此之间息息相关的laydate。
那么laydatePro都做了什么呢?实际就是把各种琐碎的事都揽过来处理了,所以量还是比较多,这里粗略的看一些关键点:
首先是一个缓存laydate实例的机制:

为啥需要这个?这个是因为pro支持同时render一组节点,也就是说不可能还像以前一样,一个render返回一个单一的这个节点的对象,因为有可能render的时候是很多个节点,那么如何得到这个返回呢?这里新增了一个laydate.instance,统一的来记录当前laydate的“实例”原则就是对应的index保存这个这个实例对象,这个index如何得到呢,这个可以可以审查一下被render过的节点,里面会存储一个lay-key这个属性的值就是当前的laydate对应的index。是不是觉得这样子以后会更加方便,以前可能需要自己去定义变量来存储现在基本想要得到某个节点的laydate实例,直接找到laydate.instance[某个dom的lay-key的值],也就是说你只要能找到这个dom就能得到这个laydate实例。这一个修改是分裂的range的一个重要的基础。
继续看关键代码:

代码解析:当遇到是设置分裂的range组件直接走封装的divideRange的方法,不会继续去render当前的这个laydate了。可以理解为舍弃了原始的节点了,新生出来两个,具体新生的内容看下面的代码:

这个就是此次修改最重要的一段啦,做了很多处理,没办法一一解析啦,大概思路就是隐藏掉原始的,然后再后面插入自己定义的分裂的组合,然后给把他们当成两个小laydate修改他们对应的属性包括done回调里面如何去更新它的好兄弟的minmax之类的操作,最后render出来。
然后使用的就是前面看到的清爽的一个配置项的问题啦,为了达到这个一个配置项来决定,后面就得写这么多的代码哈[偷笑] 。测到bug的话希望能得到反馈哈。
8,新增快速选择的两种场景支持。
效果在上面提到了就是新增多两种可快速选择的类型的支持,在默认的range下,类型是date和datetime的可以定义一些快速选择的项,可以用默认给的也可以自己定义:

配合quickConfirm还可以支持点击了快速选择之后会不会立刻把值赋值进去,还是只是圈出来时间范围等用户自行确定这两种不同的效果。

9,新增季度选择的支持。
这个功能背后的实现上有点移花接木的意思,实际上它是month的一个变异,所以你可以render的是设置type:'month',然后追加一个subType:'quarter',当然了为了更方便的使用,我新增了一个类型的支持就是直接设置type:'quarter',后面laydatePro会对应的换成季度的显示和替换一个format,相关的逻辑有:

实际依赖的是一个month的选择器,然后ready和change的时候将month选择给调包了,替换成季度的选择,大概的逻辑如下:

使用的方式:

效果:
然后也有小伙伴问如何使用,这个实际要分几种情况,一个是使用的是laydate独立组件还是layui,然后使用layui的是使用组件化还是非组件化的,对应的在release文件下下面都有,下载适合自己的,因为需要修改一点源码所以替换对应的laydate.js是需要的,里面也提供了最新版本的layui的laydate.js进行修改然后打包出来的,如果使用的就是最新版本的layui理论上直接下载我资源里面的文件替换进去就可以了,费模块化的下载对应的layui.all.js然后如果是独立版本的laydate,需要使用对应的不依赖layui引入的laydatePro.js,详细可以看看测试代码中的相关测试页面的代码是如何使用的。然后如果你现在使用的版本不是layui最新的版本,是不能直接覆盖的,需要自己下载你对应版本的layui的源码然后再源码上把我帖子里面提到的源码修改对应的在自己的代码里面修改一下,然后用gulp打包一下替换到自己的项目里面就可以了。当然最好建议是使用layui的最新版本,毕竟新版本上肯定会修复一些旧版本的问题还有会有更丰富的组件内容。
预告:正如上面提到的多加了一个源码的修改把laydate的构造器给透露出来了,

有两个目的,一个是可以在laydatePro修改部分原来laydate的bug而不用在laydate修改更多代码;第二个是可以进一步优化laydatePro里面的一些逻辑减少一些消耗提升性能。
然后后面还会不断的添加新的功能,比如自定义每一周的开始,目前laydate就是按照国际惯例,周日作为一周的开始,这个没啥不对的也没啥不好的,但是有的人就习惯周一作为开始呢?要显示自然周呢?比如下面的

然后目前里面的格子都是方块的,能不能要一个如上图的圈圈的?更甚者,还有跟奇特的需求,比如遇到一个是要的一个挂号日历啥的,需要的是以今天作为第一天,只能选择当前月的剩下的时间,一般来说设置minmax就好了,但是还要求今天作为显示的第一天这个才是难点所以跟周一作为开始一样的实现原理就能够搞定如下:

这里提到的这个几个效果本测试里面是没有的,这些是以前的帮别人解决问题的时候积累下来的,如果小伙伴们觉得反映哪些是很实用有需要的我后续会尽快的融合进来,当然有什么更好的点子或者有什么很特殊的需求也可以在帖子里面留言,俺也会努力的实现的哈。
谢谢小伙伴们一直以来的认可和支持[嘻嘻]
10,新增可以定义周n作为一周的开始(weekStart)

从注释里面也可以看到,为了使用的方便,可以设置0到6(也就是Date.getDay()对应的值)之外,同时提供了可以设置成对应的日一二三或者Su,Mo,Tu这些。下面简单的介绍一下背后的实现逻辑:
1>在默认的config中添加一个weekStart的初始值


2>在render的时候用正则表达式校验填入的值符合要求不,符合的话设置成对应的0-6如果不符合重置成0


3>到了后面实际渲染这个laydate的时候根据weekStart去生成节点
这里有两个部分,一个是表头的th上显示的

另外一个是实际td里面的内容的


11,支持this标记的背景为圆圈(circleMark)
关于圈圈怎么实现的说说我的思路,一开始觉得直接从css入手,但是看了一下,日期里面实际就是一个表格每天对应的都是一个td里面没有任何子节点了,而且他们是一个长方形的,36*30,如果给强制变成正方形的样子又变得很怪,如果不变方直接改td的border-radius那么就变成一个椭圆的了,而且还有一个更加麻烦的事情就是如果theme使用了gird的话这个就跟grid不符合了,我们关注的是active的这个标记,所以转变一下,实际现在td内部的内容太过简单只有对应的值,如果能埋进去一个div啥的,后面就自由多了,完全可以设置里面的div的样式来达到想要的效果,而不用动table的td的样式,所以还是跟其他的一样添加一个是否需要这个功能的配置,但是这里一开始是准备直接利用theme来处理的,但是又遇到另外一个问题,这里的主题实际怎么说的就是原始的laydate缺少一种多主题的支持,比如原先的支持传一个色值,那么除了这个这个我用另外一个主题来决定是圈圈的,那么怎么同时支持两个主题,综合考虑了之后还是决定不折腾,新增多一个参数:circleMark: ture/false。为啥不让它支持可以类似form的verify一样支持多主体的呢?我的考虑是即使不想要用我的扩展,换回原始的laydate,我希望你写的代码在原始的laydate下也是可以直接跑的,所以如果修改了theme的支持类似‘#ff00ff|circle’这样子的后面不用插件这段代码就会出不来要的主题了。所以修改如下
再生成日历的时候就埋进去一个div

如果设置了circleMark就添加多一个主题class

后面用样式控制

最后还有一个主意的就是如果设置了theme是#开头的颜色的,实际是会生成一个style用#当前laydate的id的控制td的背景颜色,这个优先级很高,直接把circle的给盖过去了,所以在下面这里加多一个限制,如果是circle的做一些其他的处理



最后附上这个测试项目的gitee: https://gitee.com/sun_zoro/laydatePro
在线测试: https://sun_zoro.gitee.io/laydatepro/testLaydate.html
建议对本帖子中效果喜欢的同学可以在码云上star,watch,或者pull后面会不定期的更新更多的效果,比如季度、周等等,或者你有更好的方式或者想要的效果做不出来的都可以跟我交流,当然了这些都是在有空的时候挤时间弄的,而且一个效果一般也是要测试达到一定的稳定程度才会分享出来,所以有时候一个看似挺小的功能也会花上一段时间。
回帖
  • dududu
    2018-10-14
    @岁月小偷 这个年代,真本事能干事的还是要看小偷![嘻嘻]
    3 回复
  • @麟梦 感谢反馈,这个问题实际上是laydate目前的一个bug,可以用一个普通的laydate,然后设置min:1,也就是说最小是明天的这个时候,但是点击弹出的laydate中现在是可以点击的,点击现在之后呢它就直接就用当前的时间了,实际上已经超出了min的范围了,这个是laydate半身存在的bug,以前记得处理过,不过不是本次帖子的关键问题,就没修改laydate修复这个bug了。
    3 回复
  • 干的漂亮····[给力]
    1 回复
  • @爱心 过奖过奖[微笑]
    1 回复
  • La_vida
    2018-10-16
    引用后出现这个错误
    1 回复
  • @La_vida 这个是因为需要修改laydate的源码,看这个图:

    的修改一下laydate的这个地方,添加箭头的这句就可以了。试试看
    1 回复
  • @flyer373 互相学习[嘻嘻]
    1 回复
  • A_彬
    2018-11-4
    这么好的视觉效果,贤心经过作者的同意后,整合到LAYUI上面吧,不用每次升级都要去修改原本的内容,有时忘记了,就出现了大面积的BUG或无效。

    建议每每有人发好的插件或修改过的效果 ,那些必然是比原有的功能要好的东西,不好也没脸发出来给人吐槽是吧,经过作者的同意后,整合到Layui上面,多好的东西,大家伙只要同步更新LAYUI官方的整套框架就能实现最好的效果,不需要自己东找西找的去凑功能,就算凑了功能,下次Layui又升级更新了,上次自己修改过的,在没有做文档的时候,估计都不知道修改过什么,每次修改也很麻烦。同意举牛!
    1 回复
  • Yoga
    2018-10-13
    唉,曾说好一路同行。如今已渐行渐远 。
    0 回复
  • 道理我都懂,我就想知道为什么现在社区的Bug没有理了,https://fly.layui.com/jie/41716/
    0 回复
  • 莫辞
    2018-10-13
    [good]
    0 回复
  • @Yoga who?
    0 回复
  • @Drupal猎人 且不说看过的帖子都回复,你会把社区所有的帖子都看了吗?同理的你的帖子也不可能所有人都看到都去回答,大家时间精力都是有限的,所以这些还是要看缘分的啦,是否有看到是否会之类的。
    0 回复
  • @BUG开发工程师 @莫辞 谢谢认可[微笑]
    0 回复
  • @dududu [哈哈] 还好不是真小偷,要不然太卖力有人也会困扰哈
    0 回复
  • [good] ,改出来的功能都很实用,特别是那个批量渲染,之前我就是循环去一个一个渲染的,比较麻烦!
    0 回复
  • Yoga
    2018-10-14
    @岁月小偷 说的是我与Layui的故事
    0 回复
  • @Yoga 要超车了是不[偷笑]
    0 回复
  • @藏锋入鞘丨 谢谢认可,恩恩,遍历是要遍历的,只是谁干这活哈,假装甩手给laydate.render了[偷笑]
    0 回复
  • 膜拜大神,这些功能都是大大的干货,赞赞赞![赞] [赞] [赞]
    0 回复
  • @菜鸟程序猿 [嘻嘻] [嘻嘻] 感谢认可
    0 回复
  • 爱心
    2018-10-15
    大佬还是那个大佬。。[哈哈]
    0 回复
  • @岁月小偷 批量渲染这个很赞
    0 回复
  • @爱死寂寞人 谢谢认可[微笑]
    0 回复
  • [good]
    0 回复
  • lay初见
    2018-10-15
    不错,分享出来的都是开发当中可能遇到的问题,点赞,像你学习@岁月小偷
    0 回复
  • @飞天神猪 谢谢认可[微笑]
    0 回复
  • @lay初见 互相学习[嘻嘻]
    0 回复
  • 大佬牛逼,向大佬低头。
    0 回复
  • 小魔方
    小魔方 VIP2 (社区之光)
    2018-10-16
    真棒[good] ~~~
    0 回复