chore(openspec): track fe-admin-layout implementation
sysmgr-admin 布局壳:Element Plus + 可配置布局/主题系统(5 种模式 + 主题色 + 暗黑 + 折叠/宽度 + 固定顶栏/底栏 + 面包屑/多页签/全屏/手风琴)。任务全部完成,待归档。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
1790fb30a8
commit
6bb227d635
2
openspec/changes/fe-admin-layout/.openspec.yaml
Normal file
2
openspec/changes/fe-admin-layout/.openspec.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-10-10
|
||||
109
openspec/changes/fe-admin-layout/design.md
Normal file
109
openspec/changes/fe-admin-layout/design.md
Normal file
@ -0,0 +1,109 @@
|
||||
## 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 缓存(后续再议)。
|
||||
37
openspec/changes/fe-admin-layout/proposal.md
Normal file
37
openspec/changes/fe-admin-layout/proposal.md
Normal file
@ -0,0 +1,37 @@
|
||||
## Why
|
||||
|
||||
Phase 0 前端只有登录页与路由守卫,登录后没有真正的后台布局壳。Phase 1 的管理后台需要一套可配置的布局框架——它是所有后台页面的容器,支持多种布局/导航模式、主题色、暗黑模式等。先搭好这个地基,后续用户/角色/资源等页面才有地方挂。
|
||||
|
||||
## What Changes
|
||||
|
||||
- **集成 Element Plus**:走 on-demand(`unplugin-vue-components` 的 `ElementPlusResolver` + 组件样式按需),配置中文 locale。
|
||||
- **布局壳**:替换 `src/layouts/default.vue`;布局/全局组件统一放 `src/components/`(自动注册目录),命名 `ty-` 前缀 + 中划线(`ty-header.vue` → `<ty-header>`),充分复用 auto-import。
|
||||
- **组件库友好**:组件 props/emits 显式类型化、props 驱动、与 Pinia store 解耦,为将来抽取独立组件库打基础。
|
||||
- **布局与导航模式**:支持 3 种布局(上下/纯上下/左右)× MixBar 开关,共 5 种呈现;MixBar 固定最左;「左右」无顶栏;左侧菜单支持 collapse/expand。
|
||||
- **主题色**:颜色选择器自定义,默认 `#2E7FA3`,运行时覆盖 Element Plus 主色 CSS 变量。
|
||||
- **暗黑模式**:明亮/暗黑切换。
|
||||
- **顶栏固定**:可固定/不固定;纯左右布局(无顶栏)时该选项禁用。
|
||||
- **侧边菜单宽度**:200–340 可调。
|
||||
- **底部版权**:显示「云南投云科技有限公司」,可固定。
|
||||
- **补充设置**:面包屑、多页签(tabs)、顶栏全屏、菜单手风琴。
|
||||
- **持久化与默认值**:布局/主题偏好存 localStorage(Pinia `persistedstate`);默认值集中在代码配置模块(`src/config/layout.ts`),改一处统一生效。
|
||||
- **导航菜单数据**:本任务用占位菜单,但字段结构严格对齐后端 `resource` 实体(`id/code/name/parentCode/type/path/component/icon/sort`),下个任务直接换接口数据。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `frontend/AD/layout`: 管理后台布局壳与主题系统(布局/导航模式、主题色、暗黑、折叠、宽度、固定顶栏/底栏、面包屑/多页签/全屏/手风琴、持久化)
|
||||
|
||||
### Modified Capabilities
|
||||
(无)
|
||||
|
||||
## Impact
|
||||
|
||||
- 前端 `sources/sysmgr-admin`:
|
||||
- 新增依赖 `element-plus`(+ `@element-plus/icons-vue` 如需)
|
||||
- `vite.config.ts`:`Components`/`AutoImport` 加 `ElementPlusResolver`
|
||||
- 新增 `src/components/ty-*.vue` 组件(ty-header/ty-sidebar/ty-mixbar/ty-tabs/ty-breadcrumb/ty-footer/ty-setting/ty-setting-panel)
|
||||
- 新增 `src/stores/modules/app.ts`(布局/主题偏好,persist)+ `src/config/layout.ts`(默认值)+ `src/types/resource.ts`(菜单类型)
|
||||
- 替换 `src/layouts/default.vue` 为布局壳
|
||||
- 主题色/暗黑与现有 `--wm-color-*` 令牌对齐
|
||||
- 后端无变更;本任务菜单为占位数据(字段对齐后端 resource)。
|
||||
@ -0,0 +1,115 @@
|
||||
## Purpose
|
||||
|
||||
定义管理后台布局壳与主题系统的行为:可配置的布局/导航模式、主题色、暗黑模式、侧边栏折叠与宽度、固定顶栏/底栏,以及面包屑/多页签/全屏/手风琴等配套能力。
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 布局与导航模式
|
||||
系统 SHALL 支持 3 种布局(上下 / 纯上下 / 左右)与 MixBar 开关的组合,共 5 种呈现;MixBar 始终固定在布局最左侧;「左右」布局不渲染顶栏。
|
||||
|
||||
#### Scenario: 切换布局即时生效
|
||||
- **WHEN** 用户在布局设置中切换布局或开启/关闭 MixBar
|
||||
- **THEN** 主框架立即按新布局重新排布顶栏、侧边栏与内容区
|
||||
|
||||
#### Scenario: 左右无顶栏
|
||||
- **WHEN** 布局为「左右」
|
||||
- **THEN** 主框架只渲染左侧菜单与内容区,不渲染顶栏
|
||||
|
||||
#### Scenario: 上下有顶栏
|
||||
- **WHEN** 布局为「上下」或「纯上下」
|
||||
- **THEN** 主框架渲染顶栏,其中「上下」顶部放一级导航、左侧放子菜单,「纯上下」仅顶部导航
|
||||
|
||||
### Requirement: 侧边菜单折叠
|
||||
左侧导航菜单 SHALL 支持 collapse(折叠为图标)与 expand(展开显示文字+图标)。
|
||||
|
||||
#### Scenario: 折叠与展开
|
||||
- **WHEN** 用户点击折叠按钮
|
||||
- **THEN** 侧边栏在折叠(仅图标)与展开(文字+图标)之间切换
|
||||
|
||||
### Requirement: 主题色
|
||||
系统 SHALL 支持通过颜色选择器自定义主题色,默认 `#2E7FA3`;选择后运行时更新 Element Plus 主色及其派生色阶。
|
||||
|
||||
#### Scenario: 选择主题色
|
||||
- **WHEN** 用户在设置面板用颜色选择器选取新主题色
|
||||
- **THEN** 全站 Element Plus 主色(按钮/链接/高亮等)即时变为所选颜色,刷新后仍保留
|
||||
|
||||
### Requirement: 暗黑模式
|
||||
系统 SHALL 支持明亮/暗黑模式切换;暗黑模式下切换 Element Plus 深色主题与全局深色令牌。
|
||||
|
||||
#### Scenario: 切换暗黑模式
|
||||
- **WHEN** 用户切换暗黑模式开关
|
||||
- **THEN** 全站即时切换为深色配色,刷新后仍保留
|
||||
|
||||
### Requirement: 顶栏固定
|
||||
顶栏 SHALL 支持固定(内容滚动时顶栏常驻顶部)与不固定;「左右」布局(无顶栏)时该选项禁用。
|
||||
|
||||
#### Scenario: 顶栏固定开关
|
||||
- **WHEN** 用户切换「固定顶栏」
|
||||
- **THEN** 顶栏在固定/随内容滚动之间切换
|
||||
|
||||
#### Scenario: 左右布局禁用
|
||||
- **WHEN** 布局为「左右」
|
||||
- **THEN** 「固定顶栏」选项置灰不可选
|
||||
|
||||
### Requirement: 侧边菜单宽度
|
||||
左侧菜单宽度 SHALL 可调,范围 200–340(单位 px)。
|
||||
|
||||
#### Scenario: 调整宽度
|
||||
- **WHEN** 用户拖动或输入侧边栏宽度
|
||||
- **THEN** 宽度在 200–340 之间变化并即时生效,超界被限制在边界值
|
||||
|
||||
### Requirement: 底部版权固定
|
||||
系统 SHALL 在底部显示版权信息「云南投云科技有限公司」,并支持固定/不固定。
|
||||
|
||||
#### Scenario: 底部版权
|
||||
- **WHEN** 用户开启「固定底部」
|
||||
- **THEN** 版权条常驻视口底部;关闭时随内容滚动
|
||||
|
||||
### Requirement: 布局设置持久化
|
||||
所有布局与主题设置 SHALL 持久化到 localStorage,刷新页面后保持。
|
||||
|
||||
#### Scenario: 刷新保留
|
||||
- **WHEN** 用户调整布局/主题/宽度等后刷新页面
|
||||
- **THEN** 所有设置恢复为用户上次的配置
|
||||
|
||||
### Requirement: 面包屑
|
||||
系统 SHALL 在内容区顶部显示面包屑,随当前路由与菜单层级展示当前位置。
|
||||
|
||||
#### Scenario: 面包屑导航
|
||||
- **WHEN** 用户进入某个菜单页面
|
||||
- **THEN** 面包屑显示当前菜单的层级路径
|
||||
|
||||
### Requirement: 多页签
|
||||
系统 SHALL 支持多页签(tabs)视图:打开的页面保留为页签,可切换与关闭。
|
||||
|
||||
#### Scenario: 页签切换与关闭
|
||||
- **WHEN** 用户打开多个菜单页面
|
||||
- **THEN** 各页面以页签形式保留,可切换、可关闭
|
||||
|
||||
### Requirement: 顶栏全屏
|
||||
系统 SHALL 在顶栏提供全屏切换按钮,进入/退出浏览器全屏。
|
||||
|
||||
#### Scenario: 全屏切换
|
||||
- **WHEN** 用户点击全屏按钮
|
||||
- **THEN** 页面进入浏览器全屏,再次点击退出
|
||||
|
||||
### Requirement: 菜单手风琴
|
||||
侧边菜单 SHALL 支持手风琴模式:同一级只展开一个子菜单。
|
||||
|
||||
#### Scenario: 手风琴展开
|
||||
- **WHEN** 开启手风琴且用户展开某个子菜单
|
||||
- **THEN** 同级的其他已展开子菜单自动收起
|
||||
|
||||
### Requirement: 默认值集中配置
|
||||
布局与主题的默认值 SHALL 在代码中集中配置(单一配置模块),开发者改一处即可统一调整;用户在页面上的修改覆盖默认值并持久化。
|
||||
|
||||
#### Scenario: 改默认值生效
|
||||
- **WHEN** 开发者修改配置模块中的默认主题色或默认布局
|
||||
- **THEN** 未手动改过的用户首次进入时按新默认值渲染;已手动改过的用户仍按自己的持久化设置
|
||||
|
||||
### Requirement: 菜单数据结构对齐后端
|
||||
占位菜单数据 SHALL 采用后端资源实体(`resource`)的字段结构(`id/code/name/parentCode/type/path/component/icon/sort`),不自造字段名。
|
||||
|
||||
#### Scenario: 字段对齐
|
||||
- **WHEN** 占位菜单被替换为后端「当前用户菜单树」接口数据
|
||||
- **THEN** 前端无需改动数据结构即可直接渲染
|
||||
45
openspec/changes/fe-admin-layout/tasks.md
Normal file
45
openspec/changes/fe-admin-layout/tasks.md
Normal file
@ -0,0 +1,45 @@
|
||||
## 1. Element Plus 集成
|
||||
|
||||
- [x] 1.1 安装 element-plus(及 @element-plus/icons-vue)
|
||||
- [x] 1.2 vite.config.ts:`Components`/`AutoImport` 增加 `ElementPlusResolver`(on-demand + sass 样式)
|
||||
- [x] 1.3 App.vue 用 `<el-config-provider :locale="zhCn">` 包裹,配中文 locale
|
||||
- [x] 1.4 冒烟:页面上 el-button 能渲染、ElMessage 可调用
|
||||
|
||||
## 2. 类型与默认值配置
|
||||
|
||||
- [x] 2.1 `src/types/resource.ts`:`ResourceType` 与 `ResourceNode` 类型,字段严格对齐后端 resource(id/code/name/parentCode/type/path/component/icon/sort + children)
|
||||
- [x] 2.2 `src/config/layout.ts`:`defaultLayout` 集中配置默认值(themeColor `#2E7FA3`、sidebarWidth 210 等)
|
||||
- [x] 2.3 `src/config/menu.ts`:占位菜单树(字段对齐后端,已组装 children)
|
||||
|
||||
## 3. 布局/主题状态(app store)
|
||||
|
||||
- [x] 3.1 `src/stores/modules/app.ts`:以 `defaultLayout` 作初始值,开启 persist;含 layout/mixbar/themeColor/isDark/headerFixed/sidebarWidth/footerFixed/sidebarCollapsed/showBreadcrumb/showTabs/showFullscreen/menuAccordion
|
||||
|
||||
## 4. 主题系统
|
||||
|
||||
- [x] 4.1 主题色工具:由主色计算 light-3/5/7/8/9、dark-2,写入 `:root` 的 `--el-color-primary*`
|
||||
- [x] 4.2 `applyTheme()`:统一切换 `html.dark` class 与 `data-theme`(对齐现有 `--wm-color-*` 深色令牌)
|
||||
- [x] 4.3 监听 store 的 themeColor/isDark 变化自动应用
|
||||
|
||||
## 5. 布局壳组件(src/components/ty-*.vue,kebab 命名 + 自动导入,props/emits 显式类型化)
|
||||
|
||||
- [x] 5.1 `ty-header`:顶栏(一级导航或工具栏 + 折叠 + 全屏 + 设置入口 + 用户下拉),响应 headerFixed;props 接收菜单/模式/折叠等,emits 折叠/打开设置
|
||||
- [x] 5.2 `ty-mixbar`:固定最左一级图标栏
|
||||
- [x] 5.3 `ty-sidebar`:左侧菜单(el-menu),支持 collapse/expand、width、accordion
|
||||
- [x] 5.4 `ty-tabs`:多页签(展示/切换/关闭)
|
||||
- [x] 5.5 `ty-breadcrumb`:面包屑
|
||||
- [x] 5.6 `ty-footer`:底部版权「云南投云科技有限公司」,响应 footerFixed
|
||||
- [x] 5.7 替换 `layouts/default.vue` 为布局壳:读 store,按 layout/mixbar 条件渲染并 props/emits 接线(组件不直接依赖 store)
|
||||
|
||||
## 6. 设置组件与面板
|
||||
|
||||
- [x] 6.1 `ty-setting`:设置组件(齿轮触发 + el-drawer 外壳)
|
||||
- [x] 6.2 `ty-setting-panel`:设置面板——布局模式、MixBar、主题色选择器(默认 #2E7FA3)、暗黑、顶栏固定(纯左右禁用)、侧边宽度 slider(200–340)、底部固定、面包屑/多页签/全屏/手风琴开关
|
||||
|
||||
## 7. 验证
|
||||
|
||||
- [x] 7.1 `pnpm dev` 启动,登录后进入布局壳
|
||||
- [x] 7.2 切换 5 种模式即时生效;「左右」无顶栏且「固定顶栏」禁用
|
||||
- [x] 7.3 主题色、暗黑、侧边宽度、折叠、手风琴、多页签、全屏、面包屑、底部版权均生效
|
||||
- [x] 7.4 刷新后设置保留(localStorage);改 `defaultLayout` 后未改过的用户按新默认值渲染
|
||||
- [x] 7.5 `pnpm type-check` 与 `pnpm build:prod` 通过
|
||||
@ -1 +1 @@
|
||||
Subproject commit 7738abbbfd3ddad6401a53a4cdaab643b1cc7908
|
||||
Subproject commit 6ef0b1121c86c01fa75563dbb79c6568a3cf96a4
|
||||
Loading…
x
Reference in New Issue
Block a user