el-tree check-strictly但父子关联+虚拟滚动

2022-11-13  本文已影响0人  浅忆_0810

1. 设置check-strictly可选择各层级且父子数关联

<!-- child.vue -->
<template>
  <div class="select-tree-container">
    <el-popover
      placement="bottom-start"
      popper-class="select-tree-popper"
      v-model="isShowSelect"
      @hide="closePopver"
    >
      <el-tree
        ref="tree"
        :data="treeData"
        node-key="id"
        default-expand-all
        :expand-on-click-node="false"
        check-strictly
        check-on-click-node
        show-checkbox
        :default-expanded-keys="defaultKey"
        @check="nodeClick"
      ></el-tree>

      <el-select
        slot="reference"
        ref="select"
        v-model="selectVal"
        multiple
        @remove-tag="removeTag"
      >
        <el-option
          v-for="item in options"
          :key="item.value"
          :label="item.label"
          :value="item.value"
        ></el-option>
      </el-select>
    </el-popover>
  </div>
</template>
 
<script>
export default {
  name: 'SelectTree',
  props: {
    // 树结构数据
    treeData: {
      type: Array,
      default() {
        return [];
      },
    },
    // 默认选中的节点key
    defaultKey: {
      type: Array,
      default() {
        return [];
      },
    }
  },
  data() {
    return {
      isShowSelect: false, // 是否显示树状选择器
      options: [],
      returnDatas: [], // 返回给父组件数组对象
      selectVal: []
    }
  },
  created () {
  },
  watch: {
    // 隐藏select自带的下拉框
    isShowSelect() {
      this.$refs.select.blur();
    }
  },
  methods: {
    // 节点被点击
    nodeClick(v) {
      const anode = this.$refs.tree.getNode(v)
      if (anode.checked) {
        this.setParentChecked(anode.parent)
        this.setChildChecked(anode.childNodes)
      } else {
        this.deleteParentChecked(anode.parent)
        this.deleteChildChecked(anode.childNodes)
      }

      var checkedKeys = this.$refs.tree.getCheckedKeys() // 所有被选中的节点的 key 所组成的数组数据

      this.options = checkedKeys.map(item => {
        const node = this.$refs.tree.getNode(item) // 所有被选中的节点对应的node

        if (node.level === 1) { // 第一层
          node.hide = false
        } else {
          node.hide = !node.parent.indeterminate &&  node.parent.checked
        }

        // 设置option选项
        return {
          ...node,
          label: node.label,
          value: node.key
        }
      })

      this.returnDatas = this.selectVal = this.options.filter(o => !o.indeterminate && !o.hide).map(item => item.value)
      this.closePopver()
    },
    // 如果不是全选中为父级添加半选状态,如果子集全选后,父级也要全选
    setParentChecked(parent) {
      const fnode = this.$refs.tree.getNode(parent)
      // 子集是否是全选
      const isAllChecked = fnode.childNodes.every(k => k.checked && k.indeterminate === false)
      if (!fnode.isLeaf) {
        fnode.indeterminate = !isAllChecked // 子集是否是全选,如果子集全选,则半选状态为假
        fnode.checked = true
      }
      if (fnode.parent) {
        this.setParentChecked(fnode.parent)
      }
    },
    // 将子节点全选中
    setChildChecked(childNodes) {
      if (childNodes && childNodes.length > 0) {
        childNodes.map(k => {
          k.checked = true
          this.setChildChecked(this.$refs.tree.getNode(k).childNodes)
        })
      }
    },
    // 如果取消子节点的选中,设置父级节点选中状态
    deleteParentChecked(parent, d = false) {
      const fnode = this.$refs.tree.getNode(parent)
      const isAllChecked = fnode.childNodes.some(k => d ? (k.checked || k.indeterminate) : k.checked) // 子集是否是全选
      if (!fnode.isLeaf) {
        fnode.indeterminate = isAllChecked // 子集是否是全选,如果子集全选,则半选状态为假
        fnode.checked = isAllChecked
        if (fnode.parent) { // 如果有父节点,则需要去判断父节点是否选中
          this.deleteParentChecked(fnode.parent, true)
        }
      }
    },
    // 删除子节点的勾选状态
    deleteChildChecked(childNodes) {
      if (childNodes && childNodes.length > 0) {
        childNodes.map(k => {
          k.indeterminate = false
          k.checked = false
          this.deleteChildChecked(this.$refs.tree.getNode(k).childNodes)
        })
      }
    },
    // 删除任一 select 选项
    removeTag(val) {
      this.$refs.tree.setChecked(val, false); // 设置节点的勾选状态为未选中
      this.nodeClick(val)
    },
    // 清空所有勾选
    clearSelectedNodes() {
      this.selectVal = this.returnDatas = []
      this.$refs.tree.setCheckedKeys([])
    },
    // 下拉框关闭
    closePopver() {
      this.$emit("get:value", this.selectVal, this.returnDatas);
    }
  }
};
</script>
 
<style lang="scss">
  .select-tree-container {
    .el-select {
      width: 280px;
    }
  }

  .select-tree-popper {
    box-sizing: border-box;
    width: 280px;
  }
</style>
<template>
  <div class="app-container">
    <select-tree
      :tree-data="treeData"
      :defaultKey="defaultCheckedKeys"
      @get:value="getTreeVal"
    />
  </div>
</template>

<script>
import SelectTree from '@/components/SelectTree.vue';

export default {
  components: { SelectTree },
  name: "App",
   data() {
    return {
      treeData: [
        {
          id: 1,
          label: "水果",
          children: [
            { 
              id: 4, 
              label: '香蕉',
              children: [
                { id: 41, label: '小香蕉' },
                { id: 42, label: '大香蕉' },
              ]
            },
            { 
              id: 5, 
              label: '圣女果',
              children: [
                { id: 51, label: '小圣果' },
                { id: 52, label: '大圣果' },
              ] 
            }
          ]
        },
        {
          id: 2,
          label: "食物",
          children: [
            { id: 6, label: '龙虾' },
            { id: 7, label: '螃蟹' }
          ]
        }
      ],
      defaultCheckedKeys: []
    }
  },
  methods: {
    getTreeVal(val, data) {
      // console.log(val, data);
    }
  }
};
</script>

2. 增加虚拟滚动

  1. 安装vue-virtual-scroll-list
$ yarn add vue-virtual-scroll-list
  1. tree-virtual-node.vue中增加以下方法,否则点击收起箭头无效果

  2. el-tree改为virtual-tree,其余参数都一致

<!-- child.vue -->
<template>
  <div class="select-tree-container">
    ...
    <virtual-tree
      ref="tree"
      :data="treeData"
      node-key="id"
      default-expand-all
      :expand-on-click-node="false"
      check-strictly
      check-on-click-node
      show-checkbox
      :default-expanded-keys="defaultKey"
      @check="nodeClick"
    ></virtual-tree>
    ...
  </div>
</template>
 
<script>
import VirtualTree from './virtualNodeTree/tree'

export default {
  name: 'SelectTree',
  components: { VirtualTree }
}
</script>

3. 效果图

4. 参考文章

上一篇下一篇

猜你喜欢

热点阅读