后台管理菜单改为服务端按权限渲染:修复刷新时先显示全量菜单再折叠的闪烁
Build Backend JAR / build (push) Has been cancelled

- main.py admin_page 调用 _load_current_backend_menu_items 取当前用户权限菜单,
  经 render_html 传入模板;Java 权限接口不可用时降级不渲染(避免展示越权菜单)
- admin.html 删除写死的 16 个静态菜单按钮,改为 Jinja2 按权限渲染分组菜单,
  渲染成功时 nav 带 data-server-rendered 标记,失败显示轻量占位
- admin.js loadAdminMenus 识别 data-server-rendered 后直接绑定事件并激活,
  不再请求 /current-user/menus 重渲染(保留 API 兜底路径)
This commit is contained in:
2026-09-04 00:31:04 +08:00
parent 11dba90780
commit 1474f69c73
3 changed files with 109 additions and 52 deletions
+35
View File
@@ -268,8 +268,43 @@
group.style.display = visible ? '' : 'none';
});
}
function bindAdminMenuTabs(serverRendered) {
adminMenuByRoute = {};
var routes = [];
adminMenuEl.querySelectorAll('#adminMenu .tab').forEach(function (tab) {
var route = tab.dataset.tab;
if (!route) return;
routes.push(route);
if (!adminMenuByRoute[route]) {
adminMenuByRoute[route] = { route_path: route, name: tab.getAttribute('title') || route };
}
tab.onclick = function () {
activateAdminTab(tab.dataset.tab);
};
});
return routes;
}
function loadAdminMenus(preferredTab) {
if (preferredTab) activeAdminTabName = preferredTab;
// 服务端已按权限渲染菜单(data-server-rendered=true):直接绑定事件并激活,
// 不再请求接口重渲染,避免"全量菜单→折叠"的闪烁。
if (!adminMenuEl || adminMenuEl.getAttribute('data-server-rendered') !== 'true') {
renderAdminTabsFromApi();
return;
}
var serverRoutes = bindAdminMenuTabs(true);
if (!serverRoutes.length) {
activeAdminTabName = '';
hideAllAdminPanels();
return;
}
if (window.__initAdminMenuCollapse) window.__initAdminMenuCollapse();
var fallbackTab = activeAdminTabName && serverRoutes.indexOf(activeAdminTabName) >= 0
? activeAdminTabName
: serverRoutes[0];
activateAdminTab(fallbackTab);
}
function renderAdminTabsFromApi() {
fetch('/api/admin/current-user/menus')
.then(function (r) { return r.json(); })
.then(function (res) {