From 6bb227d635f494bdd51c3631a2c4d36a0faf1fdc Mon Sep 17 00:00:00 2001 From: liuyun Date: Sun, 11 Oct 2026 00:13:10 +0800 Subject: [PATCH] chore(openspec): track fe-admin-layout implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sysmgr-admin 布局壳:Element Plus + 可配置布局/主题系统(5 种模式 + 主题色 + 暗黑 + 折叠/宽度 + 固定顶栏/底栏 + 面包屑/多页签/全屏/手风琴)。任务全部完成,待归档。 Co-Authored-By: Claude Opus 4.8 --- .../changes/fe-admin-layout/.openspec.yaml | 2 + openspec/changes/fe-admin-layout/design.md | 109 +++++++++++++++++ openspec/changes/fe-admin-layout/proposal.md | 37 ++++++ .../specs/frontend/AD/layout/spec.md | 115 ++++++++++++++++++ openspec/changes/fe-admin-layout/tasks.md | 45 +++++++ sources/sysmgr-admin | 2 +- 6 files changed, 309 insertions(+), 1 deletion(-) create mode 100644 openspec/changes/fe-admin-layout/.openspec.yaml create mode 100644 openspec/changes/fe-admin-layout/design.md create mode 100644 openspec/changes/fe-admin-layout/proposal.md create mode 100644 openspec/changes/fe-admin-layout/specs/frontend/AD/layout/spec.md create mode 100644 openspec/changes/fe-admin-layout/tasks.md diff --git a/openspec/changes/fe-admin-layout/.openspec.yaml b/openspec/changes/fe-admin-layout/.openspec.yaml new file mode 100644 index 0000000..53f62d7 --- /dev/null +++ b/openspec/changes/fe-admin-layout/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-10-10 diff --git a/openspec/changes/fe-admin-layout/design.md b/openspec/changes/fe-admin-layout/design.md new file mode 100644 index 0000000..6e16a12 --- /dev/null +++ b/openspec/changes/fe-admin-layout/design.md @@ -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` → ``。模板直接写 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 用 `` 包裹 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`(版权「云南投云科技有限公司」)、内容区 `` +- `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 缓存(后续再议)。 diff --git a/openspec/changes/fe-admin-layout/proposal.md b/openspec/changes/fe-admin-layout/proposal.md new file mode 100644 index 0000000..c5c487a --- /dev/null +++ b/openspec/changes/fe-admin-layout/proposal.md @@ -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` → ``),充分复用 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)。 diff --git a/openspec/changes/fe-admin-layout/specs/frontend/AD/layout/spec.md b/openspec/changes/fe-admin-layout/specs/frontend/AD/layout/spec.md new file mode 100644 index 0000000..9438a89 --- /dev/null +++ b/openspec/changes/fe-admin-layout/specs/frontend/AD/layout/spec.md @@ -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** 前端无需改动数据结构即可直接渲染 diff --git a/openspec/changes/fe-admin-layout/tasks.md b/openspec/changes/fe-admin-layout/tasks.md new file mode 100644 index 0000000..6fcbcd1 --- /dev/null +++ b/openspec/changes/fe-admin-layout/tasks.md @@ -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 用 `` 包裹,配中文 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` 通过 diff --git a/sources/sysmgr-admin b/sources/sysmgr-admin index 7738abb..6ef0b11 160000 --- a/sources/sysmgr-admin +++ b/sources/sysmgr-admin @@ -1 +1 @@ -Subproject commit 7738abbbfd3ddad6401a53a4cdaab643b1cc7908 +Subproject commit 6ef0b1121c86c01fa75563dbb79c6568a3cf96a4