element(饿了么)增加模糊搜索的树状图(节点可选择,父级被选中时,子级不全选)与全选功能

!数据格式与官网的树状图格式一致;

需求

1、可以通过输入框过滤,刷新树状图的数据,只显示其父级与子级(包括孙级),如搜索橙汁时树状图层级显示水->果汁->橙汁->汇源橙汁;(filter方法)
2、全选多选框只选择与判断当前显示的节点是否被全选;搜索后,点击全选也只选择当前显示的所有节点;
比如搜索奶茶后,点击全选,只全选了水—>奶茶—>奶茶的子级;删除搜索值(奶茶),全选按钮应为没被全选状态;

3、树状图旁边的文字按钮,点击全选时,子级都被全选,同时切换为取消;
点击取消时,子级全部不被选择,同时切换为全选按钮。

效果图:

在这里插入图片描述

<template>
  <div>
    <el-input
      :placeholder="$t('common.searchPlaceholder')"
      v-model.trim="filterText"
    ></el-input>
    <el-checkbox
      v-model="checkAllValue"
      @change="isCheckAll"
      style="margin: 10px 0 0px"
      v-show="!filterText"
      >{{ $t("button.check_all") }}</el-checkbox
    >
    <el-checkbox
      v-model="checkAllResponse"
      @change="isCheckResponse"
      style="margin: 10px 0 0px"
      v-show="filterText"
      >{{ $t("button.check_all") }}</el-checkbox
    >
    <el-scrollbar
      v-if="source"
      wrap-class="scrollbar-wrapper"
      style="height: 275px"
    >
      <el-tree
        v-loading="LD_Tree"
        ref="valueTree"
        show-checkbox
        :node-key="nodeKey"
        check-strictly
        :data="source"
        :expand-on-click-node="isClickExpand"
        :default-expand-all="isExpandAll"
        :props="treeProps"
        :highlight-current="isHighlightCurrent"
        :default-checked-keys="checkKeys"
        @check="treeCheckNode"
        style="height: 280px; min-width: 20%"
        class="tree-select"
      >
        <div class="custom-tree-node" slot-scope="{ node, data }">
          <span>{{ data.name }}</span>
          <span v-show="node.childNodes.length !== 0">
            <el-link
              size="mini"
              @click.stop="nodeClick(node)"
              :type="(nodeIsAllCheck(node)) ? 'warning' : ''"
            >
              {{ nodeIsAllCheck(node)  ? "取消" : "全选" }}
            </el-link>
          </span>
        </div>
      </el-tree>
    </el-scrollbar>
  </div>
</template>
<script>

export default {
  name: "SgoTreeSelect",
  props: {
    // 树状图的懒加载
    LD_Tree: {
      type: Boolean,
      default: false,
    },
    // 节点的key
    nodeKey: {
      type: String,
      default: "",
    },
    // 树状图的数据值
    source: {
      type: Array,
      default: () => [],
    },
    // 树状图的prop
    treeProps: {
      type: Object,
      default: null,
    },
    // 是否高亮当前行
    isHighlightCurrent: {
      type: Boolean,
      default: false,
    },
    // 树状图的选中的值
    checkKeys: {
      type: Array,
      default: () => [],
    },
    // 是否展开全部
    isExpandAll: {
      type: Boolean,
      default: false,
    },
    // 是否点击展开节点
    isClickExpand: {
      type: Boolean,
      default: true,
    },
  },
  watch: {
    // 过滤选中的值
    filterText(val) {
      // primeCheck 把没有过滤时所选择的值保存下来时,防止后面取消选择的时候可以恢复之前的值
      this.primeCheck = this.$refs.valueTree.getCheckedKeys();
      this.filter(val);
      this.getVisibleNode();
      if (!val) {
        this.checkAllResponse = false;
        return;
      }
    },
    // 默认选中的值,当值改变时重新设置树的值
    checkKeys: {
      handler(val) {
        // 设置值是异步操作,需要设置完后再去判断是否全选值
        new Promise((resolve) => {
          this.$refs.valueTree.setCheckedNodes(val);
          resolve();
        }).then((res) => {
          this.getVisibleNode();
        });
      },
    },
  },
  data() {
    return {
      filterText: "", // 搜索框绑定的值
      checkAllValue: false, // 全选值
      checkAllResponse: false, // 过滤后的值配置是否全选
      treeVisibleValue: [], // 值配置当前所有显示节点的值
      primeCheck: [], //  值配置没有过滤时候的选择
      treeAllKey: [], // 全选id的值
    };
  },
  methods: {
    // 初始化组件里的值
    init() {
      this.filterText = "";
      this.$refs.valueTree.setCheckedKeys([]);
      this.checkAllResponse = false;
      this.checkAllValue = false;
    },
    // 获取当前的节点值
    getCheckedKeys() {
      return this.$refs.valueTree.getCheckedKeys();
    },
    // 返回目前半选中的节点的 key 所组成的数组
    getHalfCheckedKeys() {
      return this.$refs.valueTree.getHalfCheckedKeys();
    },
    // 递归获取全部的key值
    getTreeAllKey(tree) {
      let treeKey = [];
      let getTreeId = function (tree) {
        tree.forEach((item) => {
          treeKey.push(item.id);
          if (item.children && item.children.length > 0) {
            getTreeId(item.children);
          }
        });
      };
      getTreeId(tree);
      return treeKey;
    },
    // 是否全选值配置的值
    isCheckAll() {
      if (this.checkAllValue == true) {
        // 遍历获取选择全部的id 通过this.treeAllKey 获取
        this.treeAllKey = this.getTreeAllKey(this.source);
        this.$refs.valueTree.setCheckedKeys(this.treeAllKey);
      } else {
        // // 取消选择全部的
        this.$refs.valueTree.setCheckedKeys([]);
      }
    },
    // 树状图节点改变时
    treeCheckNode(nodeObject, checkObject) {
      // checkObject.checkedKeys 为当前选中的值
      this.checkValueIsAll(checkObject.checkedKeys);
    },
    // 树状图搜索过滤节点
    filterNode(value, data) {
      if (!value) {
        return true;
      }
      return data.name.indexOf(value) !== -1;
    },
    // 通过输入框的值修改是否显示节点
    filter(value) {
      const traverse = (node) => {
        const childNodes = node.root ? node.root.childNodes : node.childNodes;
        childNodes.forEach((child) => {
          child.visible = this.filterNode(value, child.data, child);
          traverse(child);
        });
        // node.visible为真,子节点也为真
        if (node.visible && node.childNodes.length > 0) {
          findChildNodes(node.childNodes);
        }
        // 递归循环子节点
        function findChildNodes(data) {
          for (let item of data) {
            item.visible = true;
            if (item.childNodes.length) {
              findChildNodes(item.childNodes);
            }
          }
        }
        // 设置父节点是否可见
        if (!node.visible && childNodes.length) {
          let allHidden = true;
          allHidden = !childNodes.some((child) => child.visible);
          if (node.root) {
            node.root.visible = allHidden === false;
          } else {
            node.visible = allHidden === false;
          }
        }
        if (!value) return;
        if (node.visible && !node.isLeaf) {
          node.expand();
        }
      };
      traverse(this.$refs.valueTree.store);
    },
    // 获取值配置树当前显示的节点的值
    getVisibleNode() {
      let allNodeObject = this.$refs.valueTree.store.nodesMap; // 获取当前显示的所有节点对象
      let visibleNode = []; // 当前显示的节点
      this.treeVisibleValue = []; // 因为要显示新的节点,要将之前的数据清空
      // 获取显示的节点的id
      for (let item in allNodeObject) {
        // 判断是否可见
        if (allNodeObject[item].visible == true) {
          // 获取当前可见的节点
          visibleNode.push(allNodeObject[item]);
          // 判断过滤后可见节点是否都被选中
          this.checkAllResponse = visibleNode.every((item) => {
            return item.checked === true;
          });
          // 当没输入值时,判断是否被全选
          if (!this.filterText) {
            this.checkAllValue = visibleNode.every((item) => {
              return item.checked === true;
            });
          }
          // 添加当前显示节点的id
          this.treeVisibleValue.push(allNodeObject[item].data.id);
        }
      }
    },
    // 是否全选过滤后值配置的值
    isCheckResponse() {
      // 全选过滤后值配置的值
      if (this.checkAllResponse === true) {
        // 将过滤后的节点值与之前所选择的节点值进行合并去重
        this.$refs.valueTree.setCheckedKeys([
          ...new Set(this.treeVisibleValue.concat(this.primeCheck)),
        ]);
      } else {
        // 取消全选过滤后的值
        // 判断未过滤选择的节点里是否包含了当前显示的节点,如果有则删除
        for (let item in this.primeCheck) {
          if (this.treeVisibleValue.includes(this.primeCheck[item])) {
            delete this.primeCheck[item];
          }
        }
        //设置当前值为未过滤时候所选中的值
        this.$refs.valueTree.setCheckedKeys(this.primeCheck);
      }
    },
    // 全选按钮
    nodeClick(nodeObject) {
      this.treeAllKey = [];
      if (this.nodeIsAllCheck(nodeObject)) {
        // 取消全选
        nodeObject.expand();
        this.allCheck(nodeObject, false);
      } else {
        // 全选
        nodeObject.expand();
        this.allCheck(nodeObject, true);
      }
      // this.$refs.valueTree.getCheckedKeys() 为当前选中的值
      this.checkValueIsAll(this.$refs.valueTree.getCheckedKeys());
    },
    // 遍历父节点设置是否选择
    // 通过isCheck来决定子节点是否选择
    allCheck(params, isCheck) {
      if(params.childNodes===0) {
        params.checked = isCheck;
      }
      params.childNodes.forEach((item) => {
        if (item.visible === true) {
          if (item.childNodes) {
            item.checked = isCheck;
            this.allCheck(item, isCheck);
          }
        }
      });
    },
    //判断节点是否被全选
    nodeIsAllCheck(nodeObject) {
      let isAllCheck = null;
      // 过滤获取当前的可见子节点
      let visibleNode = nodeObject.childNodes.filter((item) => {
        return item.visible === true;
      });
      // 判断当前节点的子节点是否都为true
      isAllCheck = visibleNode.every((item) => {
        // 没有子节点时直接等于当前节点是否被选择
        if(item.childNodes.length==0) {
          return isAllCheck = item.checked
        // 递归当前节点的孙节点
        // 当前节点被全选时,所有子节点也必须勾选所以加上了&&item.checked
        } else {
          return this.nodeIsAllCheck(item)&&item.checked;
        }
      });
      return isAllCheck;
    },
    // 判断当前选中的值是否触发复选框全选功能
    checkValueIsAll(checkValue) {
      // 遍历获取全部的id 通过this.treeAllKey 获取
      this.treeAllKey = this.getTreeAllKey(this.source);

      // 判断是否选择了全部的值
      this.checkAllValue = this.treeAllKey.every((item) => {
        return checkValue.includes(item);
      });

      // 判断是否选择了过滤后全部的值
      this.checkAllResponse = this.treeVisibleValue.every((item) => {
        return checkValue.includes(item);
      });
    },
  },
};
</script>
<style scoped>
.custom-tree-node {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 14px;
  padding-right: 8px;
}
.tree-select >>> .el-tree-node__content{
  height: 34px;
}
.tree-select >>> .el-link{
  margin: 0 6px;
}
</style>
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值