1、dom元素
<el-tree
ref="tree"
:data="treeData"
node-key="type_id"
:highlight-current="true"
:default-expand-all="false"
draggable
@node-drag-end="compose"
@node-click="handleNodeClick"
:allow-drag="allowDrag"
>
<!--node-key="id"-->
<template slot-scope="{ node, data }">
<div>
node--{{node}}
data--{{data}}
</div>
</template>
</el-tree>
2、相关方法
/*
拖拽组合
draggingNode 要插入到dropNode
dropType 拖拽类型
*/
compose(draggingNode, dropNode, dropType, ev) {}
//判断节点能否被拖拽
allowDrag(draggingNode) {
// return draggingNode.data.label.indexOf('三级 3-2-2') === -1
return true
}
//当前目录树节点被点击
handleNodeClick(data, node) {
}
//节点高亮
this.$refs.tree.setCurrentKey(node.type_code);
this.handleNodeClick(node, this.$refs.tree.getNode(node));
3、递归循环相关操作
(1)递归遍历目录树并删除指定id的节点
// 递归遍历目录树并删除指定id的节点
deleteNodeById(treeData, targetId) {
for (let i = 0; i < treeData.length; i++) {
const node = treeData[i];
// 如果当前节点的id符合目标id,则删除该节点
if (node.id === targetId) {
treeData.splice(i, 1);
i--; // 由于删除了一个元素,需要减少索引以避免跳过下一个元素
continue;
}
// 如果当前节点有子节点,则递归调用该函数
if (node.children && node.children.length > 0) {
this.deleteNodeById(node.children, targetId);
}
}
},
(2)遍历目录树修改属性值
//遍历目录树修改属性值
updateLabel(tree, key, value) {
for (let i = 0; i < tree.length; i++) {
const node = tree[i]
if (node.id === this.dataId) {
this.$set(node, key, value)
break // 结束循环,因为我们已经找到并更新了目标对象
}
if (node.children) {
this.updateLabel(node.children, key, value) // 递归调用以处理子节点
}
}
},
//同理,可对代码进行修改以达到向目录树该节点下添加文件的目的
//添加文件,步骤1:this.$set(this.curNode, "childrens", []);
//添加文件,步骤2: this.curNode.childrens.push({文件对象})
//添加文件,步骤3:其他代码根据实际情况进行修改
//使用:
this.updateLabel(this.treeData, 'isTrue', false)
(3)通过目录树名称的模糊搜索,展开包括该名称的(多个)目录节点
注:node-key=“type_id” 目录树唯一值的添加,在我的项目中因为我的id并不能作为唯一值去判定,所以自己给每个node增加了唯一值去确定展开哪个node节点
<el-input
class="search"
type="text"
v-model="searchStr"
@keyup.enter.native="goToSearch"
placeholder="请输入节点名称搜索"
></el-input>
data(){
return{
expandedKeys: [], //所有treenode的type_id
needExpandedKeys: [], //需要展开的treenode的type_id数组
needExpandedNodes:[],//需要展开的treenode的node数组
searchStr:"", //搜索,搜索目录树节点名称
}
}
watch: {
searchStr:_debounce(function () {
this.goToSearch()
}),
}
//目录树增加唯一值
addTypeIdToTreeNode(lastList) {
//传进去的list是有tree又有tree节点下的文件file
//给每个node节点添加_id,用来展开目录设置唯一值
let treeData = lastList;
const addIdToTree = (treeData) => {
return treeData.map((node, index) => {
//因为我的项目目录树节点存在_id ,所以在这里需要判断
if (!node._id) {
const newNode = {
...node,
type_id: node.code == 0 ? "xxx" : node.code,
}; // 创建一个新的节点,包括原有的属性和新的 _id 属性
if (node.childrens && node.childrens.length > 0) {
newNode.childrens = addIdToTree(node.childrens); // 递归处理子节点
}
return newNode;
} else {
const newNode = { ...node, type_id: node._id }; // 创建一个新的节点,包括原有的属性和新的 _id 属性
// if (node.childrens && node.childrens.length > 0) {
// newNode.childrens = addIdToTree(node.childrens); // 递归处理子节点
// }
return newNode;
// return node;
}
});
};
const treeDataWithId = addIdToTree(treeData);
//获所有节点的type_code数组,为了pageNum()跨目录的目录展开
// let treeDataArr = this.treeDataList;
let str = [];
const getStr = function (list) {
list.forEach(function (row) {
// if (row.childrens && row.childrens.length > 0) {
if (row.childrens) {
str.push(row.type_id);
getStr(row.childrens);
} else {
str.push(row.type_id);
}
});
};
getStr(treeDataWithId);
this.expandedKeys = str;
// console.log("需要展开的treenode", this.expandedKeys);
// console.log("需要展开的treeDataWithId", treeDataWithId);
return treeDataWithId;
},
//搜索
goToSearch(){
let treedata = this.treeData
if(this.searchStr){
//需要关闭所有节点 //除了上次搜索展开的节点还有自己点击展开的节点
this.changeTreeNodeStatus(this.$refs.tree.store.root)
this.needExpandedNodes = []
this.needExpandedKeys = []
//获取需要展开的节点数组
this.findTypeCode(treedata, this.searchStr)
this.needExpandedNodes.forEach(item=>{
this.needExpandedKeys.push(item.type_id)
})
if(this.needExpandedKeys.length == 0){
this.$message.error("没有找到您搜索的目录节点")
}else{
//模拟点击该节点,使其高亮
this.handleNodeClick(this.needExpandedNodes[0],this.$refs.tree.getNode(this.needExpandedKeys[0]))
}
console.log("needExpandedKeys",this.needExpandedKeys)
}else{
this.changeTreeNodeStatus(this.$refs.tree.store.root)
this.needExpandedNodes = []
this.needExpandedKeys = []
}
},
//循环拿到需要展开的目录子节点
findTypeCode(treeData, targetName) {
// 遍历树结构
for (let i = 0; i < treeData.length; i++) {
const node = treeData[i];
// 如果节点的 type_name 包含目标名称,返回该节点的 type_code
if (node.type_name.includes(targetName)) {
// if (node.type_name==targetName) {
console.log(node.type_id)
if(node.type_id){
this.needExpandedNodes.push(node)
}
}
// 如果节点有子节点,递归调用自身进行深度优先搜索
if (node.childrens && node.childrens.length > 0) {
const result = this.findTypeCode(node.childrens, targetName);
// 如果在子树中找到了匹配的节点,返回结果
if (result) {
return result;
}
}
}
// 如果没有找到匹配的节点,返回 null 或者适合您的默认值
return null;
},
changeTreeNodeStatus(node) {
node.expanded = false
for (let i = 0; i < node.childNodes.length; i++) {
// 改变节点的自身expanded状态
node.childNodes[i].expanded = this.defaultExpand
// 遍历子节点
if (node.childNodes[i].childNodes.length > 0) {
this.changeTreeNodeStatus(node.childNodes[i])
}
}
},
4、css样式修改
/* 高亮显示 */
/deep/.el-tree-node:focus > .el-tree-node__content {
background-color: rgba(135, 206, 235, 0.3);
color: #409eff; //节点的字体颜色
}
/* 有子节点 且未展开 */
::v-deep .el-tree .el-icon-caret-right:before {
background: url('../../assets/images/editor-drag/icon_layer_right.png') no-repeat 0 3px;
content: '';
content: '';
display: block;
width: 8px;
height: 16px;
font-size: 8px;
background-size: 8px;
}
/* //有子节点 且已展开 */
::v-deep .el-tree .el-tree-node__expand-icon.expanded.el-icon-caret-right:before {
background: url('../../assets/images/editor-drag/icon_layer_down.png') no-repeat 0 3px;
content: '';
display: block;
width: 8px;
height: 16px;
font-size: 8px;
background-size: 8px;
}
/* 没有子节点 */
::v-deep .el-tree .el-tree-node__expand-icon.is-leaf::before {
background: url('') no-repeat 0 3px;
content: '';
display: block;
width: 8px;
height: 16px;
font-size: 8px;
background-size: 8px;
}
::v-deep .el-tree-node__expand-icon.expanded {
transform: rotate(0deg);
}
::v-deep .el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content {
background-color: #ffefd4;
}