feat: Phase 2 Sider 菜单由 FBA 服务端授权驱动 + 菜单/角色播种脚本(Epic #159)

This commit is contained in:
2026-08-06 10:04:36 +08:00
parent 5b673fcdc7
commit 067ba0abe1
4 changed files with 267 additions and 8 deletions
+53 -1
View File
@@ -396,6 +396,57 @@ var IAOP_SESSION = (function () {
return !!m && m.roles.indexOf(role) >= 0;
}
/* ---- Epic #159 Phase 2:服务端驱动菜单 ----------------------------------
* 有 FBA token 时:GET /fba/api/v1/sys/menus/sidebar 取当前用户在服务端
* 被授权的菜单树(vben5 结构,按角色在服务端过滤),与本地 MODULES 注册表
* 按 name == module.key 匹配,得到「服务端授权 ∩ 本地已知模块」的最终菜单;
* 子菜单同理按 child.key 匹配过滤。
* FBA 不可用 / 无匹配(菜单未播种 deploy/fba/seed_iaop_menus.py)时,
* 降级为本地角色过滤,保证菜单不空白。
* 返回 Promise<MODULES 子集>。 */
function menuTree(user) {
if (!user) return Promise.resolve([]);
if (!readFbaToken()) return Promise.resolve(modulesFor(user.role));
return fbaFetch("/sys/menus/sidebar")
.then(function (r) { return r.ok ? r.json() : null; })
.then(function (d) {
if (!d || d.code !== 200 || !Array.isArray(d.data)) {
return modulesFor(user.role);
}
var allowed = {}; // moduleKey -> true(无子菜单)| {childKey: true}
d.data.forEach(function (node) {
if (!node || !node.name) return;
var mod = null;
for (var i = 0; i < MODULES.length; i++) {
if (MODULES[i].key === node.name) { mod = MODULES[i]; break; }
}
if (!mod) return; // FBA 自有菜单(系统管理等)不匹配,跳过
if (node.children && node.children.length) {
var kids = {};
node.children.forEach(function (c) {
if (c && c.name) kids[c.name] = true;
});
allowed[mod.key] = kids;
} else {
allowed[mod.key] = true;
}
});
var mods = MODULES.filter(function (m) { return !!allowed[m.key]; })
.map(function (m) {
if (!m.children || !m.children.length || allowed[m.key] === true) return m;
var kids = allowed[m.key];
var copy = Object.assign({}, m, {
children: m.children.filter(function (c) { return !!kids[c.key]; })
});
return copy;
});
// 服务端树里没有任何 iAOP 模块(未播种)→ 本地角色过滤兜底
return mods.length ? mods : modulesFor(user.role);
})
.catch(function () { return modulesFor(user.role); });
}
return {
MODULES: MODULES,
ROLE_LABELS: ROLE_LABELS,
@@ -409,7 +460,8 @@ var IAOP_SESSION = (function () {
/* FBA 新增 */
fbaStatus: fbaStatus,
getCaptcha: getCaptcha,
getAuthHeaders: getAuthHeaders
getAuthHeaders: getAuthHeaders,
menuTree: menuTree
};
})();