【需求说明】MOM项目开发中,有一个角色管理的页面,涉及到对具体的角色进行授权的功能。要实现的功能是,不仅要对这个角色授权指定的几个页面,还要能授权该页面上的某些按钮,以及页面中的某些数据。数据的授权,就是允许该角色下的用户,拥有查看指定数据的授权,或者对满足一定条件的一类数据拥有查看权限。对数据的授权,我们称为数据授权。
比如角色role1,已经赋予了人员管理页面的权限,以及人员管理页面新增按钮的权限。所谓数据授权,就是要将人员管理页面中人员详情的表格数据,有选择性的授权给role1角色查看。比如将工号小于00123456的人员数据,授权给角色role1。这样角色role1下的用户,登录系统后,就能看到人员管理页面,以及人员管理页面的新增按钮,当然,也能看到工号小于00123456的职员信息数据。
数据授权的具体实现,是在对角色授权时,通过设置一定的过滤条件,对页面中一类数据进行批量授权的功能。比如对角色Role1进行数据授权时,只允许Role1查看页面Page1(如下图)的表格中,用户名包含srx的数据,如下图所示。这样,Role1下的用户登录进来后,只能看到第一条包含srx的数据,看不到第二条数据。
【需求分析】该功能实际上就是将SQL的查询条件在WEB端界面化了。本来多个查询条件并列在界面上,也没有什么难度,但是,由于领导坚持认为现有的办公工具TFS上,bug单的过滤条件支持分组的功能非常好用,所以,他要求数据授权的查询条件,也要支持条件分组功能。如下图所示,是TFS上的bug单分组的界面。
【开发人员的内心独白】
条件分组,实际上就是给SQL语句加小括号的意思么,可以按照上图,设置每一个查条件,但是,不分组,用另外的textarea替代,让用户将上面配置的多个条件,通过加减乘除等符号,自由组合,灵活多变,想怎么配就怎么配,分组只能实现简单的场景;实际上就一句话,要实现分组,太南啦~~
【方案分析】
既然说服不了领导,那就开发吧,领导的高度,或许在我开发完了才能明白。
要开始开发了,我分享下我自己的思考过程,还原下当时的场景。
首先要考虑左侧的分组竖线如何画。跟同事讨论后,他建议左侧的竖线放在一个浮动层,右侧始终是一个并列的对象数组。意思是,竖线是竖线,条件是条件,条件代表的是数据,竖线代表了数据的分组关系。可是,所有的方案,最后都要抽象为数字模型。问题来了,竖线代表的关系,抽象为怎样的数据模型呢?又怎么跟过滤条件的数据对应起来呢?具体到前台开发,就是要思考给后台提交的数据格式,以及前台根据返回的数据,如何还原用户画的竖线关系。任务不是分给同事的,他可能不会深入思考这个问题,所以,他的建议不可取。至少他还没有深入到分析出来数据模型来。
跟后台商讨接口时,后台人员给出了一个带有groups属性的对象,看到这个对象,我恍然大悟,我一直纠结条件分组如何在界面表现,实际上他们就是一个树形结构的数据关系。如何标记多个条件属于一个小组,实际上就是树形结构中的children(我们这里定义为groups),带有children的条件,跟其他条件又构成一个大组。完成树形数据的层层嵌套,而且树形结构的数据,能完全还原用户界面上的设置,不会导致条件数据的顺序发生改变。当然,后台定义的带有groups的对象是不合理的,树嘛,应该是对象数组,便于递归查找。我们定下来数据结构是这样的。当然,id,parentId,floor都是后来慢慢增加的,用来满足查找树上的节点数据而产生的。
抽象模型解决后,方向是明确的了,分析的过程就完成一大半了。接下来,要考虑的3个问题是:
问题1、分组线条的实现。数据结构搞明白后,划线其实就比较容易了,渲染带groups的数据时,先左右各一个div,左边的小div,就是一个带有上下左三边边框的div,模拟划线;右边的div,就是这一组的的条件。当然这个要通过递归实现树的渲染。
问题2、如何判断用户随便选择的几个条件,能否合法的划分为一个组。事实证明,判断当前勾选的条件能否划分为一个组,是后续的实现过程中,逻辑最复杂的一部分。合法分组的依据是,同属于一个groups下的至少两个条件,才能划分为一个组;如果groups下包含子groups,则子groups整体作为一个条件,要么都选择,要么都不选择,这两者都是合法的,部分选择则不合法。实际上,判断能否分组,也是让我绞尽了脑汁。具体的实现算法是,将所选择的多个条件的父id都找出来作为一个数组,如下图,我勾选了四个条件,则就有四行数据,每一行代表一个勾选条件数据的所有父id。-1表示是根节点了,这样就能找出-1是所选条件的公共父id,这样把最小父id找后,父id的groups中的所有数据就能找到。判断改groups中,有几个条件被选中,至少两个就是合法的,如果包含子的groups,则子groups全选代表选中,不全选代表不选中,部分选中的话,都不用往下判断了,直接就不合法,不能分组。
问题3、如何判断用户随机选择的几个条件,能合法的取消分组。合法取消分组的依据是,所选择的所有条件,要同属于一个groups组,这时取消分组的按钮才可使用,否则取消分组的按钮只能置灰。取消分组的算法,也是先找到最小的公共父id,然后看父的groups中的数据是否全部选中,如果选中,则可以取消分组
问题4、每一个组,都需要一个三边有border的div来表示,宽度为d个像素,这样右侧的div就会往右移动d个像素,多层嵌套后,右侧条件是无法对其的,如下图所示。
此时数据上带的floor就该上场了。右侧的条件div,强制往左移动floor*d个像素,floor是当前条件所在的层数。如下图的style所示。
这样对齐问题也就解决了。如下图所示。
到此,基本工作算是完成了。注意:
1、只能以最大的组为单位再次分组
2、删除一个条件后,如果此时分组内只剩下最后一个条件,要解散当前的分组,这最后一个条件的floor要减1
3、每次分组后,这一组的数据的floor必须加1
【学会的东西】
1、vue数据驱动的思想:当我想要删除界面上的一个条件时,我首先想的是如何删除条件对应的dom元素以及相应变量的销毁。思考过后发现,vue数据驱动的思想,之前我没有完全领悟其中的奥妙,此时正是用它的时候。我只要保证对数据的操作正确,其他都交给vue就好了。我把树形数据的相应条件数据删除,vue自动重绘我的数据,相应的那一个条件的dom条件也就没有了。
2、组件销毁不需要手动执行。界面上销毁一行条件,只能数据驱动,意思是想办法删除这一条数据后,组件自己会跟着销毁
3、当然,上述是理想状态下的。实际上,数据改变时,组件重新渲染时出了问题。删除一个条件,界面上看到的是,条件的数量确实少了一个,但是少的哪一个不是我删除的,意思是确实重绘了,但是条件的具体值没有重新加载。比如我删除了下图的第一行数据,正常情况下,只剩下值为bb和cc的数据了,实际情况是,cc消失了,aa和bb都存在。确认数据处理没有问题后,我就怀疑是vue重绘时出错了,重新绘制了,但是传参有问题。
百度出了一个非常有用的处理方法,如下所示,我要监听我的树形数据的变化,一旦改变,则通过v-if销毁组件,在vue自动重绘后,我再让v-if为true,相当于强制重新绘制了一变。这样能确保界面上所绘制的,一定来源于我的真正的数据。
4、我自己会开发一个树组件了。各个组件库的树组件要做的工作,跟我完成的工作是相同的,第三方树组件,再也不那么神秘了。
5、学会使用vue组件的递归,以及理解vue实例中name属性的用途了。树组件的实现,要用到vue的组件递归(自己调用自己,组件name的用处)
6、读取html上的dom元素,组织成树形json数据。这个想法是错误的,多想想vue能帮我做什么,省去我操作dom的工作。对数据驱动理解不够深刻,加上被递归弄迷糊了,其实,dom的层级关系,抽象成数学模型,就是树结构的数据,dom就是树形数据的呈现
7、监听数组变化,需要增加deep: true属性,深度监听。如下图所示
8、监听字符串,需要增加immediate: true
【遗留问题】
1、子组件修改父组组件传递的props后,父组件中值会不会改变?
答案:会。但是vue会报错,单向数据流禁止这样的操作。正确的方式式,子组件通过$emit触发父组件自己内部修改。形成一个可控的数据流。