From 19a39e0433a1d656f6cda652edb6943a6dcea438 Mon Sep 17 00:00:00 2001 From: chengkml Date: Sat, 2 May 2026 13:51:06 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8B=E7=BA=BF=E4=BB=AA=E8=A1=A8=E7=9B=98?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E5=B9=B6=E7=BB=9F=E4=B8=80=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E5=85=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: multica-agent --- MEMORY.md | 26 +- api/app/services/admin_service.py | 3 +- api/app/services/legacy_admin_rbac_service.py | 2 +- api/app/services/legacy_authz_service.py | 2 +- api/app/services/seed_service.py | 19 +- api/app/services/topic_registry.py | 1 - memory/2026-05-01.md | 48 ++++ web/src/app/admin/layout.tsx | 4 +- web/src/app/admin/menus/page.tsx | 1 - web/src/app/admin/page.tsx | 222 +----------------- web/src/app/admin/roles/page.tsx | 2 +- web/src/app/page.tsx | 2 +- web/src/middleware.ts | 12 +- 13 files changed, 94 insertions(+), 250 deletions(-) diff --git a/MEMORY.md b/MEMORY.md index e2749db..c1cd919 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -464,11 +464,11 @@ ## 前端路由展示口径(2026-04-23) -- 用户可见后台地址默认不再带 `/admin` 前缀(例如 `/users`、`/requirements`、`/dashboard`)。 +- 用户可见后台地址默认不再带 `/admin` 前缀(例如 `/users`、`/requirements`)。 - 兼容策略: - 无前缀地址通过前端中间层 rewrite 到现有 `app/admin/**` 页面实现; - 历史 `/admin/**` 地址继续可访问,并自动重定向到无前缀地址。 -- 后台首页映射口径:`/admin` 对外统一为 `/dashboard`。 +- 后台首页映射口径:`/admin` 对外统一为 `/users`。 - 菜单路径展示口径:前端渲染菜单时将后端返回的 `/admin/**` 路径规范化为无前缀地址,避免导航条继续暴露 `/admin`。 ## 后台功能下线口径(2026-04-23) @@ -546,10 +546,10 @@ ## 首页与登录口径(2026-04-23) - `/` 默认作为登录入口页,不再承载“已登录后停留的首页面板”。 -- 登录态(含刷新会话恢复)进入 `/` 时,前端立即跳转 `/dashboard`,实现“登录后直达后台”。 +- 登录态(含刷新会话恢复)进入 `/` 时,前端立即跳转 `/users`,实现“登录后直达后台”。 - 后台壳层文案对齐: - 未登录访问后台时提示“前往登录”(`/`); - - 账号菜单提供“后台首页”(`/dashboard`),不再出现“返回首页”歧义入口。 + - 账号菜单提供“用户管理”(`/users`)作为默认后台入口。 - 退出登录口径:统一跳转到登录页 `/`(不保留在当前后台路由)。 ## 站点标题口径(2026-04-24) @@ -563,14 +563,14 @@ - 左侧为品牌与机器人主题视觉区; - 右侧为白色登录卡片(品牌头、表单、主操作按钮、辅助链接)。 - 交互口径保持: - - 登录态进入 `/` 仍自动跳转 `/dashboard`; + - 登录态进入 `/` 仍自动跳转 `/users`; - 登录/注册逻辑不变,视觉改造不改变鉴权接口契约。 ## 后台账号入口口径(2026-04-23) - 后台右上角账号入口采用 AntD `Avatar` 作为下拉触发器,不再使用“账号”文字按钮。 - Avatar 文案默认使用用户名首字母(大写),空值兜底 `U`。 -- 下拉内容口径保持不变:账号信息 + 后台首页 + 退出登录。 +- 下拉内容口径保持不变:账号信息 + 默认后台入口 + 退出登录。 ## 后台左侧导航口径(2026-04-24) @@ -639,7 +639,19 @@ - 当 `NEXT_PUBLIC_API_BASE_URL` 指向 loopback(`localhost/127.0.0.1/::1`)时,前端运行时应保证 API host 与当前页面 host 对齐。 - 只要“配置 host 是 loopback 且与 `window.location.hostname` 不一致”,就自动重写为当前页面 host,并保留配置端口(默认 `8000`)。 -- 目的:避免 `localhost` 与 `127.0.0.1` 混用导致 refresh cookie 跨站语义,从而在 `/dashboard` 硬刷新时误判未登录。 +- 目的:避免 `localhost` 与 `127.0.0.1` 混用导致 refresh cookie 跨站语义,从而在后台路由硬刷新时误判未登录。 + +## 仪表盘下线口径(2026-05-02) + +- 后台“仪表盘”页面已下线,不再作为可访问菜单和默认首页。 +- 前端路由口径: + - `/admin`、`/dashboard` 统一重定向到 `/users`; + - 登录态进入 `/` 默认跳转到 `/users`; + - `web/src/app/admin/page.tsx` 改为重定向页,不再渲染卡片工作台。 +- 后端菜单口径: + - `seed_service.DEFAULT_MENUS` 删除 `dashboard`; + - `ROLE_MENU_BINDINGS` 删除 admin/user 对 `dashboard` 的绑定; + - `legacy_authz_service`、`legacy_admin_rbac_service`、`admin_service` 对 `dashboard` 统一加入下线过滤集合,屏蔽历史库残留菜单记录。 ## 后台顶部滚动口径(2026-04-25) diff --git a/api/app/services/admin_service.py b/api/app/services/admin_service.py index 110ccf8..57aded9 100644 --- a/api/app/services/admin_service.py +++ b/api/app/services/admin_service.py @@ -32,6 +32,7 @@ AUDIT_LOG_LOAD_OPTIONS = ( ) REMOVED_MENU_CODES = { + "dashboard", "admin.wxapp", "admin.system_message", "admin.inbox", @@ -403,7 +404,7 @@ def update_menu(db: Session, menu_id: int, payload: MenuUpdateRequest) -> MenuPu def delete_menu(db: Session, menu_id: int) -> bool: menu = get_menu_by_id(db, menu_id) - if not menu or menu.code in {"dashboard", "admin.users", "admin.roles", "admin.menus", "admin.system_params", "admin.power_lines", "admin.lightning_currents", "admin.lightning_distribution", "admin.workers", "admin.task_monitor", "admin.atp_models", "admin.files", "admin.elevation", "admin.syslog", "admin.wine_runner"}: + if not menu or menu.code in {"admin.users", "admin.roles", "admin.menus", "admin.system_params", "admin.power_lines", "admin.lightning_currents", "admin.lightning_distribution", "admin.workers", "admin.task_monitor", "admin.atp_models", "admin.files", "admin.elevation", "admin.syslog", "admin.wine_runner"}: return False child_exists = db.scalar(select(Menu.id).where(Menu.parent_id == menu_id)) if child_exists is not None: diff --git a/api/app/services/legacy_admin_rbac_service.py b/api/app/services/legacy_admin_rbac_service.py index 341082b..450e637 100644 --- a/api/app/services/legacy_admin_rbac_service.py +++ b/api/app/services/legacy_admin_rbac_service.py @@ -30,6 +30,7 @@ from .user_service import queue_users_auth_refresh PROTECTED_ROLE_IDS = {"admin", "user", "sys_mgr"} REMOVED_MENU_CODES = { + "dashboard", "admin.wxapp", "admin.system_message", "admin.inbox", @@ -62,7 +63,6 @@ REMOVED_MENU_CODES = { } PROTECTED_MENU_CODES = { - "dashboard", "admin.users", "admin.roles", "admin.menus", diff --git a/api/app/services/legacy_authz_service.py b/api/app/services/legacy_authz_service.py index 09db2f5..e9a94d7 100644 --- a/api/app/services/legacy_authz_service.py +++ b/api/app/services/legacy_authz_service.py @@ -47,6 +47,7 @@ ADMIN_ROLE_IDS = { } DISABLED_MENU_CODES: set[str] = { + "dashboard", "admin.wxapp", "admin.system_message", "admin.inbox", @@ -97,7 +98,6 @@ MENU_CODE_PERMISSION_MAP: dict[str, set[str]] = { "admin.lightning_currents": {"lightning.read", "lightning.manage"}, "admin.lightning_distribution": {"lightning.read", "lightning.manage"}, "admin.wine_runner": {"wine.read", "wine.manage"}, - "dashboard": {"menu.read"}, } SYNTHETIC_LEGACY_MENU_ROWS: list[dict[str, Any]] = [ diff --git a/api/app/services/seed_service.py b/api/app/services/seed_service.py index 7eeebfe..feb1a91 100644 --- a/api/app/services/seed_service.py +++ b/api/app/services/seed_service.py @@ -82,19 +82,6 @@ DEFAULT_ROLES: dict[str, dict[str, object]] = { } DEFAULT_MENUS: list[dict[str, object]] = [ - { - "code": "dashboard", - "name": "仪表盘", - "path": "/admin", - "icon": "LayoutDashboard", - "parent_code": None, - "type": "menu", - "sort_order": 10, - "status": "enabled", - "visible": True, - "cacheable": False, - "permission_code": None, - }, { "code": "admin.users", "name": "用户管理", @@ -102,7 +89,7 @@ DEFAULT_MENUS: list[dict[str, object]] = [ "icon": "Users", "parent_code": None, "type": "menu", - "sort_order": 20, + "sort_order": 10, "status": "enabled", "visible": True, "cacheable": False, @@ -280,8 +267,8 @@ DEFAULT_MENUS: list[dict[str, object]] = [ ] ROLE_MENU_BINDINGS: dict[str, list[str]] = { - "admin": ["dashboard", "admin.users", "admin.roles", "admin.menus", "admin.system_params", "admin.power_lines", "admin.lightning_currents", "admin.lightning_distribution", "admin.workers", "admin.task_monitor", "admin.atp_models", "admin.files", "admin.elevation", "admin.syslog", "admin.wine_runner"], - "user": ["dashboard"], + "admin": ["admin.users", "admin.roles", "admin.menus", "admin.system_params", "admin.power_lines", "admin.lightning_currents", "admin.lightning_distribution", "admin.workers", "admin.task_monitor", "admin.atp_models", "admin.files", "admin.elevation", "admin.syslog", "admin.wine_runner"], + "user": [], } diff --git a/api/app/services/topic_registry.py b/api/app/services/topic_registry.py index f26d773..e63a6fd 100644 --- a/api/app/services/topic_registry.py +++ b/api/app/services/topic_registry.py @@ -17,7 +17,6 @@ TOPIC_RULES: dict[str, TopicRule] = { "system": TopicRule(allow_any_authenticated_user=True, auto_subscribe=True), "auth": TopicRule(allow_any_authenticated_user=True, auto_subscribe=True), "notifications": TopicRule(allow_any_authenticated_user=True, auto_subscribe=True), - "admin.dashboard": TopicRule(allow_any_authenticated_user=True), "admin.users": TopicRule(any_permission_codes={"user.manage"}), "admin.roles": TopicRule(any_permission_codes={"role.read", "role.manage"}), "admin.menus": TopicRule(any_permission_codes={"menu.read", "menu.manage"}), diff --git a/memory/2026-05-01.md b/memory/2026-05-01.md index a7745da..4bc2684 100644 --- a/memory/2026-05-01.md +++ b/memory/2026-05-01.md @@ -764,3 +764,51 @@ - 验证: - 代码走读确认 `seed_service._default_file_storage_backends/_default_file_storage_mounts` 基于 `settings.minio_enabled` 分支,默认行为已切换到 MinIO。 + +## Work Log - 去掉仪表盘页面(2026-05-02) + +- 背景: + - Issue `FL-166` 要求去掉后台“仪表盘页面”。 + +- 本次改动(最小闭环): + - 前端路由与默认跳转收口: + - `web/src/middleware.ts` + - `/admin`、`/dashboard` 统一重定向到 `/users`; + - 历史 `/admin/**` 仍保持去前缀重定向; + - 无前缀路径继续 rewrite 到 `app/admin/**`。 + - `web/src/app/page.tsx` + - 登录态从跳转 `/dashboard` 改为跳转 `/users`。 + - `web/src/app/admin/page.tsx` + - 仪表盘页面改为纯重定向页(`router.replace("/users")`),不再渲染工作台卡片。 + - `web/src/app/admin/layout.tsx` + - `/admin` 归一化路径改为 `/users`; + - 账号下拉“后台首页”改为“用户管理”(`/users`)。 + - `web/src/app/admin/roles/page.tsx` + - 无权限兜底按钮改为返回 `/users`。 + - `web/src/app/admin/menus/page.tsx` + - 受保护菜单集合移除 `dashboard`。 + - 后端菜单与权限链路清理: + - `api/app/services/seed_service.py` + - 删除 `dashboard` 种子菜单; + - `admin`/`user` 默认角色菜单绑定删除 `dashboard`; + - `admin.users` 排序调整为首位(`sort_order=10`)。 + - `api/app/services/admin_service.py` + - `REMOVED_MENU_CODES` 增加 `dashboard`; + - 菜单删除保护集合移除 `dashboard`。 + - `api/app/services/legacy_authz_service.py` + - `DISABLED_MENU_CODES` 增加 `dashboard`; + - `MENU_CODE_PERMISSION_MAP` 删除 `dashboard` 映射。 + - `api/app/services/legacy_admin_rbac_service.py` + - `REMOVED_MENU_CODES` 增加 `dashboard`; + - `PROTECTED_MENU_CODES` 移除 `dashboard`。 + - `api/app/services/topic_registry.py` + - 删除 `admin.dashboard` 主题规则。 + - 记忆更新: + - `MEMORY.md` 新增“仪表盘下线口径(2026-05-02)”,并同步修正历史 `/dashboard` 默认入口描述为 `/users`。 + +- 验证: + - `rg -n "dashboard|/dashboard"` 检查运行代码,确认仅保留兼容重定向/过滤语义,不再保留仪表盘页面入口逻辑。 + +- 风险与影响: + - 历史收藏的 `/dashboard` 链接会跳转到 `/users`(兼容行为)。 + - 默认 `user` 角色在无其他菜单绑定时,后台菜单可能为空;该改动符合“移除仪表盘菜单”的目标口径。 diff --git a/web/src/app/admin/layout.tsx b/web/src/app/admin/layout.tsx index adff23a..17a5774 100644 --- a/web/src/app/admin/layout.tsx +++ b/web/src/app/admin/layout.tsx @@ -63,7 +63,7 @@ function normalizeAdminPath(path: string | null): string | null { return path; } if (path === "/admin" || path === "/admin/") { - return "/dashboard"; + return "/users"; } if (path.startsWith("/admin/")) { return path.slice("/admin".length); @@ -272,7 +272,7 @@ export default function AdminLayout({ children }: { children: ReactNode }) { { key: "home", icon: , - label: 后台首页, + label: 用户管理, }, { key: "logout", diff --git a/web/src/app/admin/menus/page.tsx b/web/src/app/admin/menus/page.tsx index 5f8901a..134f420 100644 --- a/web/src/app/admin/menus/page.tsx +++ b/web/src/app/admin/menus/page.tsx @@ -57,7 +57,6 @@ const SORT_OPTIONS: Array<{ value: SortKey; label: string }> = [ ]; const PROTECTED_MENU_CODES = new Set([ - "dashboard", "admin.users", "admin.roles", "admin.menus", diff --git a/web/src/app/admin/page.tsx b/web/src/app/admin/page.tsx index f6eeb0c..8033e3f 100644 --- a/web/src/app/admin/page.tsx +++ b/web/src/app/admin/page.tsx @@ -1,220 +1,14 @@ "use client"; -import Link from "next/link"; -import { - AppstoreOutlined, - AuditOutlined, - CodeOutlined, - DatabaseOutlined, - DeploymentUnitOutlined, - FileSearchOutlined, - FolderOpenOutlined, - GlobalOutlined, - GoldOutlined, - LineChartOutlined, - SafetyCertificateOutlined, - SettingOutlined, - TeamOutlined, -} from "@ant-design/icons"; -import { Avatar, Card, Col, Empty, Row, Space, Statistic, Tag, Typography, type CardProps } from "antd"; -import type { ComponentType, ReactNode } from "react"; +import { useEffect } from "react"; +import { useRouter } from "next/navigation"; -import { useAuth } from "@/components/auth-provider"; +export default function AdminEntryRedirectPage() { + const router = useRouter(); -const AntCard = Card as unknown as ComponentType; + useEffect(() => { + router.replace("/users"); + }, [router]); -type DashboardCard = { - href: string; - title: string; - description: string; - category: string; - icon: ReactNode; - visible: (hasPermission: (code: string) => boolean) => boolean; -}; - -const CARDS: DashboardCard[] = [ - { - href: "/users", - title: "用户管理", - description: "查看用户、分配角色、维护账号状态。", - category: "权限", - icon: , - visible: (hasPermission) => hasPermission("user.manage"), - }, - { - href: "/roles", - title: "角色管理", - description: "配置角色并分配菜单可见范围。", - category: "权限", - icon: , - visible: (hasPermission) => hasPermission("role.read") || hasPermission("role.manage"), - }, - { - href: "/menus", - title: "菜单管理", - description: "维护后台导航结构、菜单层级与展示状态。", - category: "权限", - icon: , - visible: (hasPermission) => hasPermission("menu.read") || hasPermission("menu.manage"), - }, - { - href: "/system-params", - title: "系统参数", - description: "维护系统级参数键值、启停状态与变更说明。", - category: "系统", - icon: , - visible: (hasPermission) => hasPermission("system_param.read") || hasPermission("system_param.manage"), - }, - { - href: "/files", - title: "文件管理", - description: "统一管理本地/SFTP/S3 文件目录,支持上传、重命名、移动和下载。", - category: "内容", - icon: , - visible: (hasPermission) => hasPermission("file.read") || hasPermission("file.manage"), - }, - { - href: "/workers", - title: "Worker监控", - description: "查看 Worker 在线状态、并发、活跃队列和每个 Worker 的任务快照。", - category: "协作", - icon: , - visible: (hasPermission) => hasPermission("celery.read") || hasPermission("celery.manage"), - }, - { - href: "/task-monitor", - title: "任务监控", - description: "监控 Celery Worker、队列积压与任务执行状态,快速定位失败与阻塞。", - category: "协作", - icon: , - visible: (hasPermission) => hasPermission("celery.read") || hasPermission("celery.manage"), - }, - { - href: "/power-lines", - title: "线路管理", - description: "维护输电线路与杆塔参数,支持导入导出和风险字段管理。", - category: "电力", - icon: , - visible: (hasPermission) => - hasPermission("line.read") || hasPermission("line.manage") || hasPermission("tower.read") || hasPermission("tower.manage"), - }, - { - href: "/power-lines/atp-viewer", - title: "ATP查看器", - description: "将 ATP 文本转换为 JSON,并用 maxGraph 进行电路图查看。", - category: "电力", - icon: , - visible: (hasPermission) => - hasPermission("line.read") || hasPermission("line.manage") || hasPermission("tower.read") || hasPermission("tower.manage"), - }, - { - href: "/lightning-currents", - title: "雷电幅值统计", - description: "导入雷电流原始序列并自动提取防雷计算参数。", - category: "电力", - icon: , - visible: (hasPermission) => hasPermission("lightning.read") || hasPermission("lightning.manage"), - }, - { - href: "/lightning-distribution", - title: "雷电分布统计", - description: "计算 Ng、空间网格、热力散点和杆塔缓冲区风险。", - category: "电力", - icon: , - visible: (hasPermission) => hasPermission("lightning.read") || hasPermission("lightning.manage"), - }, - { - href: "/elevation", - title: "高程数据管理", - description: "维护高程数据集并执行线路杆塔高程回填任务。", - category: "电力", - icon: , - visible: (hasPermission) => hasPermission("elevation.read") || hasPermission("elevation.manage"), - }, - { - href: "/syslog", - title: "系统日志", - description: "查看鉴权与会话类审计日志,支持动作与用户筛选。", - category: "系统", - icon: , - visible: (hasPermission) => hasPermission("menu.read") || hasPermission("menu.manage"), - }, - { - href: "/wine-runner", - title: "Wine执行器", - description: "通过 Wine 执行 Windows EXE,并实时查看测试日志。", - category: "研发", - icon: , - visible: (hasPermission) => hasPermission("wine.read") || hasPermission("wine.manage"), - }, -]; - -export default function AdminHomePage() { - const { hasPermission, user } = useAuth(); - const visibleCards = CARDS.filter((item) => item.visible(hasPermission)); - const categoryCount = new Set(visibleCards.map((item) => item.category)).size; - - if (visibleCards.length === 0) { - return ( - - - - ); - } - - return ( - - - - - - - - - - - - - - - - - - - -
- - - 模块导航 - - 按权限展示,入口遵循 Ant Design 卡片列表模式。 - - - - {visibleCards.map((item) => ( - - - - - - - - {item.title} - {item.category} - - {item.description} - - - - - - ))} - -
-
- ); + return null; } diff --git a/web/src/app/admin/roles/page.tsx b/web/src/app/admin/roles/page.tsx index a3c7860..8ec5a08 100644 --- a/web/src/app/admin/roles/page.tsx +++ b/web/src/app/admin/roles/page.tsx @@ -340,7 +340,7 @@ export default function AdminRolesPage() { subTitle="你没有访问该页面的权限(需要 role.read)。" extra={( )} /> diff --git a/web/src/app/page.tsx b/web/src/app/page.tsx index 717d4d0..ed4dcd1 100644 --- a/web/src/app/page.tsx +++ b/web/src/app/page.tsx @@ -24,7 +24,7 @@ export default function Home() { useEffect(() => { if (!initializing && user) { - router.replace("/dashboard"); + router.replace("/users"); } }, [initializing, router, user]); diff --git a/web/src/middleware.ts b/web/src/middleware.ts index aea1895..ef52a48 100644 --- a/web/src/middleware.ts +++ b/web/src/middleware.ts @@ -32,22 +32,26 @@ export function middleware(request: NextRequest) { // Keep backward compatibility for existing /admin links. if (pathname === "/admin" || pathname === "/admin/") { const url = request.nextUrl.clone(); - url.pathname = "/dashboard"; + url.pathname = "/users"; + return NextResponse.redirect(url); + } + if (pathname === "/dashboard" || pathname === "/dashboard/") { + const url = request.nextUrl.clone(); + url.pathname = "/users"; return NextResponse.redirect(url); } if (pathname.startsWith("/admin/")) { const url = request.nextUrl.clone(); - url.pathname = pathname.slice("/admin".length) || "/dashboard"; + url.pathname = pathname.slice("/admin".length) || "/users"; return NextResponse.redirect(url); } // New public URLs without /admin are internally rewritten to existing routes. const url = request.nextUrl.clone(); - url.pathname = pathname === "/dashboard" ? "/admin" : `/admin${pathname}`; + url.pathname = `/admin${pathname}`; return NextResponse.rewrite(url); } export const config = { matcher: "/:path*", }; -