liuyun 6bb227d635 chore(openspec): track fe-admin-layout implementation
sysmgr-admin 布局壳:Element Plus + 可配置布局/主题系统(5 种模式 + 主题色 + 暗黑 + 折叠/宽度 + 固定顶栏/底栏 + 面包屑/多页签/全屏/手风琴)。任务全部完成,待归档。

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

110 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 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`(初始值):
```ts
{
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,
}
```
5 种呈现由 `layout × mixbar` 派生:`top`、`horizontal`(±mixbar)、`sidebar`(±mixbar);`top` 布局忽略 mixbar。顶栏存在性:`horizontal`/`top` 有顶栏,`sidebar`(左右)无顶栏(与用户确认,vertical 与 left 一致、均无顶栏)。
### 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`:
```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 缓存(后续再议)。