sysmgr-admin 布局壳:Element Plus + 可配置布局/主题系统(5 种模式 + 主题色 + 暗黑 + 折叠/宽度 + 固定顶栏/底栏 + 面包屑/多页签/全屏/手风琴)。任务全部完成,待归档。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
110 lines
6.5 KiB
Markdown
110 lines
6.5 KiB
Markdown
## 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 缓存(后续再议)。
|