赞
踩
目录
需求情景描述
前端使用el-cascader组件来实现一个层级选择器,可以支持单选或多选节点,节点的层级关系和名称都由后端来定义和控制,前端根据后端提供的数据来渲染。
实现该需求,需要后端配合需要提供2个接口:
1.接口1:前端传入指定节点ID,后端返回该节点的下一层所有节点ID信息列表。
2.接口2:前端传入指定节点ID,后端返回该节点从顶部父节点至该节点的所有路径节点ID列表。
级联选择器 el-cascader 使用
最关键的只有这两项:v-model绑定值 和 props 配置项
- <el-cascader
- v-model="IdList"
- :props="props"
- ></el-cascader>
常用props配置:
动态加载选项不需要 : options 配置,一般静态的才使用options配置。
- props: {
- checkStrictly: true, //是否可以选择树干节点作为选项
- lazy: true, // 是否动态加载子节点
- // lazyLoad加载动态数据的方法(仅在 lazy 为 true 时有效)
- lazyLoad: this.loadTreeNode,
- },
loadTreeNode ,自定义的加载节点的方法(具体逻辑看注释基本就能看明白):
- //加载树节点 首次加载页面时就会执行一次,之后每选中一个节点也会调用,来渲染下一层
- loadTreeNode(node, resolve) {
- console.log(node);
- // 首次加载时 node为{root:true,level:0}
- // node 节点数据 获取node的level字段的值
- const { level } = node;
- //下一层节点
- const nodes = [];
- //如果有子节点 或者 为根节点(即首次进入level为0)
- //也有人写成 node.level == 0 作用是一样的
- if (node.hasChildren || node.root) {
- // 0 代表第一次请求
- let nodeId = level == 0 ? null : node.value;
- //这里setTimeout的目的是 显示加载动画
- setTimeout(() => {
- //调用后端接口 获得返回数据
- let ret = this.api(nodeId);
- if (ret && ret.succeeded) {
- //ret.reulst为后端返回的数据
- let nodes = ret.result;
- // 回调渲染下一层
- resolve(nodes);
- } else {
- //后端报错 弹窗提示失败
- this.$message({
- type: "error",
- message: "部门层级加载失败,请联系管理员!",
- });
- }
- }, 1);
- } else {
- //如果没有子节点就不发起请求,直接渲染,也避免了点击叶子节点仍然有加载动画的问题
- resolve(nodes);
- }
- },
叶子节点 —— 意思是:没有子节点的节点,它就是最底层。
其中 this.api 指的是调用后端提供的第一个接口:
接口1:前端传入指定节点ID,返回该节点的下一层所有节点ID信息列表(当nodeId为空时,返回第一层节点)。
返回的数据需要是一个列表,后端返回的数据 List<UserGroupTreeNode> 大概长这样:
JAVA后端的节点定义可参考:
- @Data
- @AllArgsConstructor
- @NoArgsConstructor
- public class UserGroupTreeNode {
- /**
- * 节点值
- */
- private String value;
- /**
- * 节点标签
- */
- private String label;
- /**
- * 是否为叶子节点(即没有子节点) true 没有子节点 false 有子节点
- */
- private Boolean leaf;
- }
其中value即为节点的ID,label为展示的名称,leaf为是否含有子节点,Bool类型。
value,label,leaf 变量名务必保持一致,否则前端拿到数据之后,得自己转成这个变量名。
(或者指定一下变量名称也行,具体参考element-ui官方文档。)
到这里,这个组件已经可以实现新增保存的操作了。
问题描述
上面已经解决了层级选择器的数据渲染问题,用来做查询条件筛选或者新增都已经够了。但是当我们需要修改数据时,点击修改打开的弹窗中对应的字段无法正确回显。
预想中的效果应该是:它应该逐层展示,并且点击它想要修改时,层级选择器组件应该默认选中绑定的值。
目前实际的效果却是:打开后,数据虽然对应上了,但是菜单并没有自动展开,要重新一层一层手动去选择展开,才能看到选中的数据,这显然不符合实际需求。
解决思路:
1.首先,我们在新增数据时,级联选择器v-model绑定的参数是一个列表,
例如:我们选中【sub-1-2-3】这个节点时,
它实际绑定的值是:["root","sub-1","sub-1-2","sub-1-2-3"]。
而后端实际保存的值一般都是选中节点的id,即"sub-1-2-3"。
2.已知el-cascader组件绑定的值是一个列表,所以当我们需要正确回显,也需要给它赋值一个数组才行。
所以我们需要与后端约定的第二个接口
接口2:前端传入指定节点ID,后端返回该节点从顶部父节点至该节点的所有路径节点ID列表。
格式如下图,需要特别注意的是顺序需要和实际节点层级顺序一致!
这里返回的是节点的ID数组列表,
前端拿到它后,直接赋值给 el-cascader 组件v-model绑定的变量即可。
然后就可以正常显示啦!
这样就处理完成了,但是还有一个问题,应该算是element-ui的BUG。
问题情景描述
按照前面的实现后,如果需求是做增删改查,只要涉及到修改,自然需要点击编辑按钮时,在页面中数据回显,才能编辑。但是只有在首次加载页面后,编辑数据A时,数据A回显一切正常,之后关闭弹窗,再编辑其他数据,此时的修改弹窗中el-cascader 组件文本框中,回显都为空,不再正常回显了。
分析原因:
首次编辑数据时,el-cascader 会根据v-model绑定的参数值,
比如["1","2","3","4"] 这个数组
逐层调用lazyLoad获取整个树结构,
即:依次将 "1","2","3","4"作为参数,调用lazyLoad,分别渲染头结点"1",头结点下一层,ID为节点"2"的下一层,以及下一层中,ID为“3”的下一层,最后整个合并起来。
整个过程是组件自动完成的。
所以能正确回显。
但是当关闭编辑弹窗,再编辑其他数据时,虽然el-cascader 组件v-model绑定的参数值发生了变化,但是没有再触发调用lazyLoad方法再逐层请求后端渲染整个结构树。
目前网上的解决办法总体有两种思路:
方法1实现起来太复杂,方法二简单很多,推荐使用方法2。
方法2具体实现:
给 el-cascader组件 加上 v-if="editCascaderVisible"
- <el-cascader
- v-if="editCascaderVisible"
- v-model="IdList"
- :props="props"
- ></el-cascader>
然后在editRow,即打开修改弹窗方法时,手动控制它刷新一次:
关键代码:
- this.editCascaderVisible = false;
- // 这里搞个定时器重新载入一下组件就可以触发组件拉取数据
- setTimeout(() => {
- this.editCascaderVisible = true;
- }, 1);
完整代码:
- editRow(rowData) {
-
- this.editCascaderVisible = false;
- // 这里搞个定时器重新载入一下组件就可以触发组件拉取数据
- setTimeout(() => {
- this.editCascaderVisible = true;
- }, 1);
-
- this.IdList= [];
- //在编辑的时候等到DOM更新完成再赋值
- this.$nextTick(() => {
- //将选中数据的值 赋值给表单
- this.dataForm = Object.assign({}, rowData);
- });
- //调用后端接口,获取自顶向下逐个层级ID的列表 这里我el-cascader绑定的变量是IdList
- this.IdList= this.api_getIdList(rowData.groupId);
- //打开修改弹窗
- this.dialogVisible = true;
- }
这样每次点击编辑的时候,就都能正确回显了。
另外希望element-ui官方能修复这个问题,这样我们也不用折腾了。
需求情景描述
当我们直接点击【万州支行】标签左侧的单选框时,只执行了选中操作,此时不会加载【万州支行】的下属机构,但是实际使用时,我们在点击选中时,又想看到下一级的机构选项怎么办?
如下图所示,因为我们使用的是lazyLoad模式,点击单选框选中时,不会触发加载下一级选项:
解决思路:已知:当选中的节点发生变化时,就会触发change方法,而我们点击选项文字标签(即label)时,组件就会触发加载下一级节点。所以:我们可以在change方法内,模拟点击文字标签,即可解决这个问题。
由于在页面布局上,单选框radio的同级下一个元素,就是label,所以用dom去获取它的同级下一个元素,再手动执行一次click事件即可。
实现代码:
- <el-cascader
- @change="handleChange"
- v-if="editCascaderVisible"
- v-model="IdList"
- :props="props"
- ></el-cascader>
- //处理单选点击事件
- handleChange(e) {
- this.$nextTick(() => {
- const dom = document.getElementsByClassName("el-radio is-checked");
- //这里我把dom打出来看了 最后一个选项才是我选中的节点 即[length-1] 有的博主写的是 第一个元素 即[0] 大家自行尝试
- let radioDom = dom[dom.length - 1];
- const brother = radioDom.nextElementSibling;
- brother.click();
- });
- },
需求情景描述
单选的情况下,要选中某个节点选项,需要点击radio按钮,而radio本身很小,所以它的的可点击区域也很小,想直接点中它,还得眼神好一点才行,不太方便,如果能点击标签页直接选中它就好了。
解决思路:既然点击区域很小,那就用css把它本体放大就好了。把单选框的范围放大到整个标签范围,点击标签相当于点单选框。
实现代码:
- <style>
- /*单选的级联选择器,点击标签页就可以选中*/
- .el-cascader-menu .el-radio {
- display: table;
- vertical-align: middle;
- width: 100%;
- height: 100%;
- position: absolute;
- box-sizing: border-box;
- margin-left: -25px;
- padding-left: 15px;
- margin-top: 6px;
- }
- .el-cascader-menu .el-radio .el-radio__input {
- display: table-cell;
- vertical-align: middle;
- }
- </style>
如果对你有帮助,还请点赞支持 ~
记录一下解决过程中对 el-cascader 组件的其他认识:
<el-cascader ref="editCascader" v-model="departmentIdList" :props="propsSearch" placeholder="请选择业务部门" style="width: 15vw" ></el-cascader>加入ref,可以使用
this.$refs.editCascader.panel
获取到 el-cascader 对象
可以使用
this.$refs.editCascader.panel.lazyLoad(cqbankNode);
主动调用lazyLoad绑定的方法,传入的节点要有chidren字段,或者直接取自
let cqbankNode = this.$refs.editCascader.panel.menus[0][0];
menus是菜单项
menus[0]即第一个下拉框列表,
menus[1]即第二个下拉框列表,可以通过修改menus,修改下拉框展示列。
但是根据 menus[0][0] 去获取,有时候节点children为空,可能应该换一个属性去获取,这个思路应该也能做,当时是取值属性来源错了,为空就写不下去了就换思路了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。