From 1474f69c73dc80cee8ce58e489fa91f0a343b196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E8=87=AA=E8=BE=BE?= <980324341@qq.com> Date: Fri, 4 Sep 2026 00:31:04 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=8E=E5=8F=B0=E7=AE=A1=E7=90=86=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E6=94=B9=E4=B8=BA=E6=9C=8D=E5=8A=A1=E7=AB=AF=E6=8C=89?= =?UTF-8?q?=E6=9D=83=E9=99=90=E6=B8=B2=E6=9F=93=EF=BC=9A=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=88=B7=E6=96=B0=E6=97=B6=E5=85=88=E6=98=BE=E7=A4=BA=E5=85=A8?= =?UTF-8?q?=E9=87=8F=E8=8F=9C=E5=8D=95=E5=86=8D=E6=8A=98=E5=8F=A0=E7=9A=84?= =?UTF-8?q?=E9=97=AA=E7=83=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 兜底路径) --- backend/blueprints/main.py | 22 ++++++- backend/static/admin.js | 35 ++++++++++++ backend/web_source/admin.html | 104 ++++++++++++++++++---------------- 3 files changed, 109 insertions(+), 52 deletions(-) diff --git a/backend/blueprints/main.py b/backend/blueprints/main.py index 3f69361a..2bc6769f 100644 --- a/backend/blueprints/main.py +++ b/backend/blueprints/main.py @@ -2,7 +2,7 @@ 主页面蓝图:首页、管理后台页、静态文件 """ import os -from flask import Blueprint, redirect, url_for, send_file, session +from flask import Blueprint, current_app, redirect, url_for, send_file, session from werkzeug.utils import safe_join from utils.auth import login_required, is_session_user_valid @@ -24,7 +24,25 @@ def index(): @main.route('/admin') @login_required def admin_page(): - return render_html('admin.html') + """管理后台页:服务端直接渲染当前用户有权限的菜单,避免客户端二次渲染造成闪烁。""" + context = {} + try: + # 复用权限菜单加载逻辑;Java 权限接口不可用时降级为无菜单(JS 侧会走 API 兜底) + from blueprints.admin_api import _load_current_backend_menu_items + _, _, items, denied = _load_current_backend_menu_items() + if denied is None and items: + context['admin_menu_items'] = items + context['admin_menu_rendered'] = True + else: + # 权限接口失败:真实页面同样会失败,服务端不渲染菜单,避免展示越权菜单 + context['admin_menu_rendered'] = False + current_app.logger.warning('[admin] 服务端菜单渲染失败,降级为客户端加载: %s', + (denied[0].get_json() if denied and len(denied) > 0 else None)) + except Exception as exc: + # 兜底:任何异常都不得阻断管理页打开,JS 会自行请求菜单接口 + current_app.logger.exception('[admin] 服务端渲染菜单异常: %s', exc) + context['admin_menu_rendered'] = False + return render_html('admin.html', **context) @main.route('/static/') diff --git a/backend/static/admin.js b/backend/static/admin.js index 8de1a3d8..7d92af73 100644 --- a/backend/static/admin.js +++ b/backend/static/admin.js @@ -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) { diff --git a/backend/web_source/admin.html b/backend/web_source/admin.html index 1ae0489d..619ecf02 100644 --- a/backend/web_source/admin.html +++ b/backend/web_source/admin.html @@ -4756,56 +4756,60 @@ 电商运营管理后台 -