Vue根据后端数据返回的数据中的id,将数据中的属性赋值给现有树型数据

833 阅读2分钟

示例图

Snipaste_2022-12-03_13-53-34.png

问题描述

看到这张图,我们首先就会在脑海中构建出一种思路:利用树形表格根据后端返回的树形数据中的名称和锁的状态来渲染,并在点击锁时调取接口来刷新该列表来实现改变锁的状态。但是事与愿违,第一次从后端获取的数据格式并不会携带锁的状态标识:

[
        {
          name: "用户权限",
          id: "1",
          children: [
            { name: "删除权限", id: "101" },
            { name: "新增权限", id: "102" },
            { name: "编辑权限", id: "103" },
            {
              name: "查询权限",
              id: "104",
              children: [
                { name: "查询所有权限", id: "10401" },
                { name: "查询部分权限", id: "10402" },
              ],
            },
          ],
        },
        { name: "产品权限", id: "2" },
        { name: "菜单权限", id: "3" },
      ]

而是要通过后续调取接口来获取id和锁及其他状态

[
        { lock: true,id: "1",auth: false, },
        { lock: true, id: "2", auth: false },
        { lock: true, id: "3", auth: false },
        { lock: true, id: "101", auth: false },
        { lock: true, id: "102", auth: false },
        { lock: true, id: "103", auth: false },
        { lock: true, id: "104", auth: false },
        { lock: false, id: "10401", auth: false },
        { lock: false, id: "10402", auth: false },
      ],

那么这时候我们思路就要转换一下:要将后续返回的状态对现有数据进行递归(因为不知道后续是否还会再添加几层节点),并通过id找到对应的对象,并把与之对应其他的属性给它塞进去。

解决问题

直接上代码(这里先埋一个坑)

<template>
  <div>
    <el-row>
      <el-col>
        <div class="table-box">
          <el-table
            :data="treeData"
            :show-header="false"
            row-key="id"
            :tree-props="{ children: 'children' }"
            :row-style="{ height: '40px' }"
            :cell-style="{ padding: '0' }"
          >
            <el-table-column prop="name" label="日期"> </el-table-column>
            <el-table-column width="200px">
              <template slot-scope="scope">
                <el-button
                  class="lock"
                  type="text"
                  :icon="
                    scope.row.lock == true ? 'el-icon-unlock' : 'el-icon-lock'
                  "
                  :style="
                    scope.row.lock == true ? 'color: #409eff' : 'color: black'
                  "
                  @click="changeLock(scope.row)"
                ></el-button>
              </template>
            </el-table-column>
          </el-table>
        </div>
      </el-col>
    </el-row>
    <div class="btn-box">
      <el-button type="primary" @click="searchAuth">获取权限</el-button>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      //现有数据
      treeData: [
        {
          name: "用户权限",
          id: "1",
          children: [
            { name: "删除权限", id: "101" },
            { name: "新增权限", id: "102" },
            { name: "编辑权限", id: "103" },
            {
              name: "查询权限",
              id: "104",
              children: [
                { name: "查询所有权限", id: "10401" },
                { name: "查询部分权限", id: "10402" },
              ],
            },
          ],
        },
        { name: "产品权限", id: "2" },
        { name: "菜单权限", id: "3" },
      ],
      //后端数据
      attributes: [
        {
          lock: true,
          id: "1",
          auth: false,
        },
        { lock: true, id: "2", auth: false },
        { lock: true, id: "3", auth: false },
        { lock: true, id: "101", auth: false },
        { lock: true, id: "102", auth: false },
        { lock: true, id: "103", auth: false },
        { lock: true, id: "104", auth: false },
        { lock: false, id: "10401", auth: false },
        { lock: false, id: "10402", auth: false },
      ],
    };
  },
  methods: {
    //开关锁
    changeLock(row) {
      row.lock = !row.lock;
    },
    //获取权限按钮
    searchAuth() {
      this.reduceStatus(this.treeData, this.attributes);
    },
    //递归向现有数据添加属性
    reduceStatus(treeArr, returnArr) {
      //对现有树进行遍历
      for (let i = 0; i < treeArr.length; i++) {
        let item = treeArr[i];
        //找到对应id的对象
        const target = returnArr.find((current) => current.id === item.id);
        if (target) {
          Object.keys(target).forEach((key) => {
            //向对象中新添加属性
            item[key] = target[key];
          });
        }
        //判断childern是否存在且是否存在子节点,如果存在继续向下循环
        const children = item.children || [];
        if (children.length > 0) {
          this.reduceStatus(children, returnArr);
        }
      }
    },
  },
};
</script>

当我以为问题到这就可以结束的时候,对示例测验时发现点击锁时并没有任何变化。

发现问题

为了检查这一问题,我们在获取权限后对这个现有的树数据进行打印

3283d7e7f26a054c96733fe5364cc33.png

从这我们不难看到,新属性虽然已经添加进去,但是后续添加的属性并没有转换为setter/getter,原因就在于在添加属性时想当然的利用 item[key] = taget[key] 来添加属性,而这一行为导致vue并没有对后续添加的属性进行劫持转换setter/getter

最终解决方案

reduceStatus方法中添加属性时弃用 item[key] = taget[key] 转而使用Vue.$set()方法来确保新属性同样是响应式的

reduceStatus(treeArr, returnArr) {
      //对现有树进行遍历
      for (let i = 0; i < treeArr.length; i++) {
        let item = treeArr[i];
        //找到对应id的对象
        const target = returnArr.find((current) => current.id === item.id);
        if (target) {
          Object.keys(target).forEach((key) => {
            this.$set(item, key, target[key]); //向对象中新添加属性并确保属性为响应式
          });
        }
        //判断childern是否存在且是否存在子节点,如果存在继续向下循环
        const children = item.children || [];
        if (children.length > 0) {
          this.reduceStatus(children, returnArr);
        }
      }
    },