最近做了一个类似于目录树的一个功能,
- UI框架:ElementUI
- Vue: vue2
- 如下展示的是代码的实现思路和过程
需求如下:
1.树形左侧展示的是最顶层的name,右侧展示的是它所有的子集
2.最右侧的"已选多少项"展示的是选中的子集和它的父级,如果选中的子集有多项 则放入相同的父级下,删除子集同步取消左侧的选中状态
3.最后选中的数据保存的时候 需要跟源数据进行对比,去除未选中的子集和父级,一直找到根节点
- 思路:
1.首先数据已经确定,树形结构数据。
2.其次我要进行将子集中的父级置为不可选的状态,还要考虑到父子不关联,我想到用 Element ui树型结构中props去处理 将所有children不为空的数据进行disabled 禁用掉。
3.接下来需要处理的是子集选中并放入某一个空数组并用于展现右侧已选中的视图
4.数据最后的保存采用递归进行对比,排除未选中的数据,大体思路就是这样
下面展示的图片是初始状态
展示代码:
首先 "我的数据"代码:
js
<div >
<div
v-for="(lfdata, lfindex) in cdlist" :key="lfindex">
checked:用于选中状态
<span :style="{ backgroundColor: lfindex == checkcd ? '#2C76E4' : '', color: lfindex == checkcd ? '#fff' : '#000' }" @click="lfclick(lfindex, lfdata)">{{lfdata.name}}</span></div>
</div>
<div class="defaultProps">
<el-tree :data="datadata" show-checkbox default-expand-all node-key="code"
ref="tree" highlight-current :filter-node-method="filterNode":props="defaultProps" @check="getdatalist"></el-tree>
</div>
"已选多少项"代码:
js
<div><div style="display: flex;justify-content: space-between;padding:10px;">
<p>已选<span >{{ rightlength}}</span>项</p> <span v-if="allchildcode.length > 0 " @click="closeAll">清空</span></div>
<div>
<ul v-if="filterParent.length > 0"><li class="ivu-timeline-item" v-for="(item, index) in filterParent" :key="index"><div class="ivu-timeline-item-tail">
</div><div v-if="(index + 1) != filterParent.length"></div>
<div class="ivu-timeline-item-content" ><div ><div>{{ item.name }} </div>
<Tag v-for="item1 in item.children" :key="item1.code"
:name="item1.code"
color="primary" @on-close="tagclose(item1)">
<Tooltip :content="item1.name" placement="top-left">
{{ item1.name }}</Tooltip>
</Tag>
</div>
</div>
</li>
</ul>
<ul v-else style="text-align: center;">
暂无数据
</ul>
</div>
</div>
事件代码如下:
js
该方法是选中子集进行数据提取并放入新数组,进行了数据的处理
getdatalist(nodeObj, nodeState) {
let isCheck = this.$refs.tree.getCheckedNodes().indexOf(nodeObj) > -1;
if (isCheck) {
this.findDataById(nodeObj.parentId, this.datadata, 'result')
this.filterparentcode(this.result, nodeState.checkedNodes, this.filterParent)
} else {
该方法是同步取消右侧已选数据
this.tagclose(nodeObj)
}
},
//递归查找parentid
findDataById(id, data1, result) {
this[result] = []; // 清空上一次的结果
const traverse = (data) => {
for (const item of data) {
if (item.code === id) {
this[result].push(item);
}
if (item.children) {
traverse(item.children);
}
}
};
traverse(data1);
},
// 递归查找所有的父节点code,根据父节点code将子集放入children
filterparentcode(a, b, c) {
let data = JSON.parse(JSON.stringify(a[0]))
let arr = [];
b.forEach((item, index) => {
if (item.parentId == data.code) {
arr.push(item)
}
});
data.children = arr;
if (c.length > 0) {
let dataparentindex = "";
c.forEach((item, index) => {
if (item.code == data.code) {
dataparentindex = index;
}
});
if (/^[0-9]+.?[0-9]*$/.test(dataparentindex)) {
c[dataparentindex].children = data.children;
} else {
c.push(data)
}
} else {
c.push(data)
}
},
右侧 取消事件
tagclose(event, type) {
this.$refs.tree.setChecked(event.code, false, false);
this.checkedNodesdata = this.findLeafNodes(this.$refs.tree.getCheckedNodes())
this.checkedNodesdata = [...new Set(this.checkedNodesdata)]
let parenta = this.findParentNode(JSON.parse(JSON.stringify(this.quanxuandata)), this.checkedNodesdata);
parenta = [...new Set(parenta)]
console.log(parenta);
let parentall = this.regroup(parenta, JSON.parse(JSON.stringify(this.checkedNodesdata)))
this.filterParent = parentall;
}
//查找叶子节点
findLeafNodes(tree) {
const leafNodes = [];
// 递归函数,用于遍历树节点
const traverseTree = (node) => {
if (!node.children || node.children.length === 0) {
// 没有子节点,将当前节点添加到叶节点数组中
leafNodes.push(node);
} else {
// 有子节点,继续遍历子节点
node.children.forEach(traverseTree);
}
};
// 遍历整个树结构
tree.forEach(traverseTree);
return leafNodes;
},
//查找父级节点
findParentNode(tree, parentId) {
if (tree.code === parentId) {
return tree;
}
if (tree.children) {
for (let i = 0; i < tree.children.length; i++) {
const parent = this.findParentNode(tree.children[i], parentId);
if (parent) {
return parent;
}
}
}
if (Array.isArray(tree)) {
for (let i = 0; i < tree.length; i++) {
const parent = this.findParentNode(tree[i], parentId);
if (parent) {
return parent;
}
}
}
return null;
},
"已选多少项"最后数据结构如下:
js
data: [
{ id: 1, label: '一级 1', children: [{ id: 4, label: '二级 1-1', children: [{ id: 9, label: '三级 1-1-1' }, { id: 10, label: '三级 1-1-2' }] }] }, { id: 2, label: '一级 2', children: [{ id: 5, label: '二级 2-1' }, { id: 6, label: '二级 2-2' }] }, { id: 3, label: '一级 3', children: [{ id: 7, label: '二级 3-1' }, { id: 8, label: '二级 3-2' }] }
],
最后在进行跟源数据对比去除未选中的数据:
js
数据提取 提出来所有的code
findAllParentNodeCodes(tree, childNodeCode, options = {}) {
const { children = 'children', code = 'code' } = options;
let result = [];
// 递归函数,查找所有父节点code和选中子节点code
const findNodeCodesRecursive = (node, parentCodes = []) => {
if (node[children]) {
for (let i = 0; i < node[children].length; i++) {
const childNode = node[children][i];
if (childNode[code] === childNodeCode) {
result = [...parentCodes, node[code], childNodeCode];
return true; // 已找到目标节点,返回true
} else {
if (findNodeCodesRecursive(childNode, [...parentCodes, node[code]])) {
return true; // 已找到目标节点,返回true
}
}
}
}
return false; // 未找到目标节点,返回false
};
// 查找根节点
for (let i = 0; i < tree.length; i++) {
const node = tree[i];
if (!node[children] && node[code] === childNodeCode) {
return [node[code]]; // 子节点就是根节点,返回只包含根节点code的数组
}
if (findNodeCodesRecursive(node)) {
return result;
}
}
return result;
},
过滤后最终得到的数据
filterTreeByArray(tree, codeArray) {
const childrenKey = 'children';
// 递归函数,过滤非目标节点和子节点
const filterNode = (node) => {
if (codeArray.includes(node.code)) {
if (node[childrenKey]) {
node[childrenKey] = node[childrenKey].filter(filterNode);
return true; // 返回true表示保留当前节点
}
return true;
}
return false; // 返回false表示过滤掉当前节点
};
// 遍历整个树结构,并返回过滤后的树
return tree.filter(filterNode);
},
以上就是整个业务代码,欢迎大家提出自己更好的建议。