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

如何在dxTreeList中展示菜单控件并绑定权限复选框

dxTreeList主菜单控件未显示及权限绑定问题

问题说明

我是ASP和jQuery新手,要在dxTreeList中展示页面及对应控件,同时将复选框与权限数据绑定。目前主菜单的控件无法正常显示,原因是当前的扁平化函数仅处理数组类型的Controls字段,未处理主菜单里对象类型的controls字段,导致主菜单控件丢失,也没法按预期设置权限值和展示格式。

原始JSON数据

[
  {
    "allowedPages": [],
    "controls": {"Apple": false,"Banana": false,"Orange": false,"Grape": false},
    "hasAccess": true,
    "Action": "Index",
    "menuName": "FruitBasket",
    "submenus": []
  },
  {
    "allowedPages": [],
    "controls": {},
    "pageAccess": true,
    "pageAction": "#",
    "pageName": "TropicalFruits",
    "submenus": [
      {
        "action": "tropicalUser",
        "Controls": ["Mango", "Pineapple"],
        "allowedControls": null,
        "controller": "admin",
        "hasAccess": true,
        "roles": {"TropicalAdmin1": ["Mango", "Pineapple"],"TropicalAdmin2": ["Mango"]},
        "subchildMenu": [],
        "submenu": "ExoticFruits"
      }
    ]
  }
]

目标格式数据

[
  {"id": "Dashboard","parentId": 0,"pageName": "Dashboard","action": "Index","pageAccess": true},
  {"id": "FruitBasket","parentId": 0,"pageName": "FruitBasket","action": "#","pageAccess": true},
  {"id": "TropicalFruits","parentId": "FruitBasket","pageName": "TropicalFruits","action": "","pageAccess": true},
  {"id": "Mango","parentId": "TropicalFruits","pageName": "Mango","action": "","pageAccess": true},
  {"id": "Pineapple","parentId": "TropicalFruits","pageName": "Pineapple","action": "","pageAccess": true}
]

当前扁平化函数

function flattenControlsData(pages, parentId = 0) {
  let flatControlsTreeData = [];
  pages.forEach(page => {
    if (!page) return;
    const id = page.pageName || 'No ID';
    const transformedPage = {
      id: id,
      parentId: parentId,
      pageName: page.pageName || 'Unnamed Page',
      hasAccess: page.hasAccess,
      controller: page.controller || '',
      roles: page.roles || {},
      allowedControls: page.allowedControls || [],
      allControls: Object.keys(page.controls || {}),
      isAccess: page.isAccess || false
    };
    if (transformedPage.pageAccess) {
      flatControlsTreeData.push(transformedPage);
    }
    // Add controls if they exist
    if (Array.isArray(page.Controls)) {
      page.Controls.forEach(control => {
        flatControlsTreeData.push({
          id: control,
          parentId: id,
          pageName: control,
          action: '',
          pageAccess: true,
          controller: '',
          roles: {},
          allowedControls: [],
          roleControlMappingDetail: null,
          allControls: []
        });
      });
    }
    // Add submenus
    if (Array.isArray(page.submenus)) {
      flatControlsTreeData = flatControlsTreeData.concat(flattenControlsData(page.submenus, id));
    }
    // Add subchildMenu
    if (Array.isArray(page.subchildMenu)) {
      flatControlsTreeData = flatControlsTreeData.concat(flattenControlsData(page.subchildMenu, id));
    }
  });
  console.log(flatControlsTreeData);
  return flatControlsTreeData;
}

解决办法

修改扁平化函数,新增处理对象类型controls的逻辑,同时保留权限状态,这样主菜单的控件就能正常生成,后续也能绑定复选框:

function flattenControlsData(pages, parentId = 0) {
  let flatControlsTreeData = [];
  pages.forEach(page => {
    if (!page) return;
    // 处理页面节点:优先用pageName,没有则用menuName(适配原始数据的字段差异)
    const pageId = page.pageName || page.menuName || 'No ID';
    const transformedPage = {
      id: pageId,
      parentId: parentId,
      pageName: page.pageName || page.menuName || 'Unnamed Page',
      action: page.action || page.Action || page.pageAction || '',
      pageAccess: page.pageAccess ?? page.hasAccess ?? true, // 统一权限字段
      hasAccess: page.hasAccess,
      controller: page.controller || '',
      roles: page.roles || {},
      allowedControls: page.allowedControls || [],
      isPage: true // 标记是页面节点,方便后续区分
    };
    flatControlsTreeData.push(transformedPage);

    // 1. 处理对象类型的controls(主菜单的控件)
    if (typeof page.controls === 'object' && page.controls !== null) {
      Object.entries(page.controls).forEach(([controlName, isAllowed]) => {
        flatControlsTreeData.push({
          id: `${pageId}_${controlName}`, // 生成唯一ID,避免冲突
          parentId: pageId,
          pageName: controlName,
          action: '',
          controlAccess: isAllowed, // 绑定控件的权限状态,用于复选框
          isPage: false, // 标记是控件节点
          controller: '',
          roles: {},
          allowedControls: []
        });
      });
    }

    // 2. 处理数组类型的Controls(子菜单的控件)
    if (Array.isArray(page.Controls)) {
      page.Controls.forEach(control => {
        // 从roles里获取当前控件的权限(如果有)
        let controlAccess = true;
        if (page.roles) {
          // 这里可以根据当前登录角色调整,示例默认取第一个角色的权限
          const firstRole = Object.values(page.roles)[0];
          controlAccess = firstRole.includes(control);
        }
        flatControlsTreeData.push({
          id: `${pageId}_${control}`,
          parentId: pageId,
          pageName: control,
          action: '',
          controlAccess: controlAccess,
          isPage: false,
          controller: '',
          roles: page.roles || {},
          allowedControls: page.allowedControls || []
        });
      });
    }

    // 处理子菜单
    if (Array.isArray(page.submenus)) {
      flatControlsTreeData = flatControlsTreeData.concat(flattenControlsData(page.submenus, pageId));
    }

    // 处理子子菜单
    if (Array.isArray(page.subchildMenu)) {
      flatControlsTreeData = flatControlsTreeData.concat(flattenControlsData(page.subchildMenu, pageId));
    }
  });
  console.log(flatControlsTreeData);
  return flatControlsTreeData;
}

关键修改点

  • 适配原始数据的字段差异:比如有的用menuName,有的用pageName;有的用Action,有的用action/pageAction
  • 新增对象类型controls的处理逻辑,遍历键值对生成控件节点,同时保留controlAccess字段存储权限状态
  • 给节点添加isPage标记,方便在dxTreeList里区分页面和控件,配置不同的列展示
  • 给控件生成唯一ID(pageId_controlName),避免不同页面下同名控件ID冲突
  • 子菜单的数组类型Controls也同步添加权限状态绑定,从roles里获取对应权限

dxTreeList绑定示例

配置dxTreeList时,通过isPage区分页面和控件,给控件节点添加复选框:

$("#treeList").dxTreeList({
  dataSource: flattenControlsData(yourOriginalData),
  keyExpr: "id",
  parentIdExpr: "parentId",
  columns: [
    {
      dataField: "pageName",
      caption: "名称"
    },
    {
      caption: "权限",
      cellTemplate: function(container, options) {
        // 页面节点可以显示总权限,控件节点显示复选框
        if (options.data.isPage) {
          $("<div>").text(options.data.pageAccess ? "已授权" : "未授权").appendTo(container);
        } else {
          $("<div>").dxCheckBox({
            value: options.data.controlAccess,
            onValueChanged: function(e) {
              // 这里处理权限变更逻辑,比如更新原始数据或提交到后台
              options.data.controlAccess = e.value;
            }
          }).appendTo(container);
        }
      }
    }
  ],
  expandedRowKeys: [0], // 默认展开根节点
  showRowLines: true,
  showBorders: true
});

内容的提问来源于stack exchange,提问作者Guhan Srinivasan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 19:14:57