深嵌套树的选择

深嵌套树的选择

主要思想:

  1. 拿到数据,先对数据进行处理,给数据添加expend 控制打开 关闭 ,给叶子节点添加isLeaf字段
  2. 先整理出所有非叶子节点的所有叶子节点 faLeaf ,用于控制非叶子节点的选中效果
  3. 通过遍历faLeaf,整理出一份所有非叶子节点的选中情况的对象 faIsChecked,后续如果需要提交父节点,可以通过这个对象获取
  4. 如果需要知道code对应的name值可以顺便整理一份对象 allNames
  5. 代码中使用了eventBus

以下是实现代码,文件嵌套关系如下

  • labelSelectTree
    • index
    • treeItem
    • mCheckbox
  • labelFilter
  • mock.json

labelSelectTree > index 组件

<template>
  <div class="labelSelectTree">
    <div v-for="item in list" :key="item.code">
      <div class="label-header" @click="togglePanel(item)">
        <span class="label-title">{{ item.name }}</span>
        <i v-if="item.expanded" class="label-icon el-icon-caret-top" />
        <i v-else class="label-icon el-icon-caret-bottom" />
      </div>
      <treeItem
        v-show="item.expanded"
        :list="item"
        :faLeaf="faLeaf"
        :checkedItems="checkedItems"
        :faIsChecked="faIsChecked"
      ></treeItem>
    </div>
  </div>
</template>

<script>
import { EventBus } from "@/utils/eventBus";
import treeItem from "./treeItem";
export default {
  name: "labelSelectTree",
  components: { treeItem },
  props: {
    list: Array,
    faLeaf: Object,
    value: Array,
    faIsChecked: Object,
  },
  data() {
    return {
      checkedItems: [],
    };
  },
  watch: {
    value: {
      handler(v) {
        if (!v.length) {
          this.checkedItems = [];
        }
      },
    },
  },
  mounted() {
    EventBus.$on("changeSelect", (v) => {
      this.checkedItems = [...new Set(v)];
      this.$emit("input", this.checkedItems);
    });
  },
  methods: {
    togglePanel(item) {
      item.expanded = !item.expanded;
    },
  },
};
</script>
<style lang="scss"  scoped>
.labelSelectTree {
  .label-header {
    background: #1a939380;
    box-sizing: border-box;
    width: 94% !important;
    margin: 0 auto 10px;
    cursor: pointer;
    border-radius: 10px;
    color: #fff;
    justify-content: space-between;
    align-items: center;
    display: flex;
    height: 36px;
    padding: 0 16px;
  }
  .label-title {
    font-weight: bold;
    font-size: 16px;
  }
}
</style>

labelSelectTree > treeItem组件

<template>
  <div class="treeItem label">
    <div v-for="item in list.items" :key="item.id" class="label-content">
      <div v-if="!(item.items && item.items.length)" class="selfBox">
        <mCheckbox :check="checkedItems.includes(item.code)" @handleCheck="handleCheckAllChange(item)" /><span class="label-name" @click.self="handleCheckAllChange(item)">{{
          item.name
        }}</span>
      </div>
      <div v-else class="selfBox">
        <mCheckbox :check="faIsChecked[item.code]" @handleCheck="handleCheckAllChange(item)" /><span class="label-name" @click.self="togglePanel(item)">{{
          item.name
        }}</span>
        <i :class="[
            'label-icon',
            item.expanded ? 'el-icon-caret-top' : 'el-icon-caret-bottom',
          ]" @click.self="togglePanel(item)" />
      </div>
      <div class="son" v-if="item.items && item.items.length" v-show="item.expanded">
        <tree-item :list="item" :faLeaf="faLeaf" :faIsChecked="faIsChecked" :checkedItems="checkedItems"></tree-item>
      </div>
    </div>
  </div>
</template>

<script>
import mCheckbox from "./mCheckbox";
import { EventBus } from "@/utils/eventBus";

export default {
  name: "treeItem",
  components: { mCheckbox },
  props: {
    list: Object,
    checkedItems: Array, //选中项
    faLeaf: Object, //统计父节点的所有叶子节点code
    faIsChecked: Object, //统计父节点是否选中的对象
  },
  data() {
    return {};
  },
  computed: {},
  watch: {},
  methods: {
    changeCheckedItems(code, flag) {
      code = typeof code === "string" ? [code] : code;
      let checked = [...this.checkedItems];
      if (flag) {
        checked.push(...code);
      } else {
        checked = checked.filter((item) => !code.includes(item));
      }
      EventBus.$emit("changeSelect", [...checked]);
    },
    handleCheckAllChange(self) {
      let isChecked = null;
      if (self.isLeaf) {
        // 叶子节点
        isChecked = this.checkedItems.includes(self.code);
        this.changeCheckedItems(self.code, !isChecked);
      } else {
        let selfLeaf = this.faLeaf[self.code];
        isChecked =
          selfLeaf &&
          selfLeaf.some((item) => {
            return !this.checkedItems.includes(item);
          });
        this.changeCheckedItems(selfLeaf, isChecked);
      }
    },
    togglePanel(self) {
      this.$set(self, "expanded", !self.expanded);
    },
  },
};
</script>
<style lang="scss"  scoped>
.treeItem.label {
  margin-bottom: 10px;
  width: 100%;
  color: #333;
  .label-name {
    font-size: 18px;
    padding-left: 10px;
  }
  .label-icon {
    font-weight: bold;
    color: #333;
    font-size: 16px;
  }
  .selfBox {
    display: grid;
    grid-template-columns: 20px 1fr 20px;
    align-items: center;
    line-height: 32px;
    padding: 4px 10px 4px 15px;
    cursor: pointer;
    &:hover {
      background-color: #00000040;
    }
    span {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
  }
  .label-content {
    padding-left: 13px;
  }
}
</style>

labelSelectTree > mCheckbox 组件

<template>
  <div :class="{ checkbox: true, disabled }" @click.stop="handleCheck">
    <div class="checked el-icon-check" v-if="check"></div>
  </div>
</template>
<script>
export default {
  name: "mCheckbox",
  props: {
    check: { type: Boolean, default: false },
    disabled: { type: Boolean, default: false },
  },
  data() {
    return {};
  },
  methods: {
    handleCheck() {
      if (this.disabled) return;
      this.$emit("handleCheck");
    },
  },
};
</script>
<style lang="scss" scoped>
.checkbox {
  width: 22px;
  height: 22px;
  border-width: 2px;
  background-color: #1d455240;
  border: 2px solid #00f6ff88;
  border-radius: 3px;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  .checked {
    color: #fff;
    background-color: #00f6ff88; 
  }
  &.disabled {
    border: 0.5px solid #e8ebee;
  }
}
</style>

labelFilter 数据处理 及 展示 页面

<template>
  <div class="inner_container">
    <div class="treeContainer">
      <mTree v-model="industryList" :list="labelTree" :faLeaf="faLeaf" :faIsChecked="faIsChecked"></mTree>
    </div>
    <ul class="labelDisplay">
      <li v-for="(item,i) in checkedNames" :key="i">{{ item }}</li>
    </ul>
    <div class="footer">
      <el-button type="primary" size="mini" @click="handleSubmit">确定</el-button>
      <el-button size="mini" @click="reset">重置</el-button>
    </div>
  </div>
</template>

<script>
import mTree from "./labelFilter/index";
import { originData } from "./mock.json"; 
export default {
  name: "toggleRole",
  components: { mTree },
  props: {},
  data() {
    return {
      originData: originData,
      labelTree: [],
      industryList: [],
      allNames: {},
      checkedNames: [],
    };
  },
  computed: {
    // 所有非叶子节点的叶子节点code
    faLeaf() {
      let FaLeaf = {};
      function loop(list) {
        list.map((item) => {
          if (item.items && item.items.length) {
            let codes = item.items.map((a) => a.code);
            item.code in FaLeaf
              ? FaLeaf[item.code].push(...codes)
              : (FaLeaf[item.code] = codes);
            loop(item.items);
          }
        });
      }
      loop(this.originData);
      for (const key in FaLeaf) {
        let element = FaLeaf[key];
        element.map((item, i) => {
          if (item in FaLeaf) {
            element[i] = FaLeaf[item];
          }
        });
      }
      for (const key in FaLeaf) {
        let element = FaLeaf[key];
        FaLeaf[key] = element.flat(4);
      }
      return FaLeaf;
    },
    faIsChecked() {
      let obj = {};
      for (const key in this.faLeaf) {
        const element = this.faLeaf[key];
        let flag = element.some((item) => {
          return !this.industryList.includes(item);
        });
        obj[key] = !flag;
      }
      return obj;
    },
  },
  watch: {
  },
  methods: {
    loop(arr) {
      return arr.map((item) => {
        if (item.items && item.items.length) {
          this.loop(item.items);
        } else {
          this.$set(item, "isLeaf", true);
        }
        this.$set(item, "expanded", false);
        this.allNames[item.code] = item.name
        return item;
      });
    },
    async getAllTreePaIndustry() {
      // const res = await getAllTreePaIndustry();
      // let data = []
      // this.originData = data;
      let D = [{ name: "商户行业", code: "wree", items: this.originData }];
      D = this.loop(D);
      this.labelTree = D;
    },
    handleSubmit() {
      let AllCheckedFa = [] //所有选中的父节点
      for (const key in this.faIsChecked) {
        const element = this.faIsChecked[key];
        element && AllCheckedFa.push(key)
      }
      this.checkedNames = [...AllCheckedFa, ...this.industryList].map(item => {
        return this.allNames[item]
      })
      console.log(AllCheckedFa);
      console.log(this.industryList);//所有选中的叶子节点 
    },
    reset() {
      this.industryList = []
    },
    findByCode(nestedArray, code) {
      for (let i = 0; i < nestedArray.length; i++) {
        const item = nestedArray[i];
        if (item.code === code) {
          return item;
        }
        if (Array.isArray(item.children)) {
          const foundItem = this.findByCode(item.children, code);
          if (foundItem) { return foundItem }

        }
      }
      return null;
    },
  },
  mounted() {
    this.getAllTreePaIndustry();

  },
};
</script>
 
<style scoped lang="scss">
.inner_container {
  height: 100%;
  padding: 0.075075rem /* 10/133.2 */;
  display: grid;
  grid-template-rows: 1fr 25% 0.225225rem /* 30/133.2 */;
  grid-gap: 0.112613rem /* 15/133.2 */;
  .treeContainer {
    overflow-y: auto;
    background-color: #fff;
    border-radius: 4px;
    box-shadow: 0px 0px 3px 1px #ddd;
    padding: 0.075075rem /* 10/133.2 */;
  }
  .labelDisplay {
    box-shadow: 0px 0px 3px 1px #cfcfcf;
    // border: 1px solid #ccc;
    border-radius: 4px;
    padding: 0.075075rem /* 10/133.2 */;
    text-align: left;
    overflow-y: auto;
    li {
      display: inline-block;
      color: #1677dc;
      background: #e8ecf2;
      border-radius: 0.022523rem /* 3/133.2 */;
      padding: 2px 6px;
      margin: 0 0.037538rem /* 5/133.2 */ 0.037538rem /* 5/133.2 */ 0;
      font-size: 14px;
    }
  }
  .mTree {
    overflow: auto;
  }
}
</style>

mock 数据

{
  "originData": [
    {
      "parentCode": null,
      "code": "A0000",
      "name": "童年",
      "indLevel": "1",
      "items": [
        {
          "parentCode": "A0000",
          "code": "A0400",
          "name": "灰太狼",
          "indLevel": "2",
          "items": [
            {
              "parentCode": "A0400",
              "code": "A0410",
              "name": "喜洋洋",
              "indLevel": "3",
              "items": [
                {
                  "parentCode": "A0410",
                  "code": "A0412",
                  "name": "红太狼",
                  "indLevel": "4",
                  "items": null
                },
                {
                  "parentCode": "A0410",
                  "code": "A0411",
                  "name": "小灰灰",
                  "indLevel": "4",
                  "items": null
                }
              ]
            },
            {
              "parentCode": "A0400",
              "code": "A0420",
              "name": "沸羊羊",
              "indLevel": "3",
              "items": [
                {
                  "parentCode": "A0420",
                  "code": "A0421",
                  "name": "美羊羊",
                  "indLevel": "4",
                  "items": null
                },
                {
                  "parentCode": "A0420",
                  "code": "A0422",
                  "name": "暖洋洋",
                  "indLevel": "4",
                  "items": null
                }
              ]
            }
          ]
        },
        {
          "parentCode": "A0000",
          "code": "A0100",
          "name": "懒洋洋",
          "indLevel": "2",
          "items": [
            {
              "parentCode": "A0100",
              "code": "A0110",
              "name": "慢羊羊",
              "indLevel": "3",
              "items": [
                {
                  "parentCode": "A0110",
                  "code": "A0111",
                  "name": "蕉太狼",
                  "indLevel": "4",
                  "items": null
                },
                {
                  "parentCode": "A0110",
                  "code": "A0112",
                  "name": "小猪猪",
                  "indLevel": "4",
                  "items": null
                },
                {
                  "parentCode": "A0110",
                  "code": "A0119",
                  "name": "伊之助",
                  "indLevel": "4",
                  "items": null
                },
                {
                  "parentCode": "A0110",
                  "code": "A0113",
                  "name": "炭治郎",
                  "indLevel": "4",
                  "items": null
                }
              ]
            },
            {
              "parentCode": "A0100",
              "code": "A0140",
              "name": "善逸",
              "indLevel": "3",
              "items": [
                {
                  "parentCode": "A0140",
                  "code": "A0141",
                  "name": "炎柱",
                  "indLevel": "4",
                  "items": null
                },
                {
                  "parentCode": "A0140",
                  "code": "A0142",
                  "name": "水柱",
                  "indLevel": "4",
                  "items": null
                },
                {
                  "parentCode": "A0140",
                  "code": "A0143",
                  "name": "音柱",
                  "indLevel": "4",
                  "items": null
                },
                {
                  "parentCode": "A0140",
                  "code": "A0149",
                  "name": "恋柱",
                  "indLevel": "4",
                  "items": null
                }
              ]
            },
            {
              "parentCode": "A0100",
              "code": "A0120",
              "name": "豆浆",
              "indLevel": "3",
              "items": [
                {
                  "parentCode": "A0120",
                  "code": "A0122",
                  "name": "油条",
                  "indLevel": "4",
                  "items": null
                },
                {
                  "parentCode": "A0120",
                  "code": "A0123",
                  "name": "拌粉",
                  "indLevel": "4",
                  "items": null
                },
                {
                  "parentCode": "A0120",
                  "code": "A0121",
                  "name": "凉粉",
                  "indLevel": "4",
                  "items": null
                }
              ]
            },
            {
              "parentCode": "A0100",
              "code": "A0130",
              "name": "水饺",
              "indLevel": "3",
              "items": [
                {
                  "parentCode": "A0130",
                  "code": "A0131",
                  "name": "馄饨",
                  "indLevel": "4",
                  "items": null
                },
                {
                  "parentCode": "A0130",
                  "code": "A0132",
                  "name": "手抓饼",
                  "indLevel": "4",
                  "items": null
                },
                {
                  "parentCode": "A0130",
                  "code": "A0133",
                  "name": "煎饼果子",
                  "indLevel": "4",
                  "items": null
                },
                {
                  "parentCode": "A0130",
                  "code": "A0134",
                  "name": "烤冷面",
                  "indLevel": "4",
                  "items": null
                }
              ]
            },
            {
              "parentCode": "A0100",
              "code": "A0190",
              "name": "其他农业",
              "indLevel": "3",
              "items": null
            },
            {
              "parentCode": "A0100",
              "code": "A0160",
              "name": "坚果",
              "indLevel": "3",
              "items": [
                {
                  "parentCode": "A0160",
                  "code": "A0162",
                  "name": "牛油果",
                  "indLevel": "4",
                  "items": null
                },
                {
                  "parentCode": "A0160",
                  "code": "A0163",
                  "name": "香蕉",
                  "indLevel": "4",
                  "items": null
                },
                {
                  "parentCode": "A0160",
                  "code": "A0161",
                  "name": "草莓",
                  "indLevel": "4",
                  "items": null
                },
                {
                  "parentCode": "A0160",
                  "code": "A0164",
                  "name": "西瓜",
                  "indLevel": "4",
                  "items": null
                },
                {
                  "parentCode": "A0160",
                  "code": "A0169",
                  "name": "苹果",
                  "indLevel": "4",
                  "items": null
                }
              ]
            },
            {
              "parentCode": "A0100",
              "code": "A0150",
              "name": "葡萄",
              "indLevel": "3",
              "items": [
                {
                  "parentCode": "A0150",
                  "code": "A0151",
                  "name": "柚子",
                  "indLevel": "4",
                  "items": null
                },
                {
                  "parentCode": "A0150",
                  "code": "A0152",
                  "name": "橘子",
                  "indLevel": "4",
                  "items": null
                },
                {
                  "parentCode": "A0150",
                  "code": "A0153",
                  "name": "橙子",
                  "indLevel": "4",
                  "items": null
                },
                {
                  "parentCode": "A0150",
                  "code": "A0154",
                  "name": "沃柑",
                  "indLevel": "4",
                  "items": null
                },
                {
                  "parentCode": "A0150",
                  "code": "A0159",
                  "name": "柠檬",
                  "indLevel": "4",
                  "items": null
                }
              ]
            },
            {
              "parentCode": "A0100",
              "code": "A0180",
              "name": "草种植及割草",
              "indLevel": "3",
              "items": [
                {
                  "parentCode": "A0180",
                  "code": "A0181",
                  "name": "狗尾巴草",
                  "indLevel": "4",
                  "items": null
                },
                {
                  "parentCode": "A0180",
                  "code": "A0182",
                  "name": "薰衣草",
                  "indLevel": "4",
                  "items": null
                }
              ]
            },
            {
              "parentCode": "A0100",
              "code": "A0170",
              "name": "中药材种植",
              "indLevel": "3",
              "items": [
                {
                  "parentCode": "A0170",
                  "code": "A0179",
                  "name": "当归",
                  "indLevel": "4",
                  "items": null
                },
                {
                  "parentCode": "A0170",
                  "code": "A0171",
                  "name": "鱼腥草",
                  "indLevel": "4",
                  "items": null
                }
              ]
            }
          ]
        }
      ]
    },
    {
      "parentCode": null,
      "code": "B0000",
      "name": "采矿业",
      "indLevel": "1",
      "items": [
        {
          "parentCode": "B0000",
          "code": "B0600",
          "name": "金矿",
          "indLevel": "2",
          "items": [
            {
              "parentCode": "B0600",
              "code": "B0610",
              "name": "银矿",
              "indLevel": "3",
              "items": null
            },
            {
              "parentCode": "B0600",
              "code": "B0620",
              "name": "铜矿",
              "indLevel": "3",
              "items": null
            }
          ]
        },
        {
          "parentCode": "B0000",
          "code": "B0700",
          "name": "石油和天然气开采业",
          "indLevel": "2",
          "items": [
            {
              "parentCode": "B0700",
              "code": "B0710",
              "name": "石油",
              "indLevel": "3",
              "items": [
                {
                  "parentCode": "B0710",
                  "code": "B0711",
                  "name": "陆地石油",
                  "indLevel": "4",
                  "items": null
                },
                {
                  "parentCode": "B0710",
                  "code": "B0712",
                  "name": "海洋石油",
                  "indLevel": "4",
                  "items": null
                }
              ]
            },
            {
              "parentCode": "B0700",
              "code": "B0720",
              "name": "社畜",
              "indLevel": "3",
              "items": [
                {
                  "parentCode": "B0720",
                  "code": "B0721",
                  "name": "文员",
                  "indLevel": "4",
                  "items": null
                },
                {
                  "parentCode": "B0720",
                  "code": "B0722",
                  "name": "程序猿",
                  "indLevel": "4",
                  "items": null
                }
              ]
            }
          ]
        },
        {
          "parentCode": "B0000",
          "code": "B1100",
          "name": "十上无难事",
          "indLevel": "2",
          "items": [
            {
              "parentCode": "B1100",
              "code": "B1110",
              "name": "呱呱乐",
              "indLevel": "3",
              "items": null
            },
            {
              "parentCode": "B1100",
              "code": "B1120",
              "name": "逆揍凯",
              "indLevel": "3",
              "items": null
            },
            {
              "parentCode": "B1100",
              "code": "B1190",
              "name": "四世同堂",
              "indLevel": "3",
              "items": null
            }
          ]
        }
      ]
    }
  ]
}
  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值