美文网首页
el-tree check-strictly但父子关联+虚拟滚动

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

作者: 浅忆_0810 | 来源:发表于2022-11-13 16:18 被阅读0次

    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. 参考文章

    相关文章

      网友评论

          本文标题:el-tree check-strictly但父子关联+虚拟滚动

          本文链接:https://www.haomeiwen.com/subject/iiquzrtx.html