web-admin 门户化布局改造 — 设计文档
日期: 2026-06-20
状态: 已实现(2026-06-20)
范围: web-admin 管理端框架(布局 + 菜单展示 + 权限联动)
UI 规范: docs/web-admin-ui-spec-fresh-mint.md(主色 #10b981、Fresh Mint Token)
目标
将 web-admin 从「左侧全量菜单 + 顶栏工具条」改为 一体化门户布局:
- 顶栏展示 Logo、「一体化门户」、6 个一级模块卡片(icon 上 / 名称下),选中态绿底白字。
- 下方分左右:左侧为当前模块的二/三级菜单;右侧为 WorkTab + 内容区。
- 移除顶栏的菜单折叠、面包屑、刷新按钮;移除【控制台】一级菜单。
- 一级 / 二级 / 三级菜单仍按角色与
menuCode 权限过滤显示。
已确认决策
| # |
决策 |
说明 |
| D1 |
实现方式 |
新增 MenuTypeEnum.PORTAL 专用布局模式(方案 A),不魔改 TOP_LEFT / DUAL_MENU |
| D2 |
权限边界(0/1 模块) |
始终保留顶栏卡片区域:0 个可见模块时展示「暂无可用模块」空态;1 个模块时仍展示 1 张卡片 |
| D3 |
一级菜单清单 |
CRM 管理、报销管理、地磅管理、运输管理、工作流管理、系统管理(共 6 个,不含控制台) |
| D4 |
一级展示顺序 |
CRM → 报销 → 地磅 → 运输 → 工作流 → 系统(前端常量排序,不依赖路由文件注册顺序) |
| D5 |
控制台 |
从侧栏与顶栏卡片中移除;默认首页改为第一个有权限模块的首个可访问叶子页 |
| D6 |
顶栏右侧 |
保留用户菜单;全屏按钮保留;折叠 / 面包屑 / 刷新移除 |
| D7 |
侧栏 |
去掉 Logo 与「一体化门户」品牌区;去掉顶栏式折叠 icon;侧栏本身不新增折叠按钮(固定展开,宽度沿用 menuOpenWidth 默认 230px) |
| D8 |
OpenSpec 侧栏 icon |
一级卡片有 icon;左侧二/三级菜单仍遵守「仅一级有 icon、更深层仅文字」(SidebarSubmenu 现有 level === 0 规则) |
非目标(本期不做)
- 设置面板中保留/切换 Left、Top、Mixed、Dual 等旧布局(PORTAL 为唯一产品布局,设置项可隐藏或锁定)
- 小程序端布局改造
- 权限后端 API 改造(沿用
MenuProcessor + fetchGetMenuCodes)
- 移动端完整适配(本期仅保证
<800px 不严重错位;横滑卡片 + 抽屉侧栏可作为 follow-up)
- Demo 菜单独立提升为一级模块
一、目标布局
┌────────────────────────────────────────────────────────────────────────┐
│ [Logo] 一体化门户 [CRM] [报销] [地磅] [运输] [工作流] [系统] [用户] │ ← ArtPortalHeader(全宽)
│ ↑ 卡片:icon 上 / 名称下;选中 #10b981 底 + 白字 │
├──────────────┬─────────────────────────────────────────────────────────┤
│ 二级 / 三级 │ ArtWorkTab(标签页) │
│ 菜单树 ├─────────────────────────────────────────────────────────┤
│ ArtPortal │ ArtPageContent(业务页面) │
│ Sidebar │ │
└──────────────┴─────────────────────────────────────────────────────────┘
DOM 结构(views/index/index.vue)
.app-layout-portal
├── #app-portal-header → ArtPortalHeader
└── #app-portal-body → flex row
├── #app-sidebar → ArtPortalSidebar(改造 art-sidebar-menu)
└── #app-workspace → flex column
├── #app-worktab → ArtWorkTab(从 art-header-bar 迁出)
└── #app-content → ArtPageContent
二、一级模块卡片(ArtPortalTopNav)
2.1 数据来源
- 输入:
menuStore.menuList 经 门户一级过滤器 处理后的列表。
- 过滤器职责:
- 排除控制台(
path === '/dashboard' 或 meta.title === '控制台')。
- 排除
meta.isHide === true 的项。
- 按 D4 常量顺序排序。
- 若项无可见子节点且自身不可导航 → 不渲染卡片(与
MenuProcessor.filterEmptyMenus 结果一致)。
2.2 卡片 UI
| 属性 |
值 |
| 布局 |
纵向 flex,居中;icon 在上(20–24px),标题在下(12–13px) |
| 间距 |
卡片之间 gap: 8–12px;卡片内 padding 约 8px 12px |
| 圆角 |
8px(与规范按钮/输入圆角一致) |
| 默认 |
透明底;icon/文字 --text / --text-muted |
| 选中 |
background: var(--primary);icon 与文字 #fff |
| 激活判定 |
复用 ArtMixedMenu.isMenuItemActive:当前路由(或 meta.activePath)落在该模块子树内 |
2.3 交互
- 点击卡片:
router.push(module.redirect || firstAccessibleChildPath(module))。
- 若当前路由已属于该模块:仅更新选中态,不强制跳转。
- 0 个可见模块:顶栏卡片区展示 muted 文案「暂无可用模块」(不隐藏顶栏)。
- 1 个可见模块:仍展示 1 张卡片(D2)。
2.4 一级模块与路由前缀映射
| 卡片名称 |
路由 path 前缀 |
路由模块 |
| CRM 管理 |
/crm |
sqxp.ts |
| 报销管理 |
/bx |
sqxp.ts |
| 地磅管理 |
/db |
sqxp.ts |
| 运输管理 |
/pl |
pl.ts |
| 工作流管理 |
/workflow |
sqxp.ts |
| 系统管理 |
/system |
sqxp.ts |
三、左侧菜单(ArtPortalSidebar)
3.1 渲染范围
- 仅渲染 当前激活一级模块 的
children(二/三级)。
- 激活模块解析:
const top = '/' + route.path.split('/')[1],在过滤后的一级列表中匹配。
- iframe 路由:沿用
art-sidebar-menu 现有 findIframeMenuList 逻辑。
3.2 移除内容
- 侧栏 header 区(Logo + 系统名)。
- 双列菜单 / 折叠按钮 /
menu-model 遮罩(PORTAL 模式不启用 DUAL_MENU / LEFT 分支)。
- 顶栏菜单折叠联动(
visibleMenu / menuOpen 切换对 PORTAL 无 UI 入口;menuOpen 保持 true)。
3.3 子菜单组件
- 继续复用
SidebarSubmenu + filterSidebarRoutes。
ElMenu 默认展开:保留现有 syncExpandedMenus / defaultOpenedMenus 逻辑。
四、顶栏(ArtPortalHeader)
4.1 左侧
ArtLogo(点击跳转默认首页,见 §五)。
- 文案「一体化门户」(
AppConfig.systemInfo.name,已为该值)。
4.2 中间
ArtPortalTopNav(flex-1,卡片可横向滚动;模块多时可加 ElScrollbar)。
4.3 右侧
- 全屏按钮(可选保留)。
ArtUserMenu。
- 不渲染:折叠、刷新、面包屑、快速入口、通知、语言、主题切换(与现 header 隐藏项一致)。
4.4 WorkTab 迁移
- 从
art-header-bar/index.vue 移除 <ArtWorkTab />。
- 挂载到
#app-workspace 顶部(ArtWorkTab 组件本身不改业务逻辑)。
- 样式:
worktab-strip 的 mb-3 与 workspace padding 对齐 Fresh Mint 内容区 rhythm。
五、菜单数据与权限
5.1 现有链路(保持不变)
beforeEach → MenuProcessor.getMenuList()
→ filterMenuByRoles / filterMenuByCodes
→ filterEmptyMenus
→ menuStore.setMenuList
→ RouteRegistry.register
5.2 前端增强
| 位置 |
增强 |
MenuProcessor.processFrontendMenu |
过滤掉 Dashboard / 控制台节点 |
新建 utils/router/portal-menu.ts |
PORTAL_TOP_MODULE_ORDER、filterPortalTopModules()、resolveActivePortalModule()、getFirstAccessiblePath() |
menuStore.setMenuList |
设置 homePath 时跳过 /dashboard,使用 getFirstSecondLevelMenuPath(已有) |
5.3 三级权限行为
| 层级 |
机制 |
| 一级卡片 |
MenuProcessor 输出 + filterPortalTopModules;filterMenuByCodes level=1 保留「有可见子节点」的父级 |
| 二级 |
filterSidebarRoutes |
| 三级 |
SidebarSubmenu 递归 + filterSidebarRoutes |
5.4 后端菜单(backend 模式)
- SQL 隐藏或删除 Dashboard 对应
sys_menu 记录(与前端路由同步)。
- 确认 6 个一级菜单在
sys_menu 中 parent_id 指向门户 Web 根节点;运输管理已更名见 docs/sql/20260620_pl_menu_rename_hide.sql。
- 新增迁移脚本:
docs/sql/20260620_portal_hide_dashboard.sql(建议)。
六、配置与枚举
| 文件 |
变更 |
enums/appEnum.ts |
新增 MenuTypeEnum.PORTAL = 'portal' |
config/setting.ts |
默认 menuType: MenuTypeEnum.PORTAL;showMenuButton/showRefreshButton/showCrumbs: false |
config/modules/headerBar.ts |
menuButton/refreshButton/breadcrumb.enabled: false |
store/modules/setting.ts |
支持 PORTAL 分支(若 layout 判断分散在各组件,统一用 isPortalLayout computed) |
views/index/index.vue |
PORTAL 时使用新 DOM 结构;旧 LEFT 结构可保留供模板升级对比或后续删除 |
七、路由与首页
7.1 控制台处理
sqxp.ts:Dashboard 路由设 meta.isHide: true 或整段注释/删除(与 SQL 同步)。
- 登录后默认跳转:第一个有权限模块的首个叶子页(如
/crm/...)。
7.2 路由顺序
routeModules 合并顺序不影响 UI(由 PORTAL_TOP_MODULE_ORDER 控制)。
- 文档注释更新:
sqxp.ts 头部说明去掉「控制台」。
八、样式要点
- 顶栏高度:建议
64–72px(略高于现 h-15 以容纳卡片)。
- 顶栏背景:
var(--default-bg-color) / 白底 + 底部分割线 var(--art-card-border)。
- 一级卡片选中色:
var(--primary) #10b981,勿使用小程序色 #0a9f6d。
- 下方 body:
height: calc(100vh - 顶栏高度);workspace 内容区 padding 沿用 --mint-content-padding。
- 侧栏宽度:230px(
menuOpenWidth 默认值)。
九、测试计划
| 场景 |
预期 |
| super_admin |
6 张卡片 + 对应侧栏全量 |
| admin 仅有 bx 权限 |
顶栏仅 1 张「报销管理」卡片;侧栏仅 bx 子菜单 |
| admin 无任何业务 menuCode |
顶栏卡片区显示「暂无可用模块」 |
| 点击 CRM 卡片 |
路由进入 CRM 首个可访问页;卡片选中;侧栏切换为 CRM 树 |
| 从 CRM 页内跳转 bx 页 |
顶栏选中态自动切到报销 |
直接 URL 访问 /pl/plan |
运输卡片选中;侧栏为 pl 子树 |
控制台 /dashboard |
不可从菜单进入;直接访问按路由守卫处理(404 或重定向首页) |
| WorkTab |
开/关/切换标签正常;位置在内容区上方 |
| 侧栏二/三级 |
无 icon,仅文字(OpenSpec) |
十、影响文件清单
新建
components/core/layouts/art-portal-header/index.vue
components/core/layouts/art-portal-top-nav/index.vue
utils/router/portal-menu.ts
docs/sql/20260620_portal_hide_dashboard.sql(建议)
修改
views/index/index.vue、views/index/style.scss
components/core/layouts/art-menus/art-sidebar-menu/index.vue(PORTAL 分支)
components/core/layouts/art-header-bar/index.vue(移除 WorkTab;或 PORTAL 下不再使用此组件)
enums/appEnum.ts、config/setting.ts、config/modules/headerBar.ts
router/core/MenuProcessor.ts
router/modules/sqxp.ts
可选
components/core/layouts/art-settings-panel:隐藏菜单布局切换或锁定 PORTAL
十一、风险
- WorkTab 样式错位:迁移后需目视检查 tab-card / tab-google 两种
tabStyle。
- 跨模块 deep link:书签直达子页时,顶栏选中态依赖
isMenuItemActive,需覆盖 meta.activePath。
- 模板升级:Art Design Pro 上游更新 layout 时,PORTAL 为独立分支,合并冲突集中在
index.vue。
- 窄屏:6 卡片可能溢出,顶栏需横向滚动;侧栏
<800px 现有 fixed 策略待 follow-up。
十二、后续(非本期)
- 移动端:顶栏卡片横滑 + 侧栏抽屉。
- 设置面板清理旧 layout 选项。
- OpenSpec
ui-conventions 补充「门户布局」章节(若需长期规范)。