liuyun 5e002cac74 chore(openspec): archive fe-admin-layout, sync frontend layout spec to main
- 归档 fe-admin-layout(管理后台布局壳 + 主题系统)
- 新增主 spec:frontend/AD/layout

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-11 01:09:15 +08:00

6.7 KiB
Raw Permalink Blame History

Context

sysmgr-admin 基于 agarwood 模板,已具备:Vue 3 + Vite 8 + TypeScript、Pinia(pinia-plugin-persistedstate)、vue-router 5(unplugin-vue-router 自动路由 + vite-plugin-vue-layouts)、UnoCSS、unplugin-auto-import + unplugin-vue-components(deep: true, directoryAsNamespace: false)、@iconify/vue、vue-i18n、@vueuse(useDark)。现有 src/layouts/default.vue 是占位 header + router-view;登录页 layout:false。全局设计令牌在 src/assets/scss/settings/_color.scss(--wm-color-*,深色用 [data-theme="dark"])。

动机见 proposal.md。

Goals / Non-Goals

Goals:

  • Element Plus 按需接入,中文 locale。
  • 可配置布局壳(7 种模式)+ 主题色 + 暗黑 + 折叠 + 宽度 + 固定顶栏/底栏 + 面包屑/多页签/全屏/手风琴。
  • 布局/主题偏好 localStorage 持久化;默认值可在代码中集中配置。
  • 组件库友好:组件命名/放置/定义规范化,便于将来抽取独立组件库。

Non-Goals:

  • 不接后端菜单数据(本任务用占位菜单,但字段结构对齐后端)。
  • 不做水印、色弱模式、路由过渡动画(后续再议)。
  • 不改后端。

Decisions

D1. 组件命名与放置(用户约束)

公共/布局/全局组件统一放 src/components/(自动注册目录),目录与文件名均中划线 xx-xxx.vue,组件名 ty- 前缀(投云科技),如 ty-header.vue → <ty-header>。模板直接写 kebab 标签,不手动 import。

  • 组件清单:ty-header / ty-mixbar / ty-sidebar / ty-tabs / ty-breadcrumb / ty-footer / ty-setting(齿轮触发 + drawer 外壳)/ ty-setting-panel(面板)。
  • 依据:Components({ deep: true, directoryAsNamespace: false }) 按文件名注册,ty- 前缀避免与 Element Plus(el-)及业务组件撞名。

D2. 组件库友好定义(用户约束:将来抽独立组件库)

每个组件显式 defineProps 类型化 + defineEmits + 需要时 defineSlots/defineExpose;组件 props 驱动、与 Pinia store 解耦——store 只在 src/layouts/default.vue 壳里读,通过 props 传给组件、组件通过 emits 回传交互。组件不直接 import store,便于整体抽出。

D3. Element Plus 接入:on-demand resolver

unplugin-vue-components 加 ElementPlusResolver({ importStyle: 'sass' });unplugin-auto-import 加 ElementPlusResolver(自动引入 ElMessage 等)。中文 locale 用 <el-config-provider :locale="zhCn"> 包裹 App。

  • 备选:全量引入。放弃:脚手架体积敏感,模板已有 on-demand 基础设施。

D4. 布局状态模型 + 默认值集中配置

状态存 Pinia app store(persist)。默认值不散落在 store 里,而集中在一个配置模块 src/config/layout.ts 导出 defaultLayout,store 用它作初始值。开发者改这一个文件即可统一调整默认值,用户在页面上的修改覆盖默认值并持久化到 localStorage。

defaultLayout(初始值):

{
  layout: 'horizontal',          // horizontal(上下) | top(纯上下) | sidebar(左右)
  mixbar: false,
  themeColor: '#2E7FA3',
  isDark: false,
  headerFixed: true,
  sidebarWidth: 210,             // 200–340
  footerFixed: false,
  sidebarCollapsed: false,
  showBreadcrumb: true,
  showTabs: true,
  showFullscreen: true,
  menuAccordion: false,
}

4 种呈现:top、horizontal、sidebar、sidebar+MixBar。MixBar 仅 sidebar(左右)布局可用,horizontal(上下)无 MixBar。所有布局均有顶栏(含折叠/面包屑/全屏/暗黑/设置/用户等工具),区别只在导航菜单位置:top 顶栏放全量导航、horizontal 顶栏放一级导航 + 侧栏放子菜单、sidebar 顶栏不放菜单(菜单全在侧栏)。顶栏 Logo 区与左侧菜单(或 MixBar+菜单)同宽,侧栏折叠时仅显 Logo。

D5. 主题色运行时覆盖

由所选主色用 JS 计算派生色阶(light-3/5/7/8/9、dark-2),写入 :root 的 --el-color-primary 及派生变量;默认 #2E7FA3 预置。色值计算用 colord 或自写 mix(混白/黑)。

  • 不用编译期 SCSS $colors 覆盖——无法运行时换色。

D6. 暗黑模式与现有令牌对齐

暗黑开关同时:① html 加/去 dark class(Element Plus 深色 css-vars);② html 设 data-theme="dark"/"light"(现有 --wm-color-* 深色令牌)。统一由 applyTheme() 维护,避免两套状态漂移。

D7. 菜单数据结构对齐后端

占位菜单完全按后端 sys_resource / Resource 模型的字段定义,不自造字段。类型 src/types/resource.ts:

export type ResourceType = 'menu' | 'button' | 'api'
export interface ResourceNode {
  id: string
  code: string
  name: string
  parentCode: string | null
  type: ResourceType
  path: string | null
  component: string | null
  icon: string | null
  sort: number
  children?: ResourceNode[]   // 前端由 parentCode 组装,仅用于渲染
}

占位数据内置在 src/config/menu.ts(树已组装好 children),字段名与后端一致(code/name/parentCode/type/path/component/icon/sort),下个任务直接换成「当前用户菜单树」接口返回值。

  • 菜单 icon 字段存 iconify 图标名(如 mdi:home),渲染时复用已有 src/components/icon/icon.vue(type="iconify"),不自建图标体系。

D8. 布局壳结构(store 只在壳层)

src/layouts/default.vue 读 app store,按 layout/mixbar 条件渲染并接线:

  • ty-header(一级导航或工具栏 + 折叠 + 面包屑 + 全屏 + 设置入口 + 用户下拉),props 接收模式/折叠/菜单/面包屑等
  • ty-mixbar(一级图标)、ty-sidebar(el-menu:collapse + width + accordion)
  • ty-tabs(多页签)、ty-footer(版权「云南投云科技有限公司」)、内容区 <router-view>
  • ty-setting / ty-setting-panel(设置抽屉与面板)

布局切换用 Vue 条件渲染 + flex/grid,不引第三方布局库。

Risks / Trade-offs

  • [主题色派生色阶与 Element Plus 视觉一致性] → 用标准 mix 公式生成 light-3/5/7/8/9、dark-2。
  • [props 驱动 vs 开发便利] → 组件更通用但壳层接线略多;为将来抽组件库值得。
  • [暗黑两套令牌漂移] → 集中在 applyTheme() 维护 dark class 与 data-theme。
  • [多页签 keep-alive 复杂度] → 本任务先做展示/切换/关闭,缓存后续再上。

Migration Plan

  • 纯前端新增,无迁移。
  • 部署:合并 → pnpm install(装 element-plus)→ pnpm dev:dev 或构建。
  • 回滚:git revert。

Open Questions

  • 占位菜单图标资源(@iconify vs 本地 svg)后续接后端时一并定。
  • 多页签是否 keep-alive 缓存(后续再议)。