vue項目 el-tree的界面自定義 實現增刪改查操作


一、介紹:el-tree在element文檔中有查詢全樹的代碼,本文主要是在此基礎上添加了增加、刪除、修改的界面樣式與功能。

二、具體來說:

1、鼠標移動到樹上顯示刪除和修改:

  點擊刪除,當前節點刪除;

  點擊修改,樹的選中節點變成input可以重新輸入名稱並且右邊出現取消或確認的icon。

2、點擊底部添加按鈕,如果未選中樹節點,則在最外層新增input進行名稱輸入,右邊同樣有取消或確認的icon。若選中樹節點,則在選中節點下添加子節點,同樣以input形式輸入名稱。
image
2、data里的代碼:
image
3、節點的數據格式
image
4、methods里的代碼:
image
image
image
image
image
image
image
項目要求,最多兩層組:
image
image
到這里這個整個代碼就介紹完了,這里還有個坑。

問題:當點擊新增節點,再點擊取消的icon,這時候觀察fetchData方法中兩處this.curNode的打印,會發現打印結果是不一樣的。這個問題其實並不影響樹功能的實現,因為在fetchData中獲取數據以后,進行了this.curNode=undefined。只是出於好奇,為什么會出現這個現象?
image
分析:觀察cancelUpdate執行后是不是走了其他方法?沒有,也沒有觸發watch。會不會是引用數據類型的地址引用問題?發現沒有。最后發現,是因為點擊取消的時候冒泡了!!觸發了handleNodeClick方法。
解決:
image
image
五、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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM