You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue.js菜单权限项目问题:子子菜单在所有表格单元格异常显示

问题解决方法

你的问题出在子子菜单的过滤逻辑没有和当前行的菜单绑定:当前的filteredSub_submenu计算属性是全局过滤所有选中子菜单对应的子子菜单,然后在每一行的单元格里都渲染这个全局结果,所以所有行都会显示相同的子子菜单。

具体修改步骤:

  1. 替换全局计算属性为行级过滤方法
    把原来的filteredSub_submenu计算属性改成一个接收菜单ID的方法,这样可以针对每一行的菜单,只筛选属于该菜单下的子菜单对应的子子菜单:
methods: {
  // 保留原有的setSelectedMenu、setSelectedSubMenu方法
  getFilteredSubSubmenus(menuId) {
    // 先获取当前菜单下的所有子菜单ID
    const currentMenuSubmenuIds = this.submenus
      .filter(sub => sub.menuId === menuId)
      .map(sub => sub.id);
    
    // 结合选中的子菜单来过滤子子菜单
    if (this.selectedSubMenu.includes("0")) {
      // 选中"全部"时,只显示当前菜单下的子子菜单
      return this.subsubmenus.filter(item => currentMenuSubmenuIds.includes(item.submenuId));
    } else {
      // 只显示选中的子菜单且属于当前菜单的子子菜单
      return this.subsubmenus.filter(item => {
        return this.selectedSubMenu.includes(item.submenuId) && currentMenuSubmenuIds.includes(item.submenuId);
      });
    }
  }
}
  1. 修改模板中的子子菜单渲染逻辑
    在表格的Sub-Submenu单元格里,调用这个方法并传入当前行的menu.id,这样每一行只会渲染属于自己菜单下的子子菜单:
<td>
  <ul>
    <li v-for="subsubmenu in getFilteredSubSubmenus(menu.id)" :key="subsubmenu.id">
      <label>
        <input type="checkbox" :value="subsubmenu.id" />
        {{ subsubmenu.sub_subMenuName }}
      </label>
    </li>
  </ul>
</td>
  1. 可选优化:修复子菜单的渲染效率
    原来的子菜单渲染是先循环所有filteredProduct再用v-if判断,改成直接在循环里过滤更高效:
<td>
  <ul>
    <li
      v-for="submenu in getCurrentMenuSubmenus(menu.id)"
      :key="submenu.id"
    >
      <label>
        <input type="checkbox" :value="submenu.id" @change="setSelectedSubMenu($event,submenu.id)" />
        {{ submenu.subMenuName }}
      </label>
    </li>
  </ul>
</td>

对应的新增方法:

getCurrentMenuSubmenus(menuId) {
  if (this.selectedMenu.includes("0")) {
    return this.submenus.filter(sub => sub.menuId === menuId);
  } else {
    return this.submenus.filter(sub => sub.menuId === menuId && this.selectedMenu.includes(sub.menuId));
  }
}

修改后的完整代码

HTML部分

<!-- Include the library in the page -->
<script src="https://unpkg.com/vue@2.2.0-beta.1/dist/vue.js"></script>

<!-- App -->
<div id="app">
  <table class="table">
    <thead>
      <tr>
        <th>Menu</th>
        <label><input type="checkbox" v-model="selectedMenu" value="0" />
          All</label>
        <th>Submenu</th>
        <th>Sub-Submenu</th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="menu in menus" :key="menu.id">
        <td>
          <label>
            <input
              type="checkbox"
              :value="menu.id" @change="setSelectedMenu($event,menu.id)"
            />{{ menu.menuName }}
          </label>
        </td>
        <td>
          <ul>
            <li
              v-for="submenu in getCurrentMenuSubmenus(menu.id)"
              :key="submenu.id"
            >
              <label>
                <input type="checkbox" :value="submenu.id" @change="setSelectedSubMenu($event,submenu.id)" />
                {{ submenu.subMenuName }}
              </label>
            </li>
          </ul>
        </td>
        <td>
          <ul>
            <li v-for="subsubmenu in getFilteredSubSubmenus(menu.id)" :key="subsubmenu.id">
              <label>
                <input type="checkbox" :value="subsubmenu.id" />
                {{ subsubmenu.sub_subMenuName }}
              </label>
            </li>
          </ul>
        </td>
      </tr>
    </tbody>
  </table>
</div>

JavaScript部分

console.clear()
// New VueJS instance
var app = new Vue({
  el: "#app",
  data: {
    menus: [{
        id: 1,
        menuName: "Tech 1"
      },
      {
        id: 2,
        menuName: "Tech 2"
      },
      {
        id: 3,
        menuName: "Tech 3"
      }
    ],
    selectedMenu: [],
    submenus: [{
        id: 1,
        menuId: 1,
        subMenuName: "architecture"
      },
      {
        id: 2,
        menuId: 1,
        subMenuName: "Electrical"
      },
      {
        id: 3,
        menuId: 1,
        subMenuName: "Electronics"
      },
      {
        id: 4,
        menuId: 2,
        subMenuName: "IEM"
      },
      {
        id: 5,
        menuId: 3,
        subMenuName: "CIVIL"
      }
    ],
    selectedSubMenu: [],
    subsubmenus: [{
        id: 1,
        submenuId: 1,
        sub_subMenuName: "becm"
      },
      {
        id: 2,
        submenuId: 1,
        sub_subMenuName: "ece"
      },
      {
        id: 3,
        submenuId: 4,
        sub_subMenuName: "EEE"
      },
      {
        id: 4,
        submenuId: 2,
        sub_subMenuName: "CSE"
      },
      {
        id: 5,
        submenuId: 3,
        sub_subMenuName: "MSE"
      }
    ],
  },
  computed: {
    filteredProduct: function() {
      const app = this,
        menu = app.selectedMenu;

      if (menu.includes("0")) {
        return app.submenus;
      } else {
        return app.submenus.filter(function(item) {
          return menu.indexOf(item.menuId) >= 0;
        });
      }
    }
  },
  methods: {
    setSelectedMenu: function(event, menu_id) {
      if (event.target.checked) {
        this.selectedMenu.push(menu_id);
      } else {
        var index = this.selectedMenu.indexOf(menu_id);
        this.selectedMenu.splice(index, 1);
      }
    },
    setSelectedSubMenu: function(event, submenu_id) {
      if (event.target.checked) {
        this.selectedSubMenu.push(submenu_id);
      } else {
        var index = this.selectedSubMenu.indexOf(submenu_id);
        this.selectedSubMenu.splice(index, 1);
      }
    },
    getCurrentMenuSubmenus(menuId) {
      if (this.selectedMenu.includes("0")) {
        return this.submenus.filter(sub => sub.menuId === menuId);
      } else {
        return this.submenus.filter(sub => sub.menuId === menuId && this.selectedMenu.includes(sub.menuId));
      }
    },
    getFilteredSubSubmenus(menuId) {
      const currentMenuSubmenuIds = this.submenus
        .filter(sub => sub.menuId === menuId)
        .map(sub => sub.id);
      
      if (this.selectedSubMenu.includes("0")) {
        return this.subsubmenus.filter(item => currentMenuSubmenuIds.includes(item.submenuId));
      } else {
        return this.subsubmenus.filter(item => {
          return this.selectedSubMenu.includes(item.submenuId) && currentMenuSubmenuIds.includes(item.submenuId);
        });
      }
    }
  }
})

CSS部分

ul {
  list-style-type: none;
  margin: 0;
  padding: 0;
}

核心原理

通过给每一行的子子菜单渲染绑定当前菜单ID,确保过滤逻辑只针对当前菜单下的子菜单集合,这样选中子菜单后,只有对应父菜单所在的行才会显示其下属的子子菜单,不会全局显示。

内容的提问来源于stack exchange,提问作者16_018_RHR

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 00:05:24