一、介紹:el-tree在element文檔中有查詢全樹的代碼,本文主要是在此基礎上添加了增加、刪除、修改的界面樣式與功能。
二、具體來說:
1、鼠標移動到樹上顯示刪除和修改:
點擊刪除,當前節點刪除;
點擊修改,樹的選中節點變成input可以重新輸入名稱並且右邊出現取消或確認的icon。
2、點擊底部添加按鈕,如果未選中樹節點,則在最外層新增input進行名稱輸入,右邊同樣有取消或確認的icon。若選中樹節點,則在選中節點下添加子節點,同樣以input形式輸入名稱。
2、data里的代碼:
3、節點的數據格式
4、methods里的代碼:
項目要求,最多兩層組:
到這里這個整個代碼就介紹完了,這里還有個坑。
問題:當點擊新增節點,再點擊取消的icon,這時候觀察fetchData方法中兩處this.curNode的打印,會發現打印結果是不一樣的。這個問題其實並不影響樹功能的實現,因為在fetchData中獲取數據以后,進行了this.curNode=undefined。只是出於好奇,為什么會出現這個現象?
分析:觀察cancelUpdate執行后是不是走了其他方法?沒有,也沒有觸發watch。會不會是引用數據類型的地址引用問題?發現沒有。最后發現,是因為點擊取消的時候冒泡了!!觸發了handleNodeClick方法。
解決:
五、demo代碼(以下代碼根據小伙伴需要已經改成不限深度的模板組插入,並把原來深度為2的模板組插入改成了注釋)
<template>
<div>
<el-input placeholder="請搜索" v-model="filterText"></el-input>
<el-tree
:highlight-current="true"
class="filter-tree"
:data="templateTree"
:props="defaultProps"
default-expand-all
:filter-node-method="filterNode"
ref="tree"
@node-click="handleNodeClick"
:expand-on-click-node="false"
:render-content="isUpdateGroup ? updateRenderContent :renderContent"
></el-tree>
<el-button type="primary" @click="handleAddGroup">添加組</el-button>
</div>
</template>
<script>
export default {
name:'tree',
data() {
return {
templateTree: [
//樹的數據
{
id: "1",
text: "模板組1",
nodeId: "11",
depth: 1,
typeName: "模板組",
childrenNum: 2,
nodes: [
{
id: "12",
text: "模板組2",
nodeId: "122",
depth: 2,
typeName: "模板組",
childrenNum: 0,
nodes: [
{
id: "21",
text: "模板組3",
nodeId: "144",
depth: 3,
typeName: "模板",
childrenNum: 0,
nodes:[]
}
]
},
{
id: "13",
text: "模板1",
nodeId: "133",
depth: 2,
typeName: "模板",
childrenNum: 0
}
]
}
],
defaultProps: {
children: "nodes",
label: "text"
},
isact: "", //當前hover的節點
isactTitle: "", //記錄修改節點名稱
curNode: undefined, //當前選中節點
isUpdateGroup: false, //是否在修改模板組
filterText: "",
indexRecord:[],//記錄節點軌跡
isBreak:false,//是否結束循環
};
},
watch: {
filterText(val) {
this.$refs.tree.filter(val);
}
},
methods: {
filterNode(value, data) {
if (!value) return true;
return data.text.indexOf(value) !== -1;
},
renderContent(h, { node, data, store }) {
return (
<span
style="flex: 1; display: flex; align-items: center; justify-content: space-between; padding-right: 8px;"
on-mouseenter={() => this.mouseenteract(data)}
on-mouseleave={() => this.mouseleaveact(data)}
>
<span>
<span>{node.label}</span>
</span>
{this.isact == data ? (
<span>
{this.isact.typeName == "模板組" ? (
<el-button
class="m-r-10"
type="text"
icon="el-icon-edit"
on-click={() => this.handleUpdateGroup(node, data)}
></el-button>
) : (
<span></span>
)}
<el-button
type="text"
icon="el-icon-delete"
on-click={(e) => this.handleDelete(node, data, e)}
></el-button>
</span>
) : (
<span></span>
)}
</span>
);
},
updateRenderContent(h, { node, data, store }) {
return (
<span style="flex: 1; display: flex; align-items: center; justify-content: space-between; padding-right: 8px;">
<span>
{this.isact == data ? (
<input
type="text"
onChange={this.handleChangeTitle.bind(this)}
value={node.label}
/>
) : (
<span>{node.label}</span>
)}
</span>
{this.isact == data ? (
<span>
{this.isact.typeName == "模板組" ? (
<span>
<el-button
class="m-r-10"
type="text"
icon="el-icon-check"
on-click={() => this.updateGroup(node, data)}
></el-button>
<el-button
class="m-r-10"
type="text"
icon="el-icon-close"
on-click={(e) => this.cancelUpdate(node, data, e)}
></el-button>
</span>
) : (
<span></span>
)}
</span>
) : (
<span></span>
)}
</span>
);
},
//獲取鼠標進入節點的數據
mouseenteract(da) {
this.isact = da;
},
mouseleaveact(da) {
this.isact = "";
},
handleNodeClick(pdata) {
this.curNode = pdata;
document
.getElementsByClassName("el-tree-node__content")[0]
.setAttribute("class", "el-tree-node__content");
},
handleDelete(node, data, e) {
e.stopPropagation();
//存在則添加到子級
const parent = node.parent;
const children = parent.data.nodes || parent.data;
//若parent.data是對象,操作的是子級;如果是數組,操作的是最外層
if(Array.isArray(parent.data)){
const parentIndex = parent.data.findIndex(d => d.id === data.id);
parent.data.splice(parentIndex, 1);
}else{
const childIndex = children.findIndex(d => d.id === data.id) ;
children.splice(childIndex, 1);
}
this.curNode = undefined;
},
//新增組
handleAddGroup() {
/**
* 如果模版深度最多兩層,取消該部分注釋
* //最多只有兩層組 不可能添加在dept2的組上 curNode存在 並且 深度超過1
if (this.curNode != undefined && this.curNode.depth != 1) {
this.$message.warning("不能添加超過兩層");
return;
}
*/
//如果isUpdateGroup 已經是true了 說明重復點擊了
if(this.isUpdateGroup){
return;
}
let id = ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
(
c ^
(crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))
).toString(16)
);
let newChild = {
parentId: "", //如果有這個id 是插入第二層 否則是第一層 可有可無
text: "", //必須有 this.templateContent.tempName
nodes: [],
id: id,
typeName: "模板組",
temporaryData: "1" //用來區分臨時數據
};
/* 如果模版深度最多兩層,以下條件改成該部分注釋
this.curNode && this.curNode.depth == 1
*/
this.indexRecord=[]
if (this.curNode) {
if(!this.curNode.nodes){
this.$message.warning('模板不可添加')
return
}
newChild.parentId = this.curNode.id;
/* 如果模版深度最多兩層,以下條件改成該部分注釋
const index = this.templateTree.findIndex(
item => item.id == this.curNode.id
);
this.templateTree[index].nodes.push(newChild);
*/
//找到tree中的index軌跡
this.getTemplateTreeNode(this.curNode.id,this.templateTree, 0)
//按照index軌跡插入節點
this.insertNode(newChild, this.templateTree, this.indexRecord, this.indexRecord.length )
this.isBreak = false
} else if (this.curNode == undefined) {
//沒有選中的時候 添加到最外層
newChild.depth = 1
this.templateTree.push(newChild);
}
//調用出updateRender的input
this.isact = newChild;
this.isUpdateGroup = true;
},
//遞歸遍歷獲得選中node
getTemplateTreeNode(target, list, dept){
//空數組直接返回
if(list.length == 0) return;
let dataLen = list.length;
for(let i = 0; i < dataLen; i++){
//如果不匹配
if(target != list[i].id){
//存在nodes 遍歷nodes里的節點
if(list[i].nodes){
this.indexRecord[dept] = i
let recordDept = dept+1
this.getTemplateTreeNode(target,list[i].nodes, recordDept)
}else{
//不存在nodes 繼續遍歷
continue;
}
}else{
//匹配,則修改下標數組
this.indexRecord[dept] = i
this.isBreak = true
break
}
//刪除不匹配的軌跡 如果已經break了說明已經找到正確的節點,就不用再刪了
if(!this.isBreak){
this.indexRecord.pop()
}
}
},
//插入節點
insertNode(insertChild, tree, indexArr, len){
let index = indexArr.length - len
if(len == 0) {
tree.push(insertChild)
} else{
this.insertNode(insertChild, tree[indexArr[index]].nodes, indexArr, len-1)
}
},
//修改組
handleUpdateGroup() {
this.isUpdateGroup = true;
},
//修改組名時獲取title
handleChangeTitle(e) {
let value = e.target.value;
this.isactTitle = value;
},
updateGroup(node, data) {
//先handleChangeTitle獲取title 再調用
setTimeout(() => {
if (this.isactTitle.trim() == "") {
this.$message.warning("名稱不能為空");
return;
}
//修改數據組
this.isUpdateGroup = false;
const parent = node.parent;
const children = parent.data.nodes || parent.data;
const index = children.findIndex(d => d.id === data.id);
let temp = data;
temp.text = this.isactTitle;
children.splice(index, 1, temp);
}, 500);
},
cancelUpdate(node, data, e) {
this.$message.info("已取消");
this.isUpdateGroup = false;
//如果是插入操作 需要移除數據
if (this.isact.temporaryData) {
this.handleDelete(node, data, e);
}
}
}
};
</script>
<style lang="scss" scoped>
.el-tree-node__content {
.el-button {
display: none;
}
}
.el-tree-node__content:hover {
.el-button {
display: inline;
}
}
.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content {
background-color: #eaebed;
color: #4796ec;
font-weight: bold;
}
.el-tree {
height: 350px;
overflow-y: auto !important;
.el-tree-node__content span {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
}
</style>
————————————————
版權聲明:本文為CSDN博主「川上餃子」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/outlierQiqi/article/details/106858900