Element-UI 自定义Tree组件

背景

根据业务修改element tree 组件,实现增删改节点时不收起节点不需要重新渲染整个树组件

源码

<template>
  <div>
    <el-tree
      :props="props"
      :load="loadNode"
      :data="setTree"
      @node-click="clickFn"
      lazy
      ref="tree"
    >
      <span class="custom-tree-node" slot-scope="{ node, data }">
        <span class="label">{{ node.label }}</span>
        <span>
          <template v-for="(item, index) in methodName">
          <template v-if="data.unIcon">
          <i
            :class="'el-icon-' + item"
            @click.stop="() => update(node, data, item)"
            style="margin-right: 10px"
            :key="index"
            v-if="!data.unIcon.includes(item)"
          ></i>
          </template>
          <template v-else>
          <i
            :class="'el-icon-' + item"
            @click.stop="() => update(node, data, item)"
            style="margin-right: 10px"
            :key="index"
          ></i>
          </template>
          </template>
        </span>
      </span>
    </el-tree>
  </div>
</template>
<script>
import Element from "element-ui";
export default {
  props: {
    getTreeData: [Function],
    addFn: [Function],
    editFn: [Function],
    method: [Array, Object]
  },
  computed: {
    methodName() {
      let arr = [];
      for (let k in this.method) {
        arr.push(k);
      }
      return arr;
    }
  },
  data() {
    return {
      setTree:[],
      oneTree: [],
      props: {
        label: "name",
        isLeaf: "leaf"
      },
      currentModel: false
    };
  },
  methods: {
    clickFn(data,node,dom) {
      this.$emit("getNode", {node: node, type: 'click'})
    },
    async loadNode(node, resolve) {
      //判断一级 二级
      let data =
        node.level === 0
          ? {}
          : {
              type: node.level,
              id: node.data.id
            };
      let list = await this.getTreeData(data);
      if(node.level === 0) {
        this.oneTree = list;
      }
      return resolve(list);
    },
    async update(node, data, type) {
      this.$emit("getNode", {node: node, type: type})
      this.currentModel = true;
      let type_title = ""; // 弹窗标题
      let type_text = ""; // 弹窗内容
      let defaultData = {};
      switch (type) {
        case "add":
          type_title = "新增";
          //填充默认值
          defaultData = {
            inputPlaceholder: `请输入${data.title}`
          };
          break;
        case "edit":
          type_title = "编辑";
          //填充默认值
          defaultData = {
            inputPlaceholder: `请输入${data.title}`,
            inputValue: data.name
          };
          break;
        case "delete":
          type_title = "删除";
          //填充默认值
          type_text = `确定要删除${data.name}${data.title}?`;
          break;
      }
      const MessageBoxType = type == "delete" ? "$confirm" : "$prompt"; // 判断弹窗类型
      this[MessageBoxType](
        type_text,
        `${type_title}${data.title}`,
        defaultData
      ).then(async ({ value }) => {
        // console.log('>>>>>>>', value)
        let d = {
          type: node == null ? 1 : node.level,
          id: data.id,
          name: value
        };
        // 新增
        if (type == "add") {
          if(node == null) {
            delete d.type;
          }
          let res = await this.method.add(d);
          let newChild = { id: res.id, name: value, children: [] };
          if(res.unIcon) { //icon
             newChild.unIcon = res.unIcon
          }
          if(res.leaf) { // 是否有子集
             newChild.leaf = res.leaf
          }
          if(res.title) { // title
             newChild.title = res.title
          }
          if (!data.children) {
            this.$set(data, "children", []);
          }
          console.log('>>>>>>11111222', data)
          if(node == null){
            // console.log('>>>>>', node)
            this.setTree = this.oneTree
            this.setTree.push(newChild)
          } else {
            // console.log('>>>>>', node)
            this.$refs['tree'].append(newChild,node);
          }
          return false;
        }
        // 编辑
        if (type == "edit") {
          await this.method.edit(d);
          data.name = value;
          return false;
        }
        // 删除
        if (type == "delete") {
          delete d.name;
          await this.method.delete(d);
          const parent = node.parent
          this.$refs['tree'].remove(node);
        }
      });
    }
  }
};
</script>
<style scoped>
/deep/.custom-tree-node {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 14px;
  padding-right: 8px;
}
/deep/.el-tree-node__content {
  display: flex;
  align-items: center;
  height: 26px;
  cursor: pointer;
  margin: 8px 0;
}
/deep/.el-icon-add:before {
  content: "\e783";
}
/deep/.el-icon-edit:before {
  content: "\e764";
}
.label{
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: inline-block;
  max-width: 145px;
}
</style>

使用方式

getNode方法为自定义组件返回的点击的node事件,用于判断当前点击的某个node,及事件类型:add,edit,del, click;

<xtAdminTree
    @getNode="handleNodeClick"  //返回点击的节点
    :getTreeData="getTreeData" //获取数据进行渲染
    :method="{
            add: addSubjectFn, //新增方法
            edit: editSubject, // 编辑方法
            delete: deleteSubject, //删除方法
          }"
    ref="tree"
/>

渲染方法

getTreeData(data) {
      return new Promise((resolve, reject) => {
        console.log("获取层级数据", data);
        this.openType = data.type ? data.type : 0;
        console.log("this.openType", this.openType);
        if (data.type && data.type > 0) {
          data.campus_id = data.id;
          delete data.id;
          delete data.type;
        }
        console.log("获取列表参数", data);


        campusList(data).then((res) => {
          if (res.code == 1) {
            this.listLoading = false;
            let list = res.data;
            list = res.data.map((item) => {
              return {
                id: item.id,
                name: item.name,
                leaf: this.openType == 1,
                title: this.modelLabelList[this.openType].inputLabel,
                unIcon: this.openType == 1 ? ["add"] : [],
              };
            });
            resolve(list);
          } else {
            this.$message.error("获取校区数据异常");
            resolve([]);
          }
        });
      });
    },

点击节点事件

//点击树节点的事件 , 点击事件如果不是click,需要动态传入模态框的title,用于弹框显示不同文字类型
    handleNodeClick(data) {
      console.log("node点击事件", data);
      // console.log("checked", checked);
      if (data.type == "click") {
        if (data.node.level == 2) {
          this.listQuery.page = 1;
          this.isTableTitle = true;
          this.tableTitle = data.node.data.name;
          this.tableNode = Object.assign({}, data.node);
          this.getTableByID(data.node);
        }
      } else if (data.type == "add") {
        console.log('data.node',data.node)
        if(data.node != null){
          data.node.data.title = this.modelLabelList[data.node.level].inputLabel
        }
      } else if (data.type == "edit") {
          data.node.data.title = this.modelLabelList[data.node.level-1].inputLabel
      }
    },

method方法为新增,编辑,删除的方法,返回一个promise

//新增
 addSubjectFn(data) {
   console.log("新增data", data);
   this.openType = data.type ? data.type : 0;
   return new Promise((resolve, reject) => {
     if (!data.name || data.name == "") {
       this.$message.error("提交名称不得为空,请输入后再次提交");
       reject();
     } else {
       if (data.type && data.type == 1) {
         data.campus_id = data.id;
       }
       delete data.type;
       delete data.id;
       addCampus(data).then((res) => {
         // console.log("this.openType", this.openType);
         if (res.code == 1) {
           if (this.openType == 0) {
             res.data.leaf = false;
             res.data.title = "校区名称";
           } else if (this.openType == 1) {
             res.data.unIcon = ["add"];
             res.data.leaf = true;
             res.data.title = "分校区名称";
           }
           this.$message.success("新增操作成功");


           resolve(res.data);
         } else {
           this.$message.error(res.msg);
           reject(res.msg);
         }
       });
     }
   });
 },


 //编辑
 editSubject(data) {
   console.log("编辑data", data);
   return new Promise((resolve, reject) => {
     if (!data.name || data.name == "") {
       this.$message.error("提交名称不得为空,请输入后再次提交");
       reject();
     } else {
       editCampus(data).then((res) => {
         if (res.code == 1) {
           this.$message.success("编辑操作成功");
           if (data.type == 2) {
             this.initTable();
           }
           resolve();
         } else {
           this.$message.error(res.msg);
           reject(res.msg);
         }
       });
     }
   });
 },


 //删除
 deleteSubject(data) {
   return new Promise((resolve, reject) => {
     deleteCampus(data).then((res) => {
       if (res.code == 1) {
         this.$message.success("删除校区操作成功");
         if (data.type == 2) {
           this.initTable();
         }
         resolve();
       } else {
         this.$message.error("删除校区操作异常");
         reject(res.msg);
       }
     });
   });
 },