el-tree 懒加载搜索累加回显选中的节点

2021-07-29  本文已影响0人  肥羊猪

html部分:
重点是node-key="orgCode",@check="checkChange",lazy,ref="tree",:load="loadNode",结合输入框。

<template>
 <div>
   <div class="tree-tag tbox">
     <div>
       <el-input suffix-icon="el-icon-search" placeholder="请输入" v-model="filterText" clearable maxlength="20" show-word-limit>
       </el-input>
       <div class="tree-container employee_box_tree">
         <el-tree
           class="filter-tree"
           :show-checkbox="config.showCheckBox"
           :check-strictly="config.strictly"
           :load="loadNode"
           :data="treeData"
           node-key="orgCode"
           @node-click="nodeClick"
           :props="config.treeProps"
           highlight-current
           ref="tree"
           lazy
           @check="checkChange">
           <div class="custom-tree-node" slot-scope="{ node }">
             <div>
               <span style="margin-right: 10px">{{ node.label }}</span>
               <el-tag v-if="node.data && node.data.storeType === 1" size="mini" type="success" >按钮</el-tag>
               <span v-if="codeFlag" style="color: #ff001d">({{ node.data.orgCode }})</span>
             </div>
           </div>
         </el-tree>
       </div>
     </div>
     <div>
       <div>已选择的组织,展示部分</div>
       <div class="tag-box">
         <el-tag class="tag" v-for="node in checkedNodes" size="normal" closable @close="removeTag(node)" >
           <span style="margin-right: 10px">{{ node.orgName }}</span>
           <el-tag v-if="node.data && node.data.storeType === 1" size="mini" type="success" >按钮</el-tag>
         </el-tag>
       </div>
     </div>
   </div>
   <div slot="footer" class="dialog-footer">
     <el-button type="primary" @click="saveFn">确 定</el-button>
     <el-button  @click="closeFn">取 消</el-button>
   </div>
 </div>
</template>

js部分:用chooseNode: new Map()来存所有选中的节点,选中添加,取消移除。具体参照checkChange函数。
showTreeChecked函数也是重点 ,保证每次刷新数据的时候树节点都能把默认选中的数据置为选中状态。
此处本人用的是setCheckedKeys,当然官网也有setCheckedNodes,setChecked可以选择,setTimeout主要是为了保证异步渲染能正确操作,有时候$nextTick失效,这个我没去研究。
主要是在刷新树的数据时需要把新的数据选中状态正确设置。同时也要保证传入的时候 Map初始化.

<script>
import { OrganizateTree, fetchOrganization } from "@/api/modules/staff";

export default {
  name: "orgTree",
  props: {
    // 传入的选中数据
    orgNodes: {
      type: Array,
      default: () => {
        return [];
      }
    },
    // 树的部分配置
    config: {
      type: Object,
      default: () => {},
    },
    onClick: {
      type: Function,
      default: () => {},
    },
  },

  data() {
    return {
      filterText: "",// 搜索关键字
      checkedNodes: [], // tree 已选择的集合
      treeData: [],// 树的Data
      codeFlag: false, // 是否展示编号
      chooseNode: new Map() // 当前选中所有节点
    };
  },
  mounted() {
    this.filterText = null;
  },
  methods: {
    // 提交本次数据
    saveFn() {
      this.$emit("getValue", this.checkedNodes);// 调用父组件的确定事件
    },
    // 取消本次提交
    closeFn() {
      if (this.checkedNodes.length!==this.orgNodes.length) {
        this.checkedNodes = this.orgNodes;// 右边的列表等于传入的参数
        this.$set(this.checkedNodes,this.orgNodes);// 同步刷新list
        setTimeout(() => this.$refs.tree.setCheckedKeys(this.orgNodes.map(n => n.orgCode), false));// 同步刷新tree
      }
      this.$emit('closeDialogFn');// 调用父组件的取消事件
    },
    // 禁止输入空格
    trimLR(e) {
      e.target.value = e.target.value.replace(/^\s+|\s+$/gm, "");
    },
    // 树回显节点函数
    showTreeChecked() {
      setTimeout(() => {
        const check = this.$refs.tree.getCheckedKeys(); // 当前树已选节点
        if (check.length !== this.checkedKeys.length) {
          this.$refs.tree.setCheckedKeys(this.checkedKeys, false); // 赋值选中
        }
      },100);
    },
    // 操作树的选中或者不选中
    checkChange(node) {
      if (this.chooseNode.get(node.orgCode)) {
        this.chooseNode.delete(node.orgCode); // 已存在 移除
      } else {
        this.chooseNode.set(node.orgCode, node); // 不存在 添加
      }
      this.checkedKeys = [...this.chooseNode.keys()]; // 选择的节点key
      const listTag = [...this.chooseNode.values()]; // 获取选中的节点 用于列表展示操作
      this.checkedNodes = [];// 初始化组织
      listTag.forEach(item => {// 列表展示数据赋值判断
        const { orgCode } = item;
        if (orgCode) {
          const find = this.checkedNodes.find(f => f.orgCode === orgCode);
          if (!find) {
            this.checkedNodes.push(item);
          } else {
            this.checkedNodes = this.checkedNodes.filter(f => f.orgCode === find.orgCode);// 找到
          }
        }
      })
    },
    // 移除右边列表的数据
    removeTag(node) {
      const { orgCode } = node;
      setTimeout(() => this.$refs.tree.setChecked(orgCode, false));// 同步左边的树节点不选中状态
      this.checkedNodes = this.checkedNodes.filter(f => f.orgCode !== orgCode);// 移除当前列表中的数据
    },
    // 懒加载数据
    async loadNode(node, resolve) {
      // 一级节点处理
      if (node.level === 0) {
        this.treeData = await OrganizateTree(1); // 首次加载一级节点数据函数
        resolve(this.treeData);
      }
      // 其余节点处理
      if (node.level >= 1) {
        const resp = await OrganizateTree(node.data.orgCode); // 获取该节点下的字节点
        resolve(resp);
      }
    },
    // 树点击事件
    nodeClick(node) {
      if (this.config.showCheckBox) return;
      this.checkedNodes = [node];
    },
    // 获取树的首次数据
    getTree() {
      let resp = OrganizateTree("1");
      this.treeData = resp.pageList || resp;
    },
  },

  watch: {
    // 监听搜索事件
    async filterText(val) {
      let resp;
      const loading = this.$loading({ lock: true, text: 'Loading', background: 'rgba(255, 255, 255, 0.3)'});
      if (!val) {
        this.codeFlag = false;// 不展示编号
        resp = await OrganizateTree("1");// 初次加载数据
      } else if (val) {
        this.codeFlag = true;// 展示编号
        resp = await fetchOrganization({ key: val });// 根据上层数据加载下层数据
      }
      if (resp) {
        this.treeData = resp.pageList || resp;
        this.$nextTick(() => {
          this.showTreeChecked();// 回显选中节点状态
          loading.close();// 关闭加载数据动画过渡
        });
      }
    },
    // 接收到orgNodes时,重置状态
    orgNodes: {
      immediate: true,
      async handler(nodes) {
        nodes.forEach(node => {
          this.chooseNode.set(node.orgCode, node);// 初始化选中的数据 父组件传入的数据
        })
        this.checkedNodes = nodes;// 回显列表
        this.checkedKeys = nodes.map(i => i.orgCode);// 选中的树节点
        this.showTreeChecked();// 回显树的选中状态
      },
    },
  },
};
</script>

调用的地方:

import orgTrees from "@/components/orgTree";
export default {
  components: { orgTrees},
data() {
    return {
      treeConfig: {
        treeProps: {
          children: "child", // 节点是否拥有子节点
          label: "orgName", // 节点渲染时的显示的名字
          value: "orgCode",
          isLeaf: (data, node) => {
            return data.storeType === 1; // 实体门店 叶子结点 不展示icon
          },
          disabled: (data, node) => {
            if (data.disabled) return !data.disabled;
          }
        },
        showCheckBox: true,
        strictly: true
      },
  },
  methods: {
    // 取消组织树
    cancelOrg() {
      this.openOrgStaff = false;
      this.filterText = "";
    },
    saveFn(val) {
      this.openOrgStaff = false;
      let { checkedNodes } = this.$refs.orgTree;
      this.checkedOrgs = checkedNodes; // 保存赋值选中展示
    },
}
}
<el-dialog title="选择组织" :visible.sync="openOrgStaff" width="800px" center append-to-body :show-close="false" :close-on-click-modal="false">
      <org-trees :orgNodes="checkedOrgs" v-if="openOrgStaff" @getValue="saveFn(form)" @closeDialogFn="cancelOrg" :config="treeConfig" ref="orgTree"></org-trees>
</el-dialog>

最后实现效果:

样式随机的.png
上一篇下一篇

猜你喜欢

热点阅读