diff --git a/AI-Coding/00-Quick-Profile.md b/AI-Coding/00-Quick-Profile.md new file mode 100644 index 0000000..7159b81 --- /dev/null +++ b/AI-Coding/00-Quick-Profile.md @@ -0,0 +1,9 @@ +# 00. 快速画像(给 AI 的 30 秒摘要) + +- **技术栈**:`Vue 3` + `Vue CLI 5` + `TypeScript` + `Pinia` + `Vue Router` + `Element Plus` +- **入口链路**:`src/main.ts` → `setupVab(app)` → `setupI18n(app)` → `setupStore(app)` → `setupRouter(app)` +- **Mock**:开发环境由 `vue.config.js` 将 `mock/index.js` 挂载到 devServer 中间件;路由集合来自 `mock/controller/*` +- **别名**:`@`→`src`,`@vab`→`library`,`~`→根目录,`/#`→`types` +- **用途定位**:既是可运行的后台 Demo,也是可拆分的能力集合(`library/layouts/ library/components/ library/ src/utils/ mock/ src/api/ types/`) + +> 推荐阅读顺序:本文件 → [01-Directory-Map.md](01-Directory-Map.md) → [AI-Reuse-Playbook.md](AI-Reuse-Playbook.md) diff --git a/AI-Coding/01-Directory-Map.md b/AI-Coding/01-Directory-Map.md new file mode 100644 index 0000000..751ac26 --- /dev/null +++ b/AI-Coding/01-Directory-Map.md @@ -0,0 +1,51 @@ +# 01. 仓库地图(Directory Map) + +> 目标:让 AI 快速定位“要复用的能力”在哪个目录、入口是什么、依赖闭包有哪些。 + +## `src/`(应用主代码) + +- `src/main.ts`:应用入口;决定 mock/pwa 与各子系统初始化顺序 +- `src/App.vue`:根组件;包含生产环境禁调试逻辑(`disable-devtool`) +- `src/router/`:路由与权限控制(重点:`index.ts`、`permissions`) +- `src/store/`:Pinia 初始化与业务 store 模块 +- `src/i18n/`:国际化初始化、语言获取与翻译辅助 +- `src/api/`:按域拆分 API 调用函数(示例:`src/api/router.ts`) +- `src/utils/`:通用工具与基础能力(时间格式化、树结构转换、UUID 等) +- `src/views/`:业务页面集合(通常与路由对应) +- `src/icon/`:图标加载(由 `library/index.ts` 里的 `import '@/icon'` 引入) +- `src/config/`:运行时/构建期配置聚合导出(`vue.config.js` 会 `require('./src/config')`) + +## `library/`(模板基础库) + +- `library/index.ts`:`setupVab(app)` 的实现: + - 全局样式:`library/styles/vab.scss` + - 图标注册:`vab-icons` + `@element-plus/icons-vue` + - 背景样式:`library/styles/background/*.scss` + - 插件加载:`library/plugins/**/*.ts` 并 `app.use()` +- `library/build/`:构建期扩展(webpack chain、vue plugins) + +## `library/plugins/`(全局插件) + +- `vab.ts`:全局注入(gp)与事件总线(`$pub/$sub/$unsub`) +- `directive.ts`:指令(`v-permissions`) +- `errorLog.ts`:全局错误捕获(`app.config.errorHandler`) +- `support.ts`:构建信息输出/依赖检查 + +## `library/components/`(可复用 UI 组件库) + +- `Vab*` 前缀:约定为基础组件/布局子组件(菜单、tabs、header、breadcrumb 等) +- `VabTheme`:主题系统入口(主题抽屉/设置面板),依赖 `$pub/$sub` 与 settings store + +## `library/layouts/`(布局体系) + +- `library/layouts/index.vue`:主 Layout 入口(路由壳) +- `library/layouts/VabLayout*`:多套布局可选(垂直/横向/综合/浮动/分栏等) + +## `mock/`(开发期 Mock) + +- `mock/index.js`:devServer 中间件;读取并注册 `mock/controller/*` +- `mock/controller/*.js`:每个文件导出路由数组(`url/type/response`) + +## `types/`(类型声明) + +- `types/*.d.ts`:路由/store/theme/vab 等声明,配合 `/#` 别名使用 diff --git a/AI-Coding/02-Boot-Sequence.md b/AI-Coding/02-Boot-Sequence.md new file mode 100644 index 0000000..cf6dd0f --- /dev/null +++ b/AI-Coding/02-Boot-Sequence.md @@ -0,0 +1,28 @@ +# 02. 关键入口与初始化顺序(Boot Sequence) + +## 入口文件:`src/main.ts` + +AI 需要知道“系统从哪里开始、按什么顺序挂载能力”。关键点: + +1. `validateSecretKey()`:启动前校验(与授权/环境有关) +2. 生产环境 Mock: + - 条件:`process.env.NODE_ENV === 'production'` 且 `baseURL` 不是外链(`!isExternal(baseURL)`) + - 动作:`require('@/utils/static').mockXHR()` +3. 初始化链路: + - `setupVab(app)`(全局样式、图标、插件) + - `setupI18n(app)` + - `setupStore(app)` + - `setupRouter(app).isReady().then(mount)` + +## 全局插件聚合:`library/index.ts` + +`setupVab(app)` 做了这些事: + +- 引入全局样式:`library/styles/vab.scss` +- 注册图标: + - `VabIcon`(来自 `vab-icons`) + - `@element-plus/icons-vue` 全量组件注册 +- 自动加载背景样式:`library/styles/background/*.scss` +- 自动加载插件:`library/plugins/**/*.ts`(默认导出 Vue 插件) + +> 复用提示:自动全量加载在“抽取模块”时容易带入不必要依赖;迁移时应以“最小闭包”为原则(见 [AI-Reuse-Playbook.md](AI-Reuse-Playbook.md))。 diff --git a/AI-Coding/03-Mock-System.md b/AI-Coding/03-Mock-System.md new file mode 100644 index 0000000..89b7f13 --- /dev/null +++ b/AI-Coding/03-Mock-System.md @@ -0,0 +1,23 @@ +# 03. Mock 系统(开发联调与脱网演示) + +## 挂载方式 + +- `vue.config.js`:`devServer.setupMiddlewares = require('./mock')` +- `mock/index.js`: + - 将路由集合转换为 express 形式 + - 使用 `mockjs` 返回数据 + - 监听 `mock/` 变更并热更新(`chokidar`) + +## 路由定义约定(供 AI 生成/扩展) + +在 `mock/controller/*.js` 中,每个路由对象通常包含: + +- `url`:不含 `baseURL` 前缀的路径(中间件会自动拼上 `baseURL`) +- `type`:`get` / `post` / ... +- `response(req, res)`:返回对象或函数 + +## AI 新增业务 API 的推荐动作 + +1) 在 `src/api/.ts` 新增函数(调用 `request({ url, method })`) +2) 在 `mock/controller/.js` 新增对应 mock 路由,路径保持一致 +3) 在模块规格中补充接口契约(建议写到 `openspec-lite/modules/.yaml`) diff --git a/AI-Coding/04-Router-and-Views.md b/AI-Coding/04-Router-and-Views.md new file mode 100644 index 0000000..7820f1f --- /dev/null +++ b/AI-Coding/04-Router-and-Views.md @@ -0,0 +1,46 @@ +# 04. 路由与页面组织(Router & Views) + +- 路由文件:`src/router/index.ts` +- 核心结构: + - `constantRoutes`:登录/注册/404 等公共路由 + - `asyncRoutes`:业务路由,通常以 `Layout` 为根节点容器 + +## Views 目录约定(src/views) + +本仓库页面主要集中在: + +- `src/views/index/`:工作台/仪表盘等示例首页 +- `src/views/vab/`:组件/表单/表格等示例页(演示用途居多) +- `src/views/other/`:杂项示例页(iframe/excel/drag/nested 等) +- `src/views/setting/`:后台管理相关页面(用户/角色/部门/菜单/字典/任务/日志等) +- `src/views/tools/`:工具类页面(如 EyeDropper/SpeechSynthesis) +- `src/views/login/`、`src/views/register/`、`src/views/callback/`:登录链路 +- `src/views/403.vue`、`src/views/404.vue`:错误页 + +路由对页面的引用方式以动态导入为主:`component: () => import('@/views/...')`。 + +## meta 约定(给菜单/面包屑/行为用) + +常见 meta 字段(与组件实现强相关): + +- `meta.title`:菜单/面包屑/页面标题(`VabLanguage` 会用它更新 `document.title`) +- `meta.icon` + `meta.isCustomSvg`:菜单与面包屑图标(`VabMenu`/`VabBreadcrumb`) +- `meta.hidden`:是否从菜单隐藏(`VabMenu`) +- `meta.breadcrumbHidden`:是否从面包屑隐藏(`VabBreadcrumb`) +- `meta.noColumn`:Column 布局下的特殊处理(`VabColumnBar` 会折叠菜单并隐藏 fold 按钮) +- `meta.target`(如 `_blank`):点击菜单时打开方式(`VabMenuItem`) +- `meta.badge` / `meta.dot`:菜单右侧标记(`VabMenuItem`) + +## AI 生成新模块页面的最小流程 + +- 在 `src/views//` 创建页面组件 +- 在 `src/router/index.ts`(或其拆分文件)添加路由记录 +- 如涉及权限/菜单:同步 `meta`(`title/icon/guard/...`) + +建议:新增页面后运行一次 [AI-Coding/Validation.md](AI-Coding/Validation.md) 里的“router ↔ views 对账”脚本,防止路由引用了不存在的 .vue,或新页面忘记挂到路由上。 + +## 验收点 + +- 访问路由能渲染页面 +- 菜单与面包屑符合 `meta` 约定 +- 刷新后路由仍可恢复 diff --git a/AI-Coding/05-Store.md b/AI-Coding/05-Store.md new file mode 100644 index 0000000..381d1c4 --- /dev/null +++ b/AI-Coding/05-Store.md @@ -0,0 +1,16 @@ +# 05. 状态管理(Pinia Store) + +- `src/store/index.ts`:`createPinia()` 与 `app.use(pinia)` +- i18n 语言读取:`src/i18n/index.ts` 通过 `useSettingsStore(pinia)` 获取语言 + +## AI 提取/迁移注意 + +- 迁移 `src/store/index.ts` 时,通常也需要迁移: + - `src/store/modules/settings.ts`(见 `12-Settings-Store.md` / `openspec-lite/modules/store-settings.yaml`) + - `src/store/modules` 下其它基础模块(见 `14-Store-Modules.md`) + - 与其相关的类型(`types/`,即 `/#` 别名指向的目录) + +## 验收点 + +- Store 能正常注入 +- 关键 store(如 settings)能在不报错情况下读取 diff --git a/AI-Coding/06-i18n.md b/AI-Coding/06-i18n.md new file mode 100644 index 0000000..440c179 --- /dev/null +++ b/AI-Coding/06-i18n.md @@ -0,0 +1,17 @@ +# 06. 国际化(i18n) + +- 文件:`src/i18n/index.ts` +- 关键点: + - `legacy: false`(Composition API 模式) + - 内置 `en`,`zh` 为空对象(等待补充) + - 提供 `translate(message)` 辅助函数 + - 导出 Element Plus 语言包:`enLocale` / `zhLocale` + +## 扩展建议 + +- 新增语言:在 `src/i18n/locales/` 添加 `xx.json` 并合并到 `messages` + +## 验收点 + +- 切换语言后页面能按预期渲染 +- Element Plus 语言包切换有效 diff --git a/AI-Coding/07-API.md b/AI-Coding/07-API.md new file mode 100644 index 0000000..6b8a2ca --- /dev/null +++ b/AI-Coding/07-API.md @@ -0,0 +1,17 @@ +# 07. API 层(请求封装与域拆分) + +- 示例:`src/api/router.ts` 调用 `request({ url: '/router/getList', method: 'get' })` +- 请求封装:`src/utils/request.ts`(axios 拦截器、token 注入、401/402/403 处理、错误提示) +- 刷新令牌:`src/api/refreshToken.ts`(402 时重试队列) +- 请求异常入库:通过 `@vab/plugins/errorLog`(见 `library/plugins/errorLog.ts`) + +## AI 复用原则 + +- 以“域”为单位抽取:`src/api/.ts` +- 抽取前先定位 `request` 封装(通常在 `src/utils/request` 或相近位置) +- 若新项目变更 baseURL/token/error 规范,优先在规格里声明差异,然后再做迁移适配 + +## 验收点 + +- 基础请求能发起 +- 错误处理与鉴权逻辑符合项目约束 diff --git a/AI-Coding/08-Alias-and-Types.md b/AI-Coding/08-Alias-and-Types.md new file mode 100644 index 0000000..8cda12b --- /dev/null +++ b/AI-Coding/08-Alias-and-Types.md @@ -0,0 +1,20 @@ +# 08. Alias 与类型(迁移必须同步) + +## Alias + +- `@` → `src` +- `~` → 根目录 +- `/#` → `types` +- `@vab` → `library` +- `@gp` → `library/plugins/vab` + +来源:`vue.config.js` 与 `tsconfig.json`。 + +## 类型声明 + +- `types/*.d.ts`:路由/store/theme/vab 等声明 + +## 迁移到不同构建工具(如 Vite)时的最低要求 + +- alias 需要在新工具里重新配置 +- `types/` 需要进入 tsconfig 的 `include` diff --git a/AI-Coding/09-Plop.md b/AI-Coding/09-Plop.md new file mode 100644 index 0000000..1fd951d --- /dev/null +++ b/AI-Coding/09-Plop.md @@ -0,0 +1,15 @@ +# 09. 代码生成(Plop) + +- 文件:`plopfile.js` +- 生成器:`view` / `curd` / `component` / `mock&api` + +运行: + +```powershell +pnpm run template +``` + +## AI 使用建议 + +- 如果后续 AI 以“生成代码”为策略,可优先复用 plop 模板,而不是从零写 +- 新增生成器前,先在 `openspec-lite/project.yaml` 里补充命名/目录约束 diff --git a/AI-Coding/10-Theme-System.md b/AI-Coding/10-Theme-System.md new file mode 100644 index 0000000..8d5dbb6 --- /dev/null +++ b/AI-Coding/10-Theme-System.md @@ -0,0 +1,38 @@ +# 10. Theme 系统(VabTheme + settings) + +> 目标:让 AI 在抽取/复用 Layout 时,不会漏掉主题抽屉、事件总线与 settings 的主题变量闭包。 + +## 入口与组成 + +- 主题入口组件:`library/components/VabTheme/index.vue` +- 主题抽屉:`library/components/VabTheme/components/VabThemeDrawer.vue` +- 主题设置入口:`library/components/VabTheme/components/VabThemeSetting.vue` +- 主题状态与持久化:`src/store/modules/settings.ts` + +## 关键机制 + +- 事件总线:通过 `library/plugins/vab.ts` 注入 `$pub/$sub/$unsub` + - 打开抽屉:`$pub('theme')` + - 随机换肤:`$pub('random-theme')` +- 主题应用:`useSettingsStore().updateTheme()` + - 动态 `require(@vab/styles/variables/vab-*-variables.module.scss)` + - 将 `vab-` 前缀变量映射到 `--el-` CSS 变量 + - 设置 `body` class(`vab-theme-*`)与背景类 + +## 最小依赖闭包(抽取时必须带上) + +- `library/components/VabTheme/` +- `src/store/modules/settings.ts`(至少 theme/showTheme/saveTheme/resetTheme/updateTheme) +- `library/plugins/vab.ts`(提供 `$pub/$sub` 与 `$baseLoading`) +- `library/styles/variables/`(所有 `vab-*-variables.module.scss`) +- Element Plus + i18n(组件内有 `translate()` 与 el-* 组件) + +## 验收点(smoke) + +- 触发 `$pub('theme')` 抽屉能打开/关闭 +- 保存/重置主题后,刷新页面主题能持久化且变量生效 + +## 常见坑 + +- `updateTheme()` 使用 webpack 风格的动态 `require`:迁移到非 webpack 构建(如部分 Vite 场景)需要等效实现 +- 主题抽屉/设置依赖事件总线注入:未安装 `vab` 插件时会“看起来渲染了但不工作” diff --git a/AI-Coding/11-Plugins-System.md b/AI-Coding/11-Plugins-System.md new file mode 100644 index 0000000..c87cf1f --- /dev/null +++ b/AI-Coding/11-Plugins-System.md @@ -0,0 +1,29 @@ +# 11. Plugins 系统(library/plugins/*) + +> 目标:明确本仓库“全局注入能力/指令/错误处理”的来源,避免抽取组件时漏掉运行时注入。 + +## 插件目录 + +- `library/plugins/vab.ts`:全局 gp 注入 + mitt 事件总线(`$pub/$sub/$baseMessage/...`) +- `library/plugins/directive.ts`:自定义指令(`v-permissions`) +- `library/plugins/errorLog.ts`:全局错误捕获(`app.config.errorHandler`)并写入 store +- `library/plugins/support.ts`:构建信息输出与依赖存在性检查 + +## 安装方式 + +- 正常路径:`library/index.ts` 会自动加载 `library/plugins/**/*.ts` 并 `app.use()`(setupVab 链路内) +- 抽取到目标项目: + - 若不复用 `library/index.ts` 的自动加载逻辑,需要在入口手动 `app.use(plugin)` + +## 最小依赖闭包 + +- `vab.ts`:依赖 Element Plus 全局 API(ElMessage/ElLoading/ElMessageBox/ElNotification)+ lodash + mitt + `src/config` +- `directive.ts`:依赖 `src/utils/permission`(hasPermission 及其权限数据来源) +- `errorLog.ts`:依赖 `src/store/modules/errorLog` 与 `src/config/errorLog` +- `support.ts`:依赖 `__APP_INFO__` 构建注入 + +## 验收点(smoke) + +- `vab`:`$baseMessage('ok')` 能弹出 message;`$pub/$sub` 能收发事件 +- `directive`:模板中 `v-permissions` 不报错且能按权限生效 +- `errorLog`:手动触发异常能写入 errorLog store diff --git a/AI-Coding/12-Settings-Store.md b/AI-Coding/12-Settings-Store.md new file mode 100644 index 0000000..ff5a369 --- /dev/null +++ b/AI-Coding/12-Settings-Store.md @@ -0,0 +1,35 @@ +# 12. Settings Store(主题/布局等全局配置) + +> 目标:把 Theme/Layout 的“根依赖”讲清楚,避免 AI 抽取时只搬组件却漏掉主题变量注入与持久化逻辑。 + +## 入口 + +- `src/store/modules/settings.ts`(`useSettingsStore`) + +## 管理的核心状态 + +- `theme`:包含 `layout/themeName/background/menuWidth/showTheme/showThemeSetting/tabsBarStyle...` +- `device`:`desktop | mobile`(布局响应式需要) +- `collapse`:侧边栏折叠状态 +- `language`:国际化语言 + +## 关键动作 + +- `saveTheme()`:持久化 theme +- `resetTheme()`:恢复默认 theme,并调用 `updateTheme()` +- `updateTheme()`: + - 通过 `@vab/styles/variables/vab-*-variables.module.scss` 读取变量 + - 将 `vab-` 前缀映射到 `--el-`,动态写入 Element Plus CSS 变量 + - 设置 body class:`vab-theme-*`,并按 background 追加 class + - 设置 `--el-left-menu-width` + +## 最小闭包(Theme/Layout 抽取必须带上) + +- `src/store/modules/settings.ts` +- `library/styles/variables/vab-*-variables.module.scss` +- `library/plugins/vab.ts`(Theme 事件总线注入) + +## 常见坑 + +- `updateTheme()` 使用 webpack 风格动态 `require`,迁移到非 webpack 构建器需要等效替代 +- `useCssVar` 的导入方式依赖工程约定(自动导入 vs 显式 import) diff --git a/AI-Coding/13-Component-Inventory.md b/AI-Coding/13-Component-Inventory.md new file mode 100644 index 0000000..33b2675 --- /dev/null +++ b/AI-Coding/13-Component-Inventory.md @@ -0,0 +1,70 @@ +# 13. 组件清单(library/components) + +> 目的:让 AI 在新项目中“选组件/抽组件”时,先从**可复用目录清单**开始,避免漏组件或重复造轮子。 + +## 组件根目录 + +- `library/components/`(约定:每个 `Vab*` 目录是一组可复用组件) + +## 当前组件目录(按仓库实际目录列出) + +- `VabApp`:应用壳/全局 Provider +- `VabAppMain`:主内容区(与 Layout 配合) +- `VabAvatar` +- `VabBreadcrumb` +- `VabCard` +- `VabColorfulCard` +- `VabColumnBar` +- `VabErrorLog` +- `VabFold` +- `VabFooter` +- `VabFullScreen` +- `VabHeader` +- `VabLanguage` +- `VabLink` +- `VabLock` +- `VabLogo` +- `VabMenu` +- `VabNav` +- `VabNotice` +- `VabQueryForm` +- `VabRefresh` +- `VabRouterView` +- `VabSearch` +- `VabSideBar` +- `VabTabs` +- `VabTheme` + +## 抽取建议(最小闭包) + +- 优先使用任务模板:`openspec-lite/tasks/extract-component.yaml` +- 若组件涉及 Layout/Theme:先抽 `layouts` 与 `vab-theme`,再补 `store-settings` 与 `plugin-vab` + +高耦合组件规格(机读): + +- `vab-app-main`:`openspec-lite/modules/vab-app-main.yaml` +- `vab-avatar`:`openspec-lite/modules/vab-avatar.yaml` +- `vab-breadcrumb`:`openspec-lite/modules/vab-breadcrumb.yaml` +- `vab-card`:`openspec-lite/modules/vab-card.yaml` +- `vab-colorful-card`:`openspec-lite/modules/vab-colorful-card.yaml` +- `vab-column-bar`:`openspec-lite/modules/vab-column-bar.yaml` +- `vab-router-view`:`openspec-lite/modules/vab-router-view.yaml` +- `vab-error-log`:`openspec-lite/modules/vab-error-log.yaml` +- `vab-fold`:`openspec-lite/modules/vab-fold.yaml` +- `vab-query-form`:`openspec-lite/modules/vab-query-form.yaml` +- `vab-refresh`:`openspec-lite/modules/vab-refresh.yaml` +- `vab-search`:`openspec-lite/modules/vab-search.yaml` +- `vab-lock`:`openspec-lite/modules/vab-lock.yaml` +- `vab-notice`:`openspec-lite/modules/vab-notice.yaml` +- `vab-logo`:`openspec-lite/modules/vab-logo.yaml` +- `vab-nav`:`openspec-lite/modules/vab-nav.yaml` +- `vab-language`:`openspec-lite/modules/vab-language.yaml` +- `vab-full-screen`:`openspec-lite/modules/vab-full-screen.yaml` +- `vab-footer`:`openspec-lite/modules/vab-footer.yaml` +- `vab-link`:`openspec-lite/modules/vab-link.yaml` + +## 快速定位(grep/语义检索) + +- `library/components//index.vue` +- `VabThemeDrawer` / `VabThemeSetting` +- `VabMenu` / `VabTabs` / `VabSideBar`(通常与路由/权限/store 强相关) diff --git a/AI-Coding/14-Store-Modules.md b/AI-Coding/14-Store-Modules.md new file mode 100644 index 0000000..72b143f --- /dev/null +++ b/AI-Coding/14-Store-Modules.md @@ -0,0 +1,32 @@ +# 14. Store 模块清单(src/store/modules) + +> 目的:让 AI 明确“权限/路由/主题/标签页”等基础能力分别由哪个 store 提供,抽取时不遗漏。 + +## 模块列表(按仓库实际文件列出) + +- `acl.ts`:角色/权限/admin(配合 `hasPermission()`) +- `errorLog.ts`:错误日志收集(配合 `library/plugins/errorLog.ts`) +- `routes.ts`:路由模式/菜单路由设置(前端/后端路由切换) +- `settings.ts`:主题/布局/语言/折叠等全局配置(见 `12-Settings-Store.md`) +- `tabs.ts`:标签页 visitedRoutes 管理 +- `user.ts`:登录/用户信息/登出/重置(联动 acl/routes/tabs/settings) + +对应模块规格(机读): + +- `access-control`:`openspec-lite/modules/access-control.yaml`(覆盖 acl + hasPermission + 指令) +- `store-errorlog`:`openspec-lite/modules/store-errorlog.yaml` +- `store-routes`:`openspec-lite/modules/store-routes.yaml` +- `store-settings`:`openspec-lite/modules/store-settings.yaml` +- `store-tabs`:`openspec-lite/modules/store-tabs.yaml` +- `store-user`:`openspec-lite/modules/store-user.yaml` + +## 最小依赖闭包提示 + +- 权限链路:`acl.ts` + `src/utils/permission.ts` + `library/plugins/directive.ts` +- 路由链路:`routes.ts` + `src/router/*` + `src/utils/routes.ts` +(可选)`src/api/router.ts` +- 主题链路:`settings.ts` + `library/styles/variables/vab-*-variables.module.scss` + +## 验收点(smoke) + +- 能创建 Pinia 并正常读取 settings/acl/user 等 store +- `hasPermission()` 能按 acl 状态返回布尔值 diff --git a/AI-Coding/15-Snippet-Map.md b/AI-Coding/15-Snippet-Map.md new file mode 100644 index 0000000..b667c51 --- /dev/null +++ b/AI-Coding/15-Snippet-Map.md @@ -0,0 +1,115 @@ +# 15. 代码段地图(Snippet Map) + +> 目的:给后续 AI coding 的“最常用代码段”提供**稳定来源与检索方式**,直接指向文件/关键词。 + +## 初始化与自动加载 + +- `setupVab(app)`:`library/index.ts` + - 背景 SCSS 自动加载:`require.context('./styles/background', false, /\\.scss$/)` + - 插件自动加载:`require.context('./plugins', true, /\\.ts$/)` + `app.use(Plugins(key).default)` + +## Theme(主题) + +- 打开抽屉事件:`$pub('theme')`(`library/components/VabTheme/*`) +- 随机换肤事件:`$pub('random-theme')` +- 主题变量注入:`useSettingsStore().updateTheme()`(`src/store/modules/settings.ts`) +- scss module 来源:`@vab/styles/variables/vab-*-variables.module.scss` + +## Plugins(全局注入/事件总线/指令) + +- 事件总线注入:`$pub/$sub/$unsub`(`library/plugins/vab.ts`) +- 权限指令:`v-permissions`(`library/plugins/directive.ts`) +- 权限判断:`hasPermission()`(`src/utils/permission.ts`) + +## Icons(SVG) + +- SVG 自动加载:`require.context('.', true, /\\.svg$/)`(`src/icon/index.ts`) + +## Router/Routes + +- 后端路由转换:`convertRouter()`(`src/utils/routes.ts`) +- 路由模式切换:`authentication === 'all'`(`src/store/modules/routes.ts`) +- 过滤可访问路由:`filterRoutes([...constantRoutes, ...routes], control)`(`src/store/modules/routes.ts`) +- 重置路由:`resetRouter(accessRoutes)`(`src/store/modules/routes.ts` / `src/router`) + +## Router Permissions + +- 路由守卫入口:`setupPermissions(router)`(`src/router/permissions.ts`) +- 白名单/登录拦截开关:`routesWhiteList` / `loginInterception` / `authentication`(`src/router/permissions.ts` / `src/config`) +- 更新标题:`document.title = getPageTitle(to.meta.title)`(`src/router/permissions.ts`) + +## Config + +- 配置聚合入口:`module.exports = { ...cli, ...setting, ...theme, ...network }`(`src/config/index.js`) +- 路由/登录关键开关:`authentication/loginInterception/routesWhiteList/supportVisit`(`src/config/setting.config.js`) +- 菜单关键开关:`defaultOpeneds/uniqueOpened/openFirstMenu`(`src/config/setting.config.js`) +- 网络请求关键开关:`baseURL/successCode/statusName/messageName/requestTimeout`(`src/config/net.config.js`) + +## Router View + +- 刷新当前视图缓存:`$sub('reload-router-view', ...)`(`library/components/VabRouterView/index.vue`) + +## Menu + +- 点击当前菜单刷新:`$pub('reload-router-view')`(`library/components/VabMenu/components/VabMenuItem.vue`) + +## Refresh + +- 刷新按钮触发:`$pub('reload-router-view')`(`library/components/VabRefresh/index.vue`) + +## Search + +- 打开搜索快捷键:`ctrlKey/metaKey + 'k'`(`library/components/VabSearch/index.vue`) +- 历史记录 key:`vab_search_history`(`library/components/VabSearch/index.vue`) + +## Lock + +- 锁屏开关:`handleLock()` / `handleUnLock()`(`library/components/VabLock/index.vue`) +- 直接操作侧边栏:`document.querySelector('.vab-side-bar')`(`library/components/VabLock/index.vue`) + +## Notice + +- 拉取通知:`getList()`(`library/components/VabNotice/index.vue` / `src/api/notice.ts`) + +## Language + +- 语言切换:`useI18n().locale` / `changeLanguage(`(`library/components/VabLanguage/index.vue` / `src/store/modules/settings.ts`) +- 切换后更新标题:`getPageTitle(`(`library/components/VabLanguage/index.vue` / `src/utils/pageTitle.ts`) + +## FullScreen + +- 全屏切换:`useFullscreen().toggle`(`library/components/VabFullScreen/index.vue`) + +## Footer + +- 页脚标题来源:`settings.title`(`library/components/VabFooter/index.vue` / `src/store/modules/settings.ts`) + +## Nav/Breadcrumb + +- 顶部导航聚合:``(`library/components/VabNav/index.vue`) +- 面包屑生成:`handleMatched(`(`library/components/VabBreadcrumb/index.vue` / `src/utils/routes.ts`) + +## Column Bar + +- Column 二级菜单:`partialRoutes` + `defaultOpeneds`(`library/components/VabColumnBar/index.vue` / `src/store/modules/routes.ts` / `src/config`) + +## Avatar/Logo + +- 用户下拉退出:`case 'logout'` + `toLoginRoute(route.fullPath)`(`library/components/VabAvatar/index.vue` / `src/utils/routes.ts`) +- Logo/Title 来源:`logo/title`(`library/components/VabLogo/index.vue` / `src/store/modules/settings.ts`) + +## Link/Fold/Card + +- 外链/内链切换:`isExternal(props.to)`(`library/components/VabLink/index.vue` / `src/utils/validate.ts`) +- 折叠按钮:`toggleCollapse`(`library/components/VabFold/index.vue` / `src/store/modules/settings.ts`) +- Skeleton 卡片:`el-skeleton`(`library/components/VabCard/index.vue`) + +## User/Auth + +- 登出重置闭包:`resetAll()`(`src/store/modules/user.ts`,联动 acl/routes/tabs/resetRouter/removeToken) + +## API/Request + +- axios 实例与拦截器:`axios.create` / `instance.interceptors`(`src/utils/request.ts`) +- 401/402/403 分支:`case 401` / `case 402` / `case 403`(`src/utils/request.ts`) +- token 注入:`Authorization: Bearer`(`src/utils/request.ts`) diff --git a/AI-Coding/16-Config-Keys.md b/AI-Coding/16-Config-Keys.md new file mode 100644 index 0000000..4161be9 --- /dev/null +++ b/AI-Coding/16-Config-Keys.md @@ -0,0 +1,76 @@ +# 16. Config Key Map(src/config) + +> 目的:让 AI 在迁移/新项目开发时,明确“哪些行为由哪些 config key 控制”,并能快速定位 key 的定义与使用点。 + +## 聚合入口 + +- `src/config/index.js` 会把 4 份配置聚合导出: + - `cli.config.js`(构建/CLI 相关) + - `setting.config.js`(通用/登录/路由/菜单/缓存等) + - `theme.config.js`(主题与 UI 开关默认值) + - `net.config.js`(网络请求相关) + +注意:`src/config/*` 可能会被 `vue.config.js` 在 Node 环境 `require()` 读取,因此配置文件应避免使用 `window/document`。 + +## 高影响 keys(按子系统分组) + +### 路由/权限/登录 + +- `authentication`:路由模式(`intelligence` 前端路由 / `all` 后端路由) +- `loginInterception`:是否开启登录拦截(影响 `setupPermissions` 行为) +- `routesWhiteList`:白名单路由(不校验 token) +- `supportVisit`:游客模式 +- `rolesControl`:是否按 `roles` 字段进行角色控制 +- `isHashRouterMode`:hash/history 模式相关逻辑(菜单里对 `_blank` 打开内部路由有分支) +- `publicPath`:路由/跳转时可能需要的 publicPath + +### Token/存储 + +- `tokenName`:token 字段名 +- `tokenTableName`:存储 key 名 +- `storage`:`localStorage/sessionStorage/cookie` +- `recordRoute`:token 失效回到登录页时是否记录本次路由 + +### 页面标题 + +- `title`:系统标题(影响 `getPageTitle`/浏览器标题/雪花屏标题等) +- `titleSeparator`:标题分隔符 +- `titleReverse`:标题是否反转 + +### 菜单/导航体验 + +- `uniqueOpened`:是否只保持一个子菜单展开 +- `defaultOpeneds`:默认展开菜单 path 列表 +- `openFirstMenu`:是否点击一级菜单默认开启二级菜单 +- `debounce`:需要加 loading 层防重复提交的请求标识列表 +- `keepAliveMaxNum`:keep-alive 最大缓存数量 + +### Theme 默认值与开关(theme.config.js) + +- 默认值:`layout/themeName/background/menuWidth/columnStyle/...` +- UI 开关:`showProgressBar/showTabs/showLanguage/showRefresh/showSearch/showTheme/showNotice/showFullScreen/showThemeSetting/showLock/...` + +### 网络请求(net.config.js) + +- `baseURL` / `contentType` / `requestTimeout` +- `successCode` / `statusName` / `messageName` + +### 构建/CLI(cli.config.js) + +- `devPort/outputDir/assetsDir/publicPath` +- `pwa/buildOptimize/noDebugger/lintOnSave` + +## 常见使用点(快速定位) + +- 路由守卫:`src/router/permissions.ts`(`authentication/loginInterception/routesWhiteList/supportVisit` + `showProgressBar`) +- Router 定义:`src/router/index.ts`(`authentication/isHashRouterMode/publicPath`) +- 菜单:`library/components/VabMenu/components/VabMenuItem.vue`(`isHashRouterMode`) +- 路由 store:`src/store/modules/routes.ts`(`authentication/rolesControl`) +- 标题工具:`src/utils/pageTitle.ts`(`titleSeparator/titleReverse`) +- token 工具:`src/utils/token.ts`(`storage/tokenTableName`) +- request:`src/utils/request.ts`(`baseURL/successCode/statusName/messageName/requestTimeout/contentType` 等) + +## 迁移/复用提示(最小闭包) + +- 抽 `router`/`request`/`menu` 等模块时,**优先把 `src/config/*` 一并带走**,避免 key 缺失导致行为变化。 +- 如果目标项目要改 key 名或改配置来源(例如改成 `.env` 或远端配置),建议先在规格里写清“映射关系”和“验收点”。 diff --git a/AI-Coding/AI-Reuse-Playbook.md b/AI-Coding/AI-Reuse-Playbook.md new file mode 100644 index 0000000..10fbdda --- /dev/null +++ b/AI-Coding/AI-Reuse-Playbook.md @@ -0,0 +1,54 @@ +# AI 复用操作手册(轻量化) + +> 目标:在“不改动现有代码”的前提下,用文档约束 AI 从本仓库抽取能力,并确保抽取结果可验证。 + +## A. 选模块(先确定要什么) + +在开始任何代码生成/迁移前,AI 必须先回答: + +- 我要复用的能力类型:`layout | component | plugin | util | store | i18n | api | mock | config` +- 复用方式:`copy-snippet`(复制片段)或 `copy-module`(整目录) +- 目标项目构建工具:`Vue CLI` / `Vite` / 其他 + +> 推荐:优先 `copy-module`(减少遗漏依赖),再做裁剪。 + +## B. 定位入口(必须列出入口文件) + +- 全局能力:`library/index.ts`(`setupVab`) +- 初始化:`src/main.ts` +- 路由:`src/router/index.ts` +- 状态:`src/store/index.ts` +- i18n:`src/i18n/index.ts` +- Mock:`mock/index.js` + +AI 输出中必须包含:入口文件列表 + 为什么需要它们。 + +## C. 最小闭包(必须列出依赖闭包) + +AI 必须同时列出: + +- 直接依赖(import 的文件/包) +- 运行时依赖(例如:全局样式、icons、插件自动加载) +- 类型依赖(`types/` 目录与 `/#`) +- alias 依赖(`@`、`@vab`、`~`、`/#`) + +> 如果依赖闭包不清晰,禁止直接迁移;应先补规格(见 `openspec-lite/`)。 + +## D. 迁移后验收(必须可执行) + +最低验收: + +- `pnpm run serve` 能启动 +- 页面可渲染 +- 路由能跳转 +- i18n/store 能注入 +- Mock(若启用)能命中 + +## E. 输出格式(给 OpenSpec/AI 工具链用) + +当 AI 完成一次抽取/复用任务时,输出必须包含: + +- **变更文件清单**(新增/修改/删除) +- **复用模块清单**(从哪里来、被用在何处) +- **验收命令**(lint/test/build/serve) +- **风险说明**(可能破坏的点:alias、插件自动加载、生产 mock 等) diff --git a/AI-Coding/Pitfalls.md b/AI-Coding/Pitfalls.md new file mode 100644 index 0000000..3af113a --- /dev/null +++ b/AI-Coding/Pitfalls.md @@ -0,0 +1,8 @@ +# 已知陷阱与迁移雷区(Pitfalls) + +- **生产环境禁调试**:`src/App.vue` 使用 `disable-devtool`,可能影响某些测试/调试环境。 +- **生产环境默认 Mock**:`src/main.ts` 中存在“生产启用 mockXHR”的逻辑,做真实项目发布时要确认策略。 +- **配置入口**:`vue.config.js` 通过 `require('./src/config')` 读取配置;`src/config/index.js` 会聚合导出多个子配置。 +- **自动插件加载**:`library/index.ts` 会 `require.context('./plugins', true, /\\.ts$/)` 全量加载;抽取模块时容易带入不需要依赖。 + +> 对应的约束与处理建议:见 [openspec-lite/project.yaml](openspec-lite/project.yaml)。 diff --git a/AI-Coding/README.md b/AI-Coding/README.md new file mode 100644 index 0000000..407d671 --- /dev/null +++ b/AI-Coding/README.md @@ -0,0 +1,56 @@ +# AI-Coding(AI 取材与约束入口) + +> 本目录是“轻量化模板库”形态下的 AI 指南:**不改动现有业务代码**,尽量用文档与规格约束引导 OpenSpec 开发模式的 AI 快速、准确地复用本仓库能力。 + +## 你应该从这里开始读 + +1. [00-Quick-Profile.md](00-Quick-Profile.md) —— 30 秒摘要(栈、入口链路、Mock、alias) +2. [01-Directory-Map.md](01-Directory-Map.md) —— 仓库地图(模块在哪、职责是什么) +3. [02-Boot-Sequence.md](02-Boot-Sequence.md) —— 初始化链路(`src/main.ts` / `setupVab`) +4. [10-Theme-System.md](10-Theme-System.md) —— Theme 系统(VabTheme + settings + 变量注入) +5. [11-Plugins-System.md](11-Plugins-System.md) —— Plugins(gp 注入/指令/错误捕获) +6. [12-Settings-Store.md](12-Settings-Store.md) —— Settings Store(主题/布局等全局配置) +7. [13-Component-Inventory.md](13-Component-Inventory.md) —— 组件清单(library/components) +8. [14-Store-Modules.md](14-Store-Modules.md) —— Store 模块清单(src/store/modules) +9. [15-Snippet-Map.md](15-Snippet-Map.md) —— 代码段地图(常用片段稳定来源) +10. [16-Config-Keys.md](16-Config-Keys.md) —— Config keys 地图(高影响开关与使用点) +11. [AI-Reuse-Playbook.md](AI-Reuse-Playbook.md) —— AI 复用操作手册(最小闭包、迁移步骤、验收) +12. [Pitfalls.md](Pitfalls.md) —— 已知陷阱与迁移雷区 + +## OpenSpec-lite(轻量规格) + +- [openspec-lite/project.yaml](openspec-lite/project.yaml) —— 项目级约束(技术栈/目录/禁止项/验收门槛) +- [openspec-lite/manifest.yaml](openspec-lite/manifest.yaml) —— 模块清单(供 AI 选模块与定位) +- [openspec-lite/tasks/](openspec-lite/tasks/) —— 任务模板(AI 按模板输出文件与验收项) +- [openspec-lite/modules/](openspec-lite/modules/) —— 模块规格(按需逐步补全;先从高频模块开始) + +推荐从这些高频模块规格开始: + +- `setup-vab`:`openspec-lite/modules/setup-vab.yaml` +- `config-system`:`openspec-lite/modules/config-system.yaml` +- `api-system`:`openspec-lite/modules/api-system.yaml` +- `icons`:`openspec-lite/modules/icons.yaml` +- `styles`:`openspec-lite/modules/styles.yaml` +- `access-control`:`openspec-lite/modules/access-control.yaml` +- `layouts`:`openspec-lite/modules/layouts.yaml` +- `ui-components`:`openspec-lite/modules/ui-components.yaml` +- `store-settings`:`openspec-lite/modules/store-settings.yaml` +- `vab-theme`:`openspec-lite/modules/vab-theme.yaml` +- `plugin-vab`:`openspec-lite/modules/plugin-vab.yaml` +- `plop`:`openspec-lite/modules/plop.yaml` +- `plugin-directive`:`openspec-lite/modules/plugin-directive.yaml` +- `plugin-errorlog`:`openspec-lite/modules/plugin-errorlog.yaml` +- `plugin-support`:`openspec-lite/modules/plugin-support.yaml` +- `VabApp`:`openspec-lite/modules/vab-app.yaml` +- `VabMenu`:`openspec-lite/modules/vab-menu.yaml` +- `VabTabs`:`openspec-lite/modules/vab-tabs.yaml` +- `VabHeader`:`openspec-lite/modules/vab-header.yaml` +- `VabSideBar`:`openspec-lite/modules/vab-sidebar.yaml` + +更多模块(例如 `store-user` / `store-routes` / `store-tabs` / `store-errorlog`)已收录在 `openspec-lite/manifest.yaml` 中,可按需从清单选取。 + +## 本目录的“轻量化原则” + +- **不要求改代码**:所有约束优先通过文档/规格落地;如必须改代码,应先在规格里写明原因与影响面。 +- **先可用,再完美**:优先把“模块定位、最小闭包、验收门槛”写清楚。 +- **一切可检索**:每份文档都应包含明确关键词与入口文件路径,方便 AI 语义检索/grep。 diff --git a/AI-Coding/Search-Anchors.md b/AI-Coding/Search-Anchors.md new file mode 100644 index 0000000..2207af3 --- /dev/null +++ b/AI-Coding/Search-Anchors.md @@ -0,0 +1,40 @@ +# 可供 AI 检索的关键锚点(Search Anchors) + +> 这些关键词可用于语义检索或 grep。 + +- 入口:`createApp(App)`、`setupVab(app)`、`setupRouter(app).isReady()` +- setupVab:`require.context('./plugins'`、`require.context('./styles/background'`、`app.component('VabIcon'`、`createHead()` +- Mock:`setupMiddlewares: require('./mock')`、`mockjs`、`responseFake` +- Alias:`alias`、`@vab`、`/#` +- i18n:`createI18n({ legacy: false })`、`translate(` +- Store:`createPinia()`、`useSettingsStore(pinia)` +- Routes Store:`setRoutes(`、`filterRoutes(`、`resetRouter(` +- Router/Views:`component: () => import('@/views/`、`constantRoutes`、`asyncRoutes`、`breadcrumbHidden`、`noColumn`、`isCustomSvg`、`meta.target`、`meta.badge`、`meta.dot` +- Router Guard:`setupPermissions(`、`router.beforeEach`、`routesWhiteList`、`loginInterception`、`authentication`、`supportVisit`、`VabProgress.start()`、`document.title = getPageTitle` +- User Store:`getUserInfo(`、`resetAll(`、`removeToken(` +- Tabs Store:`visitedRoutes`、`noClosable` +- RouterView:`reload-router-view`、`keepAliveNameList`、`Vab[^/]+)/') | ForEach-Object { $_.Groups['name'].Value } | Sort-Object -Unique; +Compare-Object -ReferenceObject $componentDirs -DifferenceObject $manifestComponents +``` + +对账 `library/layouts/*` 子目录是否都被 `AI-Coding/openspec-lite/modules/layouts.yaml` 覆盖: + +```powershell +$repo = (Get-Location).Path; +$layoutDirs = Get-ChildItem -LiteralPath "$repo\library\layouts" -Directory | Select-Object -ExpandProperty Name | Sort-Object; +$layoutsSpec = Get-Content -LiteralPath "$repo\AI-Coding\openspec-lite\modules\layouts.yaml" -Raw; +$specDirs = [regex]::Matches($layoutsSpec,'library/layouts/(?VabLayout[^/]+)/') | ForEach-Object { $_.Groups['name'].Value } | Sort-Object -Unique; +Compare-Object -ReferenceObject $layoutDirs -DifferenceObject $specDirs +``` + +对账 `components.d.ts` 里自动导入的 `Vab*` 组件是否都被任一 module spec 覆盖(按源文件路径精确匹配): + +```powershell +$repo = (Get-Location).Path; +$dtsPath = "$repo\library\build\vuePlugins\components.d.ts"; +$specDir = "$repo\AI-Coding\openspec-lite\modules"; + +$dts = Get-Content -LiteralPath $dtsPath -Raw; +$specText = (Get-ChildItem -LiteralPath $specDir -Filter '*.yaml' | ForEach-Object { Get-Content -LiteralPath $_.FullName -Raw }) -join "`n---`n"; + +$imports = [regex]::Matches( + $dts, + "Vab[A-Za-z0-9_]+:\\s*typeof\\s+import\\('(?

[^']+)'\\)\\['default'\\]", + [System.Text.RegularExpressions.RegexOptions]::Multiline +) | ForEach-Object { $_.Groups['p'].Value } | Where-Object { $_ -like '*components*' } | ForEach-Object { + $p = $_; + $p = $p -replace "^\\./\\.\\./\\.\\./",""; + $p = $p -replace "^\\./\\.\\./",""; + $p = $p -replace "^\\./",""; + if ($p -like 'components/*') { $p = "library/" + $p } + $p +} | Sort-Object -Unique; + +$missing = foreach ($p in $imports) { if ($specText -notmatch [regex]::Escape($p)) { $p } }; +if ($missing) { $missing } else { '(none)' } +``` + +对账 `src/router/index.ts` 引用的 `@/views/*.vue` 是否都存在,并列出未被路由引用的 views: + +```powershell +$repo = (Get-Location).Path; +$routerPath = "$repo\src\router\index.ts"; +$viewsRoot = "$repo\src\views"; + +$router = Get-Content -LiteralPath $routerPath -Raw; +# 只提取“非注释行”中的 views 引用,避免把 // 注释的 demo 路由当成缺失文件 +$routeViewRefs = [regex]::Matches( + $router, + "(?m)^(?!\\s*//).*@/views/(?

[^'\\\"\\)]+\\.vue)", + [System.Text.RegularExpressions.RegexOptions]::Multiline +) | ForEach-Object { $_.Groups['p'].Value } | Sort-Object -Unique; + +$missingFiles = foreach ($p in $routeViewRefs) { + $abs = Join-Path $viewsRoot ($p -replace '/', '\\'); + if (-not (Test-Path -LiteralPath $abs)) { "src/views/$p" } +}; + +$allViews = Get-ChildItem -LiteralPath $viewsRoot -Recurse -File -Filter '*.vue' | ForEach-Object { + $_.FullName.Substring($viewsRoot.Length + 1).Replace('\\','/') +} | Sort-Object -Unique; + +$unreferenced = Compare-Object -ReferenceObject $allViews -DifferenceObject $routeViewRefs -PassThru | Where-Object { $_ -in $allViews } | Sort-Object; + +'--- router references missing files ---'; +if ($missingFiles) { $missingFiles } else { '(none)' }; +'--- views not referenced by router ---'; +if ($unreferenced) { $unreferenced | ForEach-Object { "src/views/$_" } } else { '(none)' }; +``` diff --git a/AI-Coding/openspec-lite/manifest.yaml b/AI-Coding/openspec-lite/manifest.yaml new file mode 100644 index 0000000..5a35d2e --- /dev/null +++ b/AI-Coding/openspec-lite/manifest.yaml @@ -0,0 +1,414 @@ +# OpenSpec-lite: 模块清单(可逐步补全) +# 目的:让 AI 在“选模块”阶段就能定位入口与主要依赖。 + +modules: + - id: boot + name: 应用入口与初始化 + type: core + entrypoints: + - src/main.ts + - library/index.ts + keywords: + - createApp + - setupVab + - setupI18n + - setupStore + - setupRouter + + - id: setup-vab + name: setupVab(library/index.ts 自动加载/图标/样式/插件) + type: core + entrypoints: + - library/index.ts + - library/plugins/ + - library/styles/ + - src/icon/index.ts + keywords: + - require.context + - VabIcon + - createHead + + - id: router + name: 路由与权限 + type: core + entrypoints: + - src/router/index.ts + - src/router/permissions.ts + keywords: + - constantRoutes + - asyncRoutes + - meta + + - id: store + name: Pinia 状态 + type: core + entrypoints: + - src/store/index.ts + - src/store/modules + keywords: + - createPinia + + - id: store-settings + name: Settings Store(主题/布局等全局配置) + type: store + entrypoints: + - src/store/modules/settings.ts + - library/styles/variables/ + keywords: + - updateTheme + - themeName + - menuWidth + + - id: store-routes + name: Routes Store(路由拦截/菜单路由设置) + type: store + entrypoints: + - src/store/modules/routes.ts + keywords: + - setRoutes + - resetRouter + + - id: store-user + name: User Store(登录/用户信息/登出) + type: store + entrypoints: + - src/store/modules/user.ts + keywords: + - getUserInfo + - resetAll + + - id: store-tabs + name: Tabs Store(visitedRoutes) + type: store + entrypoints: + - src/store/modules/tabs.ts + keywords: + - visitedRoutes + - noClosable + + - id: store-errorlog + name: ErrorLog Store(错误日志收集) + type: store + entrypoints: + - src/store/modules/errorLog.ts + keywords: + - addErrorLog + + - id: i18n + name: 国际化 + type: core + entrypoints: + - src/i18n/index.ts + keywords: + - createI18n + - translate + + - id: api-system + name: API 层(src/api + request 封装) + type: core + entrypoints: + - src/api/ + - src/utils/request.ts + keywords: + - axios + - interceptors + - refreshToken + + - id: config-system + name: 配置系统(src/config 聚合) + type: core + entrypoints: + - src/config/index.js + - src/config + keywords: + - cli.config + - theme.config + - net.config + + - id: access-control + name: 访问控制(ACL + hasPermission + v-permissions) + type: core + entrypoints: + - src/store/modules/acl.ts + - src/utils/permission.ts + - library/plugins/directive.ts + keywords: + - hasPermission + - v-permissions + + - id: mock + name: 本地 Mock + type: tooling + entrypoints: + - mock/index.js + - mock/controller + keywords: + - setupMiddlewares + - mockjs + - responseFake + + - id: icons + name: SVG Icons(src/icon) + type: ui + entrypoints: + - src/icon/index.ts + - src/icon + keywords: + - require.context + - svg + + - id: styles + name: 样式体系(library/styles) + type: ui + entrypoints: + - library/styles/vab.scss + - library/styles/variables + - library/styles/background + keywords: + - scss + - variables + + - id: ui-components + name: Vab 组件库 + type: ui + entrypoints: + - library/components/ + keywords: + - Vab + - ElementPlus + + - id: vab-app + name: VabApp + type: ui + entrypoints: + - library/components/VabApp/ + + - id: vab-app-main + name: VabAppMain + type: ui + entrypoints: + - library/components/VabAppMain/ + + - id: vab-avatar + name: VabAvatar + type: ui + entrypoints: + - library/components/VabAvatar/ + + - id: vab-breadcrumb + name: VabBreadcrumb + type: ui + entrypoints: + - library/components/VabBreadcrumb/ + + - id: vab-card + name: VabCard + type: ui + entrypoints: + - library/components/VabCard/ + + - id: vab-colorful-card + name: VabColorfulCard + type: ui + entrypoints: + - library/components/VabColorfulCard/ + + - id: vab-column-bar + name: VabColumnBar + type: ui + entrypoints: + - library/components/VabColumnBar/ + + - id: vab-fold + name: VabFold + type: ui + entrypoints: + - library/components/VabFold/ + + - id: vab-footer + name: VabFooter + type: ui + entrypoints: + - library/components/VabFooter/ + + - id: vab-full-screen + name: VabFullScreen + type: ui + entrypoints: + - library/components/VabFullScreen/ + + - id: vab-language + name: VabLanguage + type: ui + entrypoints: + - library/components/VabLanguage/ + + - id: vab-link + name: VabLink + type: ui + entrypoints: + - library/components/VabLink/ + + - id: vab-logo + name: VabLogo + type: ui + entrypoints: + - library/components/VabLogo/ + + - id: vab-router-view + name: VabRouterView + type: ui + entrypoints: + - library/components/VabRouterView/ + + - id: vab-query-form + name: VabQueryForm + type: ui + entrypoints: + - library/components/VabQueryForm/ + + - id: vab-error-log + name: VabErrorLog + type: ui + entrypoints: + - library/components/VabErrorLog/ + + - id: vab-menu + name: VabMenu + type: ui + entrypoints: + - library/components/VabMenu/ + + - id: vab-nav + name: VabNav + type: ui + entrypoints: + - library/components/VabNav/ + + - id: vab-tabs + name: VabTabs + type: ui + entrypoints: + - library/components/VabTabs/ + + - id: vab-refresh + name: VabRefresh + type: ui + entrypoints: + - library/components/VabRefresh/ + + - id: vab-search + name: VabSearch + type: ui + entrypoints: + - library/components/VabSearch/ + + - id: vab-lock + name: VabLock + type: ui + entrypoints: + - library/components/VabLock/ + + - id: vab-notice + name: VabNotice + type: ui + entrypoints: + - library/components/VabNotice/ + + - id: vab-header + name: VabHeader + type: ui + entrypoints: + - library/components/VabHeader/ + + - id: vab-sidebar + name: VabSideBar + type: ui + entrypoints: + - library/components/VabSideBar/ + + - id: layouts + name: 布局体系 + type: ui + entrypoints: + - library/layouts/index.vue + - library/layouts/ + keywords: + - Layout + + - id: vab-theme + name: Theme 系统(VabTheme) + type: ui + entrypoints: + - library/components/VabTheme/ + - src/store/modules/settings.ts + - library/plugins/vab.ts + keywords: + - theme + - updateTheme + - $pub + + - id: plugins + name: Plugins(全局注入/指令/错误处理) + type: core + entrypoints: + - library/plugins/ + keywords: + - app.use + - globalProperties + + - id: plugin-vab + name: plugin-vab(gp + mitt 事件总线) + type: plugin + entrypoints: + - library/plugins/vab.ts + keywords: + - $pub + - $sub + - mitt + + - id: plugin-directive + name: plugin-directive(v-permissions) + type: plugin + entrypoints: + - library/plugins/directive.ts + keywords: + - v-permissions + - hasPermission + + - id: plugin-errorlog + name: plugin-errorlog(全局错误捕获) + type: plugin + entrypoints: + - library/plugins/errorLog.ts + keywords: + - errorHandler + - useErrorLogStore + + - id: plugin-support + name: plugin-support(构建信息/依赖检查) + type: plugin + entrypoints: + - library/plugins/support.ts + keywords: + - __APP_INFO__ + + - id: build + name: 构建扩展 + type: tooling + entrypoints: + - vue.config.js + - library/build + keywords: + - chainWebpack + - createVuePlugin + + - id: plop + name: Plop 代码生成器 + type: tooling + entrypoints: + - plopfile.js + - plop-templates/ + keywords: + - plop + - setGenerator diff --git a/AI-Coding/openspec-lite/modules/access-control.yaml b/AI-Coding/openspec-lite/modules/access-control.yaml new file mode 100644 index 0000000..bb8710b --- /dev/null +++ b/AI-Coding/openspec-lite/modules/access-control.yaml @@ -0,0 +1,27 @@ +# Module Spec:access-control(ACL + hasPermission + v-permissions) + +module: + id: access-control + name: 访问控制(角色/权限/指令) + type: core + entrypoints: + - src/store/modules/acl.ts + - src/utils/permission.ts + - library/plugins/directive.ts + +public_api: + concept: + - "useAclStore 保存 admin/role/permission" + - "hasPermission(target) 统一判断路由/按钮权限" + - "v-permissions 指令在模板侧消费 hasPermission" + +dependency_closure: + runtime: + - "Pinia store 初始化(src/store/index.ts)" + +acceptance: + - "acl 中 admin=true 时 hasPermission 永远为 true" + - "指令 v-permissions 可用且不报错(具体隐藏/移除行为以实现为准)" + +pitfalls: + - "权限数据来源通常由 user.getUserInfo() 写入 acl;抽取时需明确数据流" diff --git a/AI-Coding/openspec-lite/modules/api-system.yaml b/AI-Coding/openspec-lite/modules/api-system.yaml new file mode 100644 index 0000000..b0691dc --- /dev/null +++ b/AI-Coding/openspec-lite/modules/api-system.yaml @@ -0,0 +1,34 @@ +# Module Spec:api-system(src/api + axios request 封装) + +module: + id: api-system + name: API 层(src/api 域拆分 + src/utils/request.ts) + type: core + entrypoints: + - src/api/ + - src/utils/request.ts + +public_api: + concept: + - "src/api/.ts:按业务域拆分 API 函数" + - "src/utils/request.ts:axios 实例 + 拦截器 + 统一错误处理 + token 注入" + +dependency_closure: + runtime: + - "axios(instance + interceptors)" + - "qs(x-www-form-urlencoded 序列化)" + - "store/user:token 注入、401/402 时 resetAll / refresh token" + - "plugin-vab:gp.$baseMessage / gp.$baseLoading(loading 与错误提示)" + - "plugin-errorlog:needErrorLog/addErrorLog(请求异常入库)" + - "router:401/403 跳转" + - "config:baseURL/requestTimeout/contentType/successCode/statusName/messageName/debounce 等" + - "api/refreshToken:402 刷新 token 重试队列" + +acceptance: + - "正常接口返回 code=200 时返回 data" + - "401 跳转 /login 且 resetAll 执行" + - "402 触发 refreshToken 并重放队列请求" + +pitfalls: + - "request.ts 强依赖 gp(全局注入)与 user store;抽取到新项目需明确入口安装顺序" + - "successCode/statusName/messageName 等与后端协议强耦合,迁移时先在规格声明差异" diff --git a/AI-Coding/openspec-lite/modules/config-system.yaml b/AI-Coding/openspec-lite/modules/config-system.yaml new file mode 100644 index 0000000..7b10986 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/config-system.yaml @@ -0,0 +1,31 @@ +# Module Spec:config-system(src/config:配置聚合) + +module: + id: config-system + name: 配置系统(src/config 聚合导出) + type: core + entrypoints: + - src/config/index.js + - src/config/cli.config.js + - src/config/setting.config.js + - src/config/theme.config.js + - src/config/net.config.js + +public_api: + concept: + - "src/config/index.js 将 4 个子配置聚合导出(cli/setting/theme/network)" + - "部分配置会被 vue.config.js 以 Node 方式 require 读取,因此子配置文件应保持 Node 兼容(避免 window/document)" + + key_index: + - "authentication/loginInterception/routesWhiteList/supportVisit/rolesControl/isHashRouterMode" + - "tokenName/tokenTableName/storage/recordRoute" + - "title/titleSeparator/titleReverse" + - "defaultOpeneds/uniqueOpened/openFirstMenu" + - "layout/themeName/menuWidth/columnStyle/showProgressBar/showTabs/showTheme/showThemeSetting" + - "baseURL/contentType/requestTimeout/successCode/statusName/messageName" + +acceptance: + - "运行构建/启动时,vue.config.js require config 不报错" + +pitfalls: + - "配置文件在 Node 侧执行:避免使用浏览器对象" diff --git a/AI-Coding/openspec-lite/modules/icons.yaml b/AI-Coding/openspec-lite/modules/icons.yaml new file mode 100644 index 0000000..758512f --- /dev/null +++ b/AI-Coding/openspec-lite/modules/icons.yaml @@ -0,0 +1,20 @@ +# Module Spec:icons(src/icon:SVG 自动加载) + +module: + id: icons + name: SVG Icons(src/icon require.context 自动加载) + type: ui + entrypoints: + - src/icon/index.ts + - src/icon/*.svg + +public_api: + concept: + - "通过 require.context 自动加载 src/icon 下的 svg(构建期打包进 sprite/资源管线,取决于 webpack 配置)" + +dependency_closure: + bundler: + - "webpack require.context" + +acceptance: + - "启动后 svg 资源被打包且可引用(具体引用方式取决于项目现有 svg loader 配置)" diff --git a/AI-Coding/openspec-lite/modules/layouts.yaml b/AI-Coding/openspec-lite/modules/layouts.yaml new file mode 100644 index 0000000..3eaf159 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/layouts.yaml @@ -0,0 +1,40 @@ +# Module Spec:layouts(布局体系) + +module: + id: layouts + name: 布局体系(Layout Shell + 多布局实现) + type: ui + entrypoints: + - library/layouts/index.vue + - library/layouts/VabLayoutVertical/ + - library/layouts/VabLayoutHorizontal/ + - library/layouts/VabLayoutCommon/ + - library/layouts/VabLayoutComprehensive/ + - library/layouts/VabLayoutFloat/ + - library/layouts/VabLayoutColumn/ + +public_api: + concept: + - "通过 theme.layout 选择渲染的布局组件:" + +usage_examples: + - "在 settings store 中设置 theme.layout 为 vertical/horizontal/...,Layout 会动态切换" + +dependency_closure: + runtime: + - "Pinia settings store:src/store/modules/settings(theme/layout/collapse/device)" + - "Element Plus:el-backtop" + - "Theme 组件:library/components/VabTheme(VabThemeDrawer/VabThemeSetting)" + - "事件总线注入:library/plugins/vab.ts($pub/$sub/$unsub;Theme 抽屉依赖该注入)" + styles: + - "library/styles/variables/variables.module.scss(布局 SCSS 变量)" + - "library/styles/variables/vab-*-variables.module.scss(Theme 注入 Element Plus CSS 变量)" + +acceptance: + - "Layout 可渲染并不报错" + - "窗口宽度 < 992 时进入 mobile 模式并能折叠菜单(watch/resize 生效)" + - "Theme 抽屉/设置入口存在时不报错(缺失 $pub/$sub 注入会导致 Theme 不工作)" + +pitfalls: + - "Layout 通过 require.context 自动注册 layouts 子目录下的 .vue;迁移到非 webpack 环境时需要等效能力或手动注册" + - "Layout 与 Theme 都依赖 webpack 能力(require.context / 动态 require scss module);抽取到非 webpack 构建器时需要等效替代方案" diff --git a/AI-Coding/openspec-lite/modules/mock.yaml b/AI-Coding/openspec-lite/modules/mock.yaml new file mode 100644 index 0000000..4e907d9 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/mock.yaml @@ -0,0 +1,21 @@ +# Module Spec(示例):mock + +module: + id: mock + name: 本地 Mock(devServer 中间件) + entrypoints: + - mock/index.js + - mock/controller + +public_api: + concept: + - "route object: { url, type, response }" + +usage_examples: + - "在 mock/controller/.js 增加路由对象,devServer 会自动注册" + +acceptance: + - "启动开发服务器后,请求命中 mock 并返回 mockjs 数据" + +pitfalls: + - "路由匹配会自动拼 baseURL 前缀(来自 src/config)" diff --git a/AI-Coding/openspec-lite/modules/plop.yaml b/AI-Coding/openspec-lite/modules/plop.yaml new file mode 100644 index 0000000..1a4dc11 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/plop.yaml @@ -0,0 +1,19 @@ +# Module Spec:plop(代码生成器) + +module: + id: plop + name: Plop 代码生成器(view/curd/component/mock&api) + type: tooling + entrypoints: + - plopfile.js + - plop-templates/ + +public_api: + concept: + - "通过 plop generators 生成页面、curd、组件以及 mock&api 框架代码" + +acceptance: + - "执行 plop 命令可正常出现 generators 并生成文件" + +pitfalls: + - "生成结果依赖仓库既有目录约定(src/views、src/api、mock/controller 等);新项目需对齐目录或改模板" diff --git a/AI-Coding/openspec-lite/modules/plugin-directive.yaml b/AI-Coding/openspec-lite/modules/plugin-directive.yaml new file mode 100644 index 0000000..0a43569 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/plugin-directive.yaml @@ -0,0 +1,24 @@ +# Module Spec:plugin-directive(自定义指令:v-permissions) + +module: + id: plugin-directive + name: 自定义指令(v-permissions 权限控制) + type: plugin + entrypoints: + - library/plugins/directive.ts + +public_api: + concept: + - "注册 v-permissions 指令,用于按权限隐藏/禁用 UI" + +dependency_closure: + runtime: + - "权限判断:src/utils/permission(hasPermission)" + - "路由/用户权限数据:通常来自 store/user 或 routes 权限模块(视实现而定)" + +acceptance: + - "模板中存在 v-permissions 使用时不报错" + - "无权限时能按设计移除/隐藏元素(以 hasPermission 实现为准)" + +pitfalls: + - "指令依赖 hasPermission 的语义;抽取到目标项目需同步其实现与权限数据来源" diff --git a/AI-Coding/openspec-lite/modules/plugin-errorlog.yaml b/AI-Coding/openspec-lite/modules/plugin-errorlog.yaml new file mode 100644 index 0000000..214dc18 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/plugin-errorlog.yaml @@ -0,0 +1,24 @@ +# Module Spec:plugin-errorlog(全局错误捕获与上报 store) + +module: + id: plugin-errorlog + name: 错误日志插件(app.config.errorHandler + store 收集) + type: plugin + entrypoints: + - library/plugins/errorLog.ts + +public_api: + concept: + - "根据配置决定是否启用全局 errorHandler" + - "将错误写入 useErrorLogStore(用于页面展示/上报)" + +dependency_closure: + runtime: + - "Pinia:src/store/modules/errorLog(useErrorLogStore)" + - "config:src/config/errorLog(或 src/config/index.js 聚合)" + +acceptance: + - "启用后,运行时异常会进入 error log store" + +pitfalls: + - "若 store 未初始化或模块缺失,会导致 errorHandler 内再次报错" diff --git a/AI-Coding/openspec-lite/modules/plugin-support.yaml b/AI-Coding/openspec-lite/modules/plugin-support.yaml new file mode 100644 index 0000000..5a6af89 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/plugin-support.yaml @@ -0,0 +1,23 @@ +# Module Spec:plugin-support(生产环境信息/依赖检查) + +module: + id: plugin-support + name: Support 插件(构建信息输出/依赖检查) + type: plugin + entrypoints: + - library/plugins/support.ts + +public_api: + concept: + - "生产环境输出构建信息(__APP_INFO__)" + - "检查关键依赖是否存在(例如 vab-icons)" + +dependency_closure: + runtime: + - "__APP_INFO__ 全局常量(通常由构建注入)" + +acceptance: + - "生产环境能按预期打印/校验,不影响运行" + +pitfalls: + - "依赖检查失败可能影响全局能力(与 vab 插件的保护逻辑相关)" diff --git a/AI-Coding/openspec-lite/modules/plugin-vab.yaml b/AI-Coding/openspec-lite/modules/plugin-vab.yaml new file mode 100644 index 0000000..e173b3c --- /dev/null +++ b/AI-Coding/openspec-lite/modules/plugin-vab.yaml @@ -0,0 +1,29 @@ +# Module Spec:plugin-vab(全局能力注入 + 事件总线) + +module: + id: plugin-vab + name: Vab 插件(gp 全局方法 + mitt 事件总线) + type: plugin + entrypoints: + - library/plugins/vab.ts + +public_api: + concept: + - "通过 app.provide + app.config.globalProperties 注入 gp($baseLoading/$baseMessage/$baseAlert/$baseConfirm/$baseNotify/$baseTableHeight/$pub/$sub/$unsub)" + - "内部使用 mitt 作为事件总线实现 $pub/$sub/$unsub" + +dependency_closure: + runtime: + - "Element Plus:ElLoading / ElMessage / ElMessageBox / ElNotification" + - "mitt" + - "lodash" + - "src/config(loadingText/messageDuration)" + types: + - "types/library.d.ts(globalPropertiesType)" + +acceptance: + - "安装插件后,可通过 inject('$pub')/this.$pub 发布事件" + - "$baseMessage/$baseLoading 能正常工作" + +pitfalls: + - "生产环境存在授权/依赖检查逻辑,可能将 app.config.globalProperties 置空(不要在外部假设 gp 永远存在)" diff --git a/AI-Coding/openspec-lite/modules/router.yaml b/AI-Coding/openspec-lite/modules/router.yaml new file mode 100644 index 0000000..4e37cdb --- /dev/null +++ b/AI-Coding/openspec-lite/modules/router.yaml @@ -0,0 +1,37 @@ +# Module Spec:router(路由与权限守卫) + +module: + id: router + name: 路由与页面组织 + entrypoints: + - src/router/index.ts + - src/router/permissions.ts + +public_api: + exports: + - constantRoutes + - asyncRoutes + - setupPermissions(router) + +usage_examples: + - "新增页面:在 src/views// 添加 Vue 文件,并在 asyncRoutes 中挂载到 Layout children" + +dependency_closure: + runtime: + - "router:vue-router(beforeEach/afterEach)" + - "store-user:token/getUserInfo/resetAll/setVirtualRoles" + - "store-routes:setRoutes(authentication)" + - "store-settings:theme.showProgressBar" + - "config:authentication/loginInterception/routesWhiteList/supportVisit" + - "utils/pageTitle:getPageTitle(to.meta.title)" + - "utils/routes:toLoginRoute" + - "nprogress:VabProgress(含 nprogress.css)" + +acceptance: + - "路由可跳转" + - "刷新后路由仍可恢复" + +pitfalls: + - "如涉及权限/菜单,需同步 meta(title/icon/...);同时注意 routesWhiteList/loginInterception/authentication 等开关影响守卫逻辑" + - "permissions.ts 直接写 document.title;若新项目改为 useHead 等方式需统一" + diff --git a/AI-Coding/openspec-lite/modules/setup-vab.yaml b/AI-Coding/openspec-lite/modules/setup-vab.yaml new file mode 100644 index 0000000..8dcf55e --- /dev/null +++ b/AI-Coding/openspec-lite/modules/setup-vab.yaml @@ -0,0 +1,30 @@ +# Module Spec:setup-vab(library/index.ts:自动加载、图标、样式、插件) + +module: + id: setup-vab + name: setupVab(app)(自动加载 styles/background/plugins + 图标注册) + type: core + entrypoints: + - library/index.ts + +public_api: + concept: + - "setupVab(app) 是本模板把 'library/' 能力接入应用的总入口" + - "它负责:加载 svg 图标、全局样式、注册图标组件、自动加载背景样式与 plugins" + +dependency_closure: + runtime: + - "src/icon/index.ts(svg require.context)" + - "library/styles/vab.scss(全局样式入口)" + - "@vueuse/head(createHead)" + - "vab-icons(VabIcon 组件 + CSS)" + - "@element-plus/icons-vue(全量注册 ElementPlus 图标组件)" + bundler: + - "webpack require.context:background scss / plugins ts 自动加载" + +acceptance: + - "调用 setupVab(app) 后:VabIcon 可用、ElementPlus 图标组件可用" + - "plugins 自动 app.use 安装,$pub/$sub 等全局注入可用(由 plugin-vab 决定)" + +pitfalls: + - "setupVab 依赖 require.context;迁移到非 webpack 构建器需要替代实现(手动 import 或 glob)" diff --git a/AI-Coding/openspec-lite/modules/store-errorlog.yaml b/AI-Coding/openspec-lite/modules/store-errorlog.yaml new file mode 100644 index 0000000..6902e90 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/store-errorlog.yaml @@ -0,0 +1,23 @@ +# Module Spec:store-errorlog(错误日志收集 store) + +module: + id: store-errorlog + name: ErrorLog Store(错误日志收集) + type: store + entrypoints: + - src/store/modules/errorLog.ts + +public_api: + concept: + - "addErrorLog:追加错误日志" + - "clearErrorLog:清空错误日志" + +dependency_closure: + runtime: + - "通常由 plugin-errorlog(library/plugins/errorLog.ts)与 request 异常写入触发" + +acceptance: + - "触发 addErrorLog 后 errorLogs 可读取" + +pitfalls: + - "若错误处理链路缺失(plugin/request 未接入),store 仍可用但不会自动产生数据" diff --git a/AI-Coding/openspec-lite/modules/store-routes.yaml b/AI-Coding/openspec-lite/modules/store-routes.yaml new file mode 100644 index 0000000..c22fd3e --- /dev/null +++ b/AI-Coding/openspec-lite/modules/store-routes.yaml @@ -0,0 +1,28 @@ +# Module Spec:store-routes(路由模式/菜单路由状态) + +module: + id: store-routes + name: Routes Store(路由拦截/菜单路由设置) + type: store + entrypoints: + - src/store/modules/routes.ts + +public_api: + concept: + - "setRoutes(mode):根据 authentication/rolesControl 生成可访问路由并 resetRouter" + - "支持前端路由(asyncRoutes)与后端路由(getList -> convertRouter)两种模式" + +dependency_closure: + runtime: + - "router:src/router(asyncRoutes/constantRoutes/resetRouter)" + - "utils/routes:convertRouter/filterRoutes" + - "api/router:getList(后端路由模式)" + - "config:authentication/rolesControl" + - "plugin-vab:gp.$baseMessage(后端路由格式异常提示)" + +acceptance: + - "前端路由模式下:setRoutes() 后 routes 可用于菜单渲染" + - "后端路由模式下:getList 返回 list 后可 convertRouter 并 resetRouter" + +pitfalls: + - "后端路由 list 格式必须符合 convertRouter 预期;否则会提示错误" diff --git a/AI-Coding/openspec-lite/modules/store-settings.yaml b/AI-Coding/openspec-lite/modules/store-settings.yaml new file mode 100644 index 0000000..1a7dfa4 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/store-settings.yaml @@ -0,0 +1,36 @@ +# Module Spec:store-settings(全局配置/主题/布局状态) + +module: + id: store-settings + name: Settings Store(主题/布局/语言/折叠等全局配置) + type: store + entrypoints: + - src/store/modules/settings.ts + +public_api: + concept: + - "集中管理全局配置:theme、device、collapse、language、lock、logo、title" + - "持久化:localStorage(theme/collapse/language/...)" + - "主题注入:updateTheme() 动态加载 scss module 并写入 CSS 变量(--el-*)" + +usage_examples: + - "useSettingsStore().updateTheme() 在应用启动或主题变更后调用" + - "useSettingsStore().toggleCollapse() 控制侧边栏折叠" + +dependency_closure: + runtime: + - "src/config(默认 theme/layout/开关项等)" + - "@vueuse/core:useCssVar(如果项目通过 auto-import 或显式引入提供)" + bundler: + - "webpack dynamic require:require(`@vab/styles/variables/vab-${themeName}-variables.module.scss`)" + alias: + - "@ -> src" + - "@vab -> library" + +acceptance: + - "切换 themeName 后 updateTheme() 能更新 Element Plus CSS 变量" + - "刷新后 theme/collapse/language 等能从 localStorage 恢复" + +pitfalls: + - "updateTheme() 使用动态 require scss module;非 webpack 构建需替代实现" + - "useCssVar 的来源依赖工程约定(若未自动注入,需要显式 import)" diff --git a/AI-Coding/openspec-lite/modules/store-tabs.yaml b/AI-Coding/openspec-lite/modules/store-tabs.yaml new file mode 100644 index 0000000..c13e1f9 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/store-tabs.yaml @@ -0,0 +1,24 @@ +# Module Spec:store-tabs(标签页 visitedRoutes) + +module: + id: store-tabs + name: Tabs Store(visitedRoutes 管理) + type: store + entrypoints: + - src/store/modules/tabs.ts + +public_api: + concept: + - "addVisitedRoute/delVisitedRoute/delOthers/delLeft/delRight/delAll" + - "确保至少存在一个 noClosable tab(默认第一个)" + +dependency_closure: + runtime: + - "router types:VabRouteRecord(/#/router)" + +acceptance: + - "路由切换时 addVisitedRoute 可累积标签页" + - "关闭/关闭其它/左右/全部等操作不报错" + +pitfalls: + - "对 meta 合并有特殊逻辑(dynamicNewTab/noClosable),迁移时避免改坏行为" diff --git a/AI-Coding/openspec-lite/modules/store-user.yaml b/AI-Coding/openspec-lite/modules/store-user.yaml new file mode 100644 index 0000000..aeaee12 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/store-user.yaml @@ -0,0 +1,31 @@ +# Module Spec:store-user(登录/用户信息/登出/重置) + +module: + id: store-user + name: User Store(登录/用户信息/登出/重置) + type: store + entrypoints: + - src/store/modules/user.ts + +public_api: + concept: + - "login/socialLogin:登录并 afterLogin(通知、设置 token)" + - "getUserInfo:拉取 username/avatar/roles/permissions 并写入 acl" + - "logout/resetAll:清空 token/acl/routes/tabs 并 resetRouter" + +dependency_closure: + runtime: + - "api/user:login/getUserInfo/logout/socialLogin" + - "utils/token:getToken/setToken/removeToken" + - "config:tokenName" + - "store:acl/routes/tabs/settings 联动" + - "router:resetRouter" + - "plugin-vab:gp.$baseNotify/$baseMessage(提示)" + +acceptance: + - "登录成功后 token 写入并能继续请求" + - "getUserInfo 后 acl 中 roles/permissions 生效" + - "logout 后 resetAll 清理完成且路由重置" + +pitfalls: + - "logout 内含 location.reload;迁移到新项目需确认是否保留该行为" diff --git a/AI-Coding/openspec-lite/modules/styles.yaml b/AI-Coding/openspec-lite/modules/styles.yaml new file mode 100644 index 0000000..9bb01b6 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/styles.yaml @@ -0,0 +1,22 @@ +# Module Spec:styles(library/styles:全局样式/变量/背景) + +module: + id: styles + name: 样式体系(全局样式 + 变量 + 背景) + type: ui + entrypoints: + - library/styles/vab.scss + - library/styles/variables/ + - library/styles/background/ + +public_api: + concept: + - "vab.scss 是全局样式入口(normalize/transition/变量等聚合)" + - "variables 下含布局变量与 Theme 变量(vab-*-variables.module.scss)" + +dependency_closure: + bundler: + - "scss/sass loader(由 Vue CLI 提供)" + +acceptance: + - "引入 vab.scss 后基础样式生效,不影响 Element Plus" diff --git a/AI-Coding/openspec-lite/modules/ui-components.yaml b/AI-Coding/openspec-lite/modules/ui-components.yaml new file mode 100644 index 0000000..885ef1b --- /dev/null +++ b/AI-Coding/openspec-lite/modules/ui-components.yaml @@ -0,0 +1,27 @@ +# Module Spec:ui-components(Vab 组件库总览) + +module: + id: ui-components + name: Vab 组件库(library/components/Vab*) + type: ui + entrypoints: + - library/components/ + +public_api: + concept: + - "每个组件目录对应一个 Vab* 组件族;复用优先按目录整包搬运" + +dependency_closure: + runtime: + - "Element Plus(大量 el-* 依赖)" + - "icons:vab-icons 与 @element-plus/icons-vue(常通过 setupVab 全局注册)" + - "全局样式:library/styles/vab.scss" + alias: + - "@ -> src" + - "@vab -> library" + +acceptance: + - "目标项目能渲染组件(至少 smoke render)" + +pitfalls: + - "部分组件可能依赖路由、store、权限指令等全局插件;抽取前必须列出依赖闭包" diff --git a/AI-Coding/openspec-lite/modules/vab-app-main.yaml b/AI-Coding/openspec-lite/modules/vab-app-main.yaml new file mode 100644 index 0000000..43ff525 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-app-main.yaml @@ -0,0 +1,27 @@ +# Module Spec:vab-app-main(主内容区:联动 routes store) + +module: + id: vab-app-main + name: VabAppMain(主内容区) + type: ui + entrypoints: + - library/components/VabAppMain/index.vue + +public_api: + concept: + - "监听 route 变化,更新 routes store 的 tab/activeMenu" + - "渲染 vab-router-view + vab-footer" + +dependency_closure: + runtime: + - "vue-router:useRoute" + - "store-routes:tab/activeMenu" + - "utils/routes:handleActivePath" + - "组件依赖:VabRouterView、VabFooter(需同时可用/注册)" + +acceptance: + - "路由变化时 activeMenu.data 更新为当前激活路径" + - "主内容区能渲染 router-view 与 footer" + +pitfalls: + - "tab 使用 route.matched[0].name;若路由层级/匹配为空需要在目标项目确认兼容" diff --git a/AI-Coding/openspec-lite/modules/vab-app.yaml b/AI-Coding/openspec-lite/modules/vab-app.yaml new file mode 100644 index 0000000..56ef427 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-app.yaml @@ -0,0 +1,21 @@ +# Module Spec:VabApp + +module: + id: vab-app + name: VabApp(应用壳/全局容器) + type: ui + entrypoints: + - library/components/VabApp/index.vue + - library/components/VabApp/ + +usage_examples: + - "App 根组件中使用 作为应用容器" + +dependency_closure: + runtime: + - "可能依赖 router-view 与 Element Plus ConfigProvider(由 VabApp 内部决定)" + - "可能依赖 pwa / i18n / route meta" + +acceptance: + - "渲染不报错" + - "路由切换正常(若内部包含 router-view)" diff --git a/AI-Coding/openspec-lite/modules/vab-avatar.yaml b/AI-Coding/openspec-lite/modules/vab-avatar.yaml new file mode 100644 index 0000000..15b049c --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-avatar.yaml @@ -0,0 +1,28 @@ +# Module Spec:vab-avatar(用户头像下拉) + +module: + id: vab-avatar + name: VabAvatar(用户头像/用户名下拉) + type: ui + entrypoints: + - library/components/VabAvatar/index.vue + +public_api: + concept: + - "右上角用户头像下拉:个人中心/外链/退出登录,并在退出后跳转到登录页(携带回跳参数)" + +dependency_closure: + runtime: + - "store-user:avatar/username/logout()" + - "vue-router:useRoute/useRouter" + - "utils/routes:toLoginRoute(fullPath)" + - "i18n:translate()" + - "Element Plus:el-dropdown/el-dropdown-menu/el-dropdown-item/el-avatar" + - "VabIcon" + +acceptance: + - "下拉可见时箭头激活态切换" + - "点击退出登录会调用 userStore.logout() 并跳转 toLoginRoute(route.fullPath)" + +pitfalls: + - "依赖 userStore 提供 avatar/username;新项目需保证字段一致" diff --git a/AI-Coding/openspec-lite/modules/vab-breadcrumb.yaml b/AI-Coding/openspec-lite/modules/vab-breadcrumb.yaml new file mode 100644 index 0000000..d775232 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-breadcrumb.yaml @@ -0,0 +1,28 @@ +# Module Spec:vab-breadcrumb(面包屑) + +module: + id: vab-breadcrumb + name: VabBreadcrumb(面包屑) + type: ui + entrypoints: + - library/components/VabBreadcrumb/index.vue + +public_api: + concept: + - "根据 routesStore.getRoutes + 当前 route.path 生成面包屑,并支持 meta.icon/meta.isCustomSvg" + +dependency_closure: + runtime: + - "store-routes:getRoutes" + - "vue-router:useRoute" + - "utils/routes:handleMatched(routes, path)" + - "i18n:translate()" + - "Element Plus:el-breadcrumb/el-breadcrumb-item" + - "VabIcon" + +acceptance: + - "meta.breadcrumbHidden=true 的路由不出现在面包屑" + - "每个 crumb 的跳转目标使用 item.redirect(若存在)" + +pitfalls: + - "依赖后端路由结构与 meta 字段(title/icon/isCustomSvg)" diff --git a/AI-Coding/openspec-lite/modules/vab-card.yaml b/AI-Coding/openspec-lite/modules/vab-card.yaml new file mode 100644 index 0000000..281f5fd --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-card.yaml @@ -0,0 +1,25 @@ +# Module Spec:vab-card(卡片封装 + Skeleton) + +module: + id: vab-card + name: VabCard(卡片封装) + type: ui + entrypoints: + - library/components/VabCard/index.vue + +public_api: + concept: + - "对 el-card 的轻封装:header slot/prop + 可选 skeleton loading(默认 500ms 结束)" + +dependency_closure: + runtime: + - "Element Plus:el-card/el-skeleton" + - "vue-router:onBeforeRouteLeave(清理定时器)" + - "SCSS:$base-transition" + +acceptance: + - "skeleton=true 时先显示 skeleton,再渲染默认 slot" + - "路由离开时清理 timer" + +pitfalls: + - "skeletonRows 注释提示:显示数量可能比传入多 1(Element Plus 行为)" diff --git a/AI-Coding/openspec-lite/modules/vab-colorful-card.yaml b/AI-Coding/openspec-lite/modules/vab-colorful-card.yaml new file mode 100644 index 0000000..2028112 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-colorful-card.yaml @@ -0,0 +1,24 @@ +# Module Spec:vab-colorful-card(渐变卡片) + +module: + id: vab-colorful-card + name: VabColorfulCard(渐变卡片) + type: ui + entrypoints: + - library/components/VabColorfulCard/index.vue + +public_api: + concept: + - "基于 el-card 的渐变背景卡片:支持 header 标题与右上角 icon" + +dependency_closure: + runtime: + - "Element Plus:el-card" + - "VabIcon(可选)" + +acceptance: + - "传入 colorFrom/colorTo 时背景为 linear-gradient" + - "传入 icon 时显示 vab-icon" + +pitfalls: + - "colorFrom/colorTo 需为合法 CSS color 字符串" diff --git a/AI-Coding/openspec-lite/modules/vab-column-bar.yaml b/AI-Coding/openspec-lite/modules/vab-column-bar.yaml new file mode 100644 index 0000000..8acf493 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-column-bar.yaml @@ -0,0 +1,31 @@ +# Module Spec:vab-column-bar(列式布局左侧 Tab + 二级菜单) + +module: + id: vab-column-bar + name: VabColumnBar(Column 布局列栏) + type: ui + entrypoints: + - library/components/VabColumnBar/index.vue + +public_api: + concept: + - "Column 布局专用:左侧 tabs 切换顶级菜单,右侧 el-menu 渲染 partialRoutes(二级菜单)" + +dependency_closure: + runtime: + - "store-settings:theme(layout/columnStyle)/collapse + foldSideBar/openSideBar" + - "store-routes:tab/tabMenu/activeMenu/routes/partialRoutes" + - "config:defaultOpeneds/openFirstMenu/uniqueOpened" + - "vue-router:useRoute/useRouter" + - "utils/validate:isExternal" + - "i18n:translate()" + - "styles:@vab/styles/variables/variables.module.scss(column-second-menu-background 等)" + - "Element Plus:el-scrollbar/el-tabs/el-tab-pane/el-menu/el-divider" + - "VabLogo/VabMenu/VabIcon" + +acceptance: + - "theme.layout==='column' 时可用;route.meta.noColumn=true 时会自动折叠侧边栏并隐藏 fold-unfold" + - "点击 tab:若 tabMenu.path 为外链则 window.open;否则(openFirstMenu=true)跳转到 redirect 或自身" + +pitfalls: + - "直接操作 DOM:document.querySelector('.fold-unfold') 修改 style;新项目结构不同需适配" diff --git a/AI-Coding/openspec-lite/modules/vab-error-log.yaml b/AI-Coding/openspec-lite/modules/vab-error-log.yaml new file mode 100644 index 0000000..b445a5a --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-error-log.yaml @@ -0,0 +1,28 @@ +# Module Spec:vab-error-log(错误日志展示入口) + +module: + id: vab-error-log + name: VabErrorLog(错误日志展示) + type: ui + entrypoints: + - library/components/VabErrorLog/index.vue + +public_api: + concept: + - "展示 errorLogs 数量徽标,点击打开弹窗列表" + - "提供清空日志入口(clearErrorLog)" + +dependency_closure: + runtime: + - "store-errorlog:useErrorLogStore(errorLogs/clearErrorLog)" + - "Element Plus:el-badge/el-dialog/el-table/el-tag/el-popover/el-button" + - "VabIcon:全局组件(由 setup-vab 注册)" + related_modules: + - "plugin-errorlog:负责把运行时错误写入 store(否则列表为空)" + +acceptance: + - "errorLogs.length > 0 时显示徽标并可打开弹窗" + - "点击 '暂不显示' 能清空 store" + +pitfalls: + - "仅负责展示;日志产生依赖 plugin/request 链路" diff --git a/AI-Coding/openspec-lite/modules/vab-fold.yaml b/AI-Coding/openspec-lite/modules/vab-fold.yaml new file mode 100644 index 0000000..b4a299e --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-fold.yaml @@ -0,0 +1,20 @@ +# Module Spec:vab-fold(折叠按钮) + +module: + id: vab-fold + name: VabFold(侧边栏折叠/展开) + type: ui + entrypoints: + - library/components/VabFold/index.vue + +public_api: + concept: + - "根据 settings.collapse 展示不同图标,并触发 settings.toggleCollapse()" + +dependency_closure: + runtime: + - "store-settings:collapse/toggleCollapse" + - "VabIcon" + +acceptance: + - "点击后触发 toggleCollapse 并切换图标" diff --git a/AI-Coding/openspec-lite/modules/vab-footer.yaml b/AI-Coding/openspec-lite/modules/vab-footer.yaml new file mode 100644 index 0000000..928e2bc --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-footer.yaml @@ -0,0 +1,25 @@ +# Module Spec:vab-footer(页脚) + +module: + id: vab-footer + name: VabFooter(页脚) + type: ui + entrypoints: + - library/components/VabFooter/index.vue + +public_api: + concept: + - "展示年份与站点 title(来自 settings store)" + +dependency_closure: + runtime: + - "store-settings:title" + - "VabIcon" + styles: + - "SCSS 变量:$base-padding / $base-border-color(来自 styles 模块)" + +acceptance: + - "渲染后能显示年份与 title" + +pitfalls: + - "样式依赖全局 SCSS 变量;目标项目缺失变量会导致样式编译失败" diff --git a/AI-Coding/openspec-lite/modules/vab-full-screen.yaml b/AI-Coding/openspec-lite/modules/vab-full-screen.yaml new file mode 100644 index 0000000..f38f33e --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-full-screen.yaml @@ -0,0 +1,24 @@ +# Module Spec:vab-full-screen(全屏切换) + +module: + id: vab-full-screen + name: VabFullScreen(全屏切换) + type: ui + entrypoints: + - library/components/VabFullScreen/index.vue + +public_api: + concept: + - "点击图标切换全屏状态(useFullscreen().toggle)" + +dependency_closure: + runtime: + - "@vueuse/core:useFullscreen(若项目通过 auto-import 或显式引入提供)" + - "store-settings:theme.showFullScreen" + - "VabIcon" + +acceptance: + - "theme.showFullScreen=true 时显示图标,点击可进入/退出全屏" + +pitfalls: + - "useFullscreen 的来源依赖工程约定(自动导入 vs 显式 import)" diff --git a/AI-Coding/openspec-lite/modules/vab-header.yaml b/AI-Coding/openspec-lite/modules/vab-header.yaml new file mode 100644 index 0000000..d4ac3df --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-header.yaml @@ -0,0 +1,21 @@ +# Module Spec:VabHeader + +module: + id: vab-header + name: VabHeader(头部导航) + type: ui + entrypoints: + - library/components/VabHeader/index.vue + - library/components/VabHeader/ + +usage_examples: + - "布局组件中使用 VabHeader,承载用户信息/语言切换/全屏/刷新等入口" + +dependency_closure: + runtime: + - "可能依赖 settings store(fixedHeader/showTabs 等)" + - "可能依赖 i18n、router" + +acceptance: + - "渲染不报错" + - "常用操作(如全屏/刷新/语言切换)不崩溃(若组件支持)" diff --git a/AI-Coding/openspec-lite/modules/vab-language.yaml b/AI-Coding/openspec-lite/modules/vab-language.yaml new file mode 100644 index 0000000..7ad0d38 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-language.yaml @@ -0,0 +1,28 @@ +# Module Spec:vab-language(语言切换) + +module: + id: vab-language + name: VabLanguage(语言切换) + type: ui + entrypoints: + - library/components/VabLanguage/index.vue + +public_api: + concept: + - "通过下拉菜单切换语言:更新 settings.language + i18n locale + document.title" + +dependency_closure: + runtime: + - "store-settings:theme.showLanguage / changeLanguage(language)" + - "vue-i18n:useI18n().locale" + - "vue-router:useRoute(读取 route.meta.title)" + - "utils/pageTitle:getPageTitle" + - "Element Plus:el-dropdown/el-dropdown-menu/el-dropdown-item" + - "VabIcon" + +acceptance: + - "theme.showLanguage=true 时显示入口" + - "切换后 settings.language 与 i18n locale 同步更新" + +pitfalls: + - "document.title 依赖 route.meta.title;目标项目若 meta.title 缺失需适配" diff --git a/AI-Coding/openspec-lite/modules/vab-link.yaml b/AI-Coding/openspec-lite/modules/vab-link.yaml new file mode 100644 index 0000000..1a8f342 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-link.yaml @@ -0,0 +1,21 @@ +# Module Spec:vab-link(智能链接) + +module: + id: vab-link + name: VabLink(外链/内链统一) + type: ui + entrypoints: + - library/components/VabLink/index.vue + +public_api: + concept: + - "根据 isExternal(to) 自动选择渲染 a 或 router-link" + +dependency_closure: + runtime: + - "utils/validate:isExternal" + - "vue-router:router-link" + +acceptance: + - "外链:target=_blank + rel=noopener" + - "内链:透传 to 给 router-link" diff --git a/AI-Coding/openspec-lite/modules/vab-lock.yaml b/AI-Coding/openspec-lite/modules/vab-lock.yaml new file mode 100644 index 0000000..4090db2 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-lock.yaml @@ -0,0 +1,29 @@ +# Module Spec:vab-lock(锁屏/解锁) + +module: + id: vab-lock + name: VabLock(屏幕锁) + type: ui + entrypoints: + - library/components/VabLock/index.vue + +public_api: + concept: + - "点击锁屏图标将 settings.lock 置为 true,并隐藏侧边栏 DOM" + - "解锁通过表单校验后将 settings.lock 置为 false,并恢复侧边栏" + +dependency_closure: + runtime: + - "store-settings:theme.showLock / lock / title / handleLock / handleUnLock" + - "store-user:avatar" + - "i18n:translate" + - "Element Plus:el-avatar/el-form/el-form-item/el-input/el-button" + - "浏览器 DOM:document.querySelector('.vab-side-bar')(直接改 style)" + +acceptance: + - "theme.showLock=true 时显示锁图标;点击后 lock=true 并出现锁屏层" + - "解锁成功后 lock=false,页面恢复" + +pitfalls: + - "组件内密码校验为固定值(示例逻辑);迁移到新项目需确认是否替换为真实策略" + - "直接操作 '.vab-side-bar' DOM;若目标项目侧边栏类名不同需要适配" diff --git a/AI-Coding/openspec-lite/modules/vab-logo.yaml b/AI-Coding/openspec-lite/modules/vab-logo.yaml new file mode 100644 index 0000000..1bce6ff --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-logo.yaml @@ -0,0 +1,26 @@ +# Module Spec:vab-logo(Logo + Title) + +module: + id: vab-logo + name: VabLogo(Logo/标题) + type: ui + entrypoints: + - library/components/VabLogo/index.vue + +public_api: + concept: + - "读取 settings.logo/settings.title,并根据 theme.layout 渲染不同样式的 logo 区域" + +dependency_closure: + runtime: + - "store-settings:theme/layout + logo + title" + - "vue-router:router-link" + - "VabIcon(自定义 svg:is-custom-svg)" + - "SCSS:$base-header-height/$base-logo-height/$base-title-color 等" + +acceptance: + - "logo 存在时使用 vab-icon 渲染自定义 svg" + - "theme.layout==='horizontal' 时标题可隐藏(hidden-xs-only)" + +pitfalls: + - "Column 布局会固定定位 logo,依赖左侧菜单宽度相关变量" diff --git a/AI-Coding/openspec-lite/modules/vab-menu.yaml b/AI-Coding/openspec-lite/modules/vab-menu.yaml new file mode 100644 index 0000000..e31f0e5 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-menu.yaml @@ -0,0 +1,39 @@ +# Module Spec:VabMenu + +module: + id: vab-menu + name: VabMenu(菜单体系) + type: ui + entrypoints: + - library/components/VabMenu/index.vue + - library/components/VabMenu/components/VabMenuItem.vue + - library/components/VabMenu/components/VabSubMenu.vue + +public_api: + concept: + - "递归渲染菜单:VabMenu 根据子路由可见性选择渲染 VabSubMenu 或 VabMenuItem" + - "点击菜单:根据 meta.target/_blank、外链/内链、同路由刷新等逻辑进行跳转或触发 reload" + +usage_examples: + - "布局组件中引入 VabMenu,配合路由 meta 与权限渲染导航" + +dependency_closure: + runtime: + - "vue-router:useRoute/useRouter(跳转/判断当前路由)" + - "store-settings:collapse/device/theme(layout) + foldSideBar(移动端点击收起)" + - "plugin-vab:inject('$pub')(同路由点击触发 $pub('reload-router-view'))" + - "config:isHashRouterMode(hash 模式下 _blank 打开内部路由)" + - "utils/validate:isExternal(外链判断)" + - "i18n:translate(菜单 title)" + - "Element Plus:el-menu-item/el-sub-menu/el-tag" + - "VabIcon" + +acceptance: + - "item.children 中存在可见子路由时渲染 VabSubMenu,否则渲染 VabMenuItem" + - "meta.target === '_blank' 时按外链/内链规则在新窗口打开" + - "点击当前已激活路由时触发 $pub('reload-router-view')" + +pitfalls: + - "组件内部使用 webpack require.context 自动注册子组件;迁移到非 webpack 构建器需等效替代" + - "Element Plus 关于 teleported 的历史兼容问题:弹层渲染到 body 下时需要全局样式配合(见 VabMenu/index.vue 注释)" + - "如果菜单权限依赖 ACL/指令(v-permissions/hasPermission),迁移时必须把相关依赖一起带走" diff --git a/AI-Coding/openspec-lite/modules/vab-nav.yaml b/AI-Coding/openspec-lite/modules/vab-nav.yaml new file mode 100644 index 0000000..5bc58d3 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-nav.yaml @@ -0,0 +1,29 @@ +# Module Spec:vab-nav(顶部导航条聚合) + +module: + id: vab-nav + name: VabNav(顶部导航条) + type: ui + entrypoints: + - library/components/VabNav/index.vue + +public_api: + concept: + - "顶部导航聚合:左侧(Fold + Tabs/Breadcrumb),右侧(ErrorLog/Lock/Search/Notice/FullScreen/Language/Theme/Refresh/Avatar)" + +dependency_closure: + runtime: + - "store-routes:tab/tabMenu/routes" + - "vue-router:useRouter" + - "utils/validate:isExternal" + - "config:openFirstMenu" + - "i18n:translate()" + - "Element Plus:el-row/el-col/el-tabs/el-tab-pane" + - "Components:VabFold/VabBreadcrumb/VabErrorLog/VabLock/VabSearch/VabNotice/VabFullScreen/VabLanguage/VabTheme/VabRefresh/VabAvatar" + +acceptance: + - "layout='comprehensive' 时显示顶部 tabs,否则显示面包屑(hidden-xs-only)" + - "点击 tab:外链 window.open;否则(openFirstMenu=true)跳 redirect 或自身" + +pitfalls: + - "强依赖多组件与 routesStore 输出结构;抽取时必须把依赖组件闭包一起带走" diff --git a/AI-Coding/openspec-lite/modules/vab-notice.yaml b/AI-Coding/openspec-lite/modules/vab-notice.yaml new file mode 100644 index 0000000..e97a4fe --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-notice.yaml @@ -0,0 +1,31 @@ +# Module Spec:vab-notice(消息中心) + +module: + id: vab-notice + name: VabNotice(消息中心/通知) + type: ui + entrypoints: + - library/components/VabNotice/index.vue + +public_api: + concept: + - "显示通知 badge,弹出 popover + tabs 展示通知/邮件" + - "从 api/notice.getList 拉取数据并计算 badge" + - "提供清空消息入口(仅清空前端列表与 badge,并提示)" + +dependency_closure: + runtime: + - "store-settings:theme.showNotice" + - "api/notice:getList" + - "plugin-vab:$baseMessage(清空提示,通过 inject 获取)" + - "i18n:translate" + - "Element Plus:el-badge/el-popover/el-tabs/el-tab-pane/el-scrollbar/el-avatar/el-button" + - "VabIcon" + +acceptance: + - "theme.showNotice=true 时显示通知入口与 badge" + - "点击/切换 tab 会触发 fetchData 更新列表" + +pitfalls: + - "数据协议依赖 notice.getList 的返回结构(list/total)" + - "清空仅影响前端状态,不等价于后端已读/删除" diff --git a/AI-Coding/openspec-lite/modules/vab-query-form.yaml b/AI-Coding/openspec-lite/modules/vab-query-form.yaml new file mode 100644 index 0000000..df350e2 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-query-form.yaml @@ -0,0 +1,29 @@ +# Module Spec:vab-query-form(查询表单布局容器) + +module: + id: vab-query-form + name: VabQueryForm(查询条件容器) + type: ui + entrypoints: + - library/components/VabQueryForm/index.vue + - library/components/VabQueryForm/components/VabQueryFormTopPanel.vue + - library/components/VabQueryForm/components/VabQueryFormBottomPanel.vue + - library/components/VabQueryForm/components/VabQueryFormLeftPanel.vue + - library/components/VabQueryForm/components/VabQueryFormRightPanel.vue + +public_api: + concept: + - "基于 Element Plus el-row 的 slot 容器,用于统一查询表单布局与间距" + +dependency_closure: + runtime: + - "Element Plus:el-row" + styles: + - "SCSS 变量:$base-input-height / $base-margin(来自全局样式变量体系,见 styles 模块)" + +acceptance: + - "作为容器包裹 el-form-item/el-button 时布局与间距符合预期" + +pitfalls: + - "样式依赖全局 SCSS 变量;目标项目若未引入对应变量会导致样式编译失败" + diff --git a/AI-Coding/openspec-lite/modules/vab-refresh.yaml b/AI-Coding/openspec-lite/modules/vab-refresh.yaml new file mode 100644 index 0000000..1e82dc6 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-refresh.yaml @@ -0,0 +1,25 @@ +# Module Spec:vab-refresh(刷新当前路由视图) + +module: + id: vab-refresh + name: VabRefresh(刷新按钮) + type: ui + entrypoints: + - library/components/VabRefresh/index.vue + +public_api: + concept: + - "点击后通过事件总线发布 reload-router-view,刷新当前 router-view 缓存" + +dependency_closure: + runtime: + - "store-settings:theme.showRefresh(是否显示按钮)" + - "plugin-vab:$pub(发布事件)" + related_modules: + - "vab-router-view:订阅并处理 reload-router-view" + +acceptance: + - "theme.showRefresh=true 时显示图标,点击触发 $pub('reload-router-view')" + +pitfalls: + - "未安装 plugin-vab 或未接入 vab-router-view 时,点击不会产生效果" diff --git a/AI-Coding/openspec-lite/modules/vab-router-view.yaml b/AI-Coding/openspec-lite/modules/vab-router-view.yaml new file mode 100644 index 0000000..0a2c57b --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-router-view.yaml @@ -0,0 +1,31 @@ +# Module Spec:vab-router-view(路由视图壳:keep-alive + 过渡 + reload 事件) + +module: + id: vab-router-view + name: VabRouterView(router-view + keep-alive + reload) + type: ui + entrypoints: + - library/components/VabRouterView/index.vue + +public_api: + concept: + - "统一承载页面 router-view,提供 keep-alive include 列表与过渡动画" + - "监听事件总线:reload-router-view,用于刷新当前视图缓存" + +dependency_closure: + runtime: + - "vue-router:useRoute / " + - "plugin-vab:$sub/$unsub(事件订阅)" + - "store-settings:theme.showProgressBar / theme.showPageTransition" + - "store-tabs:visitedRoutes(生成 keepAliveNameList)" + - "utils/routes:handleActivePath(生成 routerKey)" + - "config:keepAliveMaxNum" + - "nprogress(显示刷新进度条)" + +acceptance: + - "路由切换时 keep-alive include 列表随 visitedRoutes 更新" + - "$sub('reload-router-view') 触发后,当前视图能被重新渲染(routerKey 变更)" + +pitfalls: + - "存在 get-code 事件并依赖组件的 __source 字段;不同构建/插件下可能不存在" + - "未安装 plugin-vab 时 $sub/$unsub 不存在,reload/get-code 事件不会工作" diff --git a/AI-Coding/openspec-lite/modules/vab-search.yaml b/AI-Coding/openspec-lite/modules/vab-search.yaml new file mode 100644 index 0000000..a0e0e86 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-search.yaml @@ -0,0 +1,30 @@ +# Module Spec:vab-search(菜单搜索:Ctrl/⌘+K) + +module: + id: vab-search + name: VabSearch(菜单搜索) + type: ui + entrypoints: + - library/components/VabSearch/index.vue + +public_api: + concept: + - "通过 Ctrl/⌘+K 或点击图标打开搜索面板(teleport 到 body)" + - "从 routes store 的 getRoutes 平铺菜单项并模糊搜索" + - "维护本地搜索历史 localStorage(key=vab_search_history)" + +dependency_closure: + runtime: + - "store-routes:getRoutes(菜单路由来源)" + - "vue-router:useRouter(router.push 内链跳转)" + - "浏览器 API:window.addEventListener('keydown'), navigator.userAgent, localStorage" + - "VabIcon:搜索图标与历史删除图标" + +acceptance: + - "Ctrl/⌘+K 可打开/关闭面板(Escape 关闭)" + - "输入关键字能过滤菜单并 Enter 跳转" + - "搜索历史能写入/删除/清空" + +pitfalls: + - "该组件包含较多样式与 DOM 交互(teleport/fixed mask);抽取到新项目需确保全局样式/层级不冲突" + - "菜单来源依赖 routes store 已完成 setRoutes;否则列表为空" diff --git a/AI-Coding/openspec-lite/modules/vab-sidebar.yaml b/AI-Coding/openspec-lite/modules/vab-sidebar.yaml new file mode 100644 index 0000000..820f435 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-sidebar.yaml @@ -0,0 +1,20 @@ +# Module Spec:VabSideBar + +module: + id: vab-sidebar + name: VabSideBar(侧边栏容器) + type: ui + entrypoints: + - library/components/VabSideBar/index.vue + - library/components/VabSideBar/ + +usage_examples: + - "在布局中使用 VabSideBar 包裹 VabMenu,实现侧边导航" + +dependency_closure: + runtime: + - "settings store:collapse/device 等" + - "Element Plus(如内部使用 el-scrollbar 等)" + +acceptance: + - "折叠状态切换后布局不崩溃" diff --git a/AI-Coding/openspec-lite/modules/vab-tabs.yaml b/AI-Coding/openspec-lite/modules/vab-tabs.yaml new file mode 100644 index 0000000..09850f1 --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-tabs.yaml @@ -0,0 +1,21 @@ +# Module Spec:VabTabs + +module: + id: vab-tabs + name: VabTabs(多标签页/导航标签) + type: ui + entrypoints: + - library/components/VabTabs/index.vue + - library/components/VabTabs/ + +usage_examples: + - "布局头部或主区域放置 VabTabs,展示已访问路由并支持关闭/切换" + +dependency_closure: + runtime: + - "vue-router:监听路由变化" + - "store:tabs 状态(如果实现依赖 store 模块)" + +acceptance: + - "访问多个路由后能出现多个 tab(如功能支持)" + - "切换/关闭 tab 不崩溃" diff --git a/AI-Coding/openspec-lite/modules/vab-theme.yaml b/AI-Coding/openspec-lite/modules/vab-theme.yaml new file mode 100644 index 0000000..d6ee8fc --- /dev/null +++ b/AI-Coding/openspec-lite/modules/vab-theme.yaml @@ -0,0 +1,40 @@ +# Module Spec:vab-theme(主题系统入口与交互) + +module: + id: vab-theme + name: VabTheme(主题配置入口/抽屉/设置面板) + type: ui + entrypoints: + - library/components/VabTheme/index.vue + - library/components/VabTheme/components/VabThemeDrawer.vue + - library/components/VabTheme/components/VabThemeSetting.vue + - library/components/VabTheme/components/ + +public_api: + concept: + - "VabTheme:一个刷子图标入口,用于触发打开主题抽屉(Drawer)" + - "VabThemeDrawer:主题抽屉,监听事件总线打开/随机主题,并调用 settings store 的 saveTheme/resetTheme/updateTheme" + - "VabThemeSetting:右侧固定设置入口(主题配置/随机换肤/购买源码/清理缓存),通过事件总线触发行为" + +usage_examples: + - "点击 VabTheme 触发 $pub('theme') 打开抽屉" + - "点击 VabThemeSetting 的随机换肤触发 $pub('random-theme')" + +dependency_closure: + runtime: + - "Pinia settings store:src/store/modules/settings(theme.showTheme、saveTheme/resetTheme/updateTheme 等)" + - "事件总线/全局注入:library/plugins/vab.ts($pub/$sub/$unsub/$baseLoading 等)" + - "i18n translate:src/i18n/index.ts(组件内调用 translate)" + - "Element Plus:Drawer/Radio/Select/Button 等" + styles: + - "主题变量:library/styles/variables/vab-*-variables.module.scss(updateTheme() 动态注入 CSS 变量)" + +acceptance: + - "页面渲染包含主题入口(刷子图标)" + - "触发 $pub('theme') 时抽屉能打开;关闭时不报错" + - "保存/重置主题后调用 updateTheme() 生效(CSS 变量与 body class 变化)" + +pitfalls: + - "Theme 依赖 $pub/$sub 注入;若未安装 vab 插件,事件不会触发" + - "settings.updateTheme() 依赖 webpack 的 require 动态引入 scss module;迁移到非 webpack 环境需等效实现" + - "移动端(<992)某些操作会触发 reload;抽取时需确认期望行为" diff --git a/AI-Coding/openspec-lite/project.yaml b/AI-Coding/openspec-lite/project.yaml new file mode 100644 index 0000000..4b11cbd --- /dev/null +++ b/AI-Coding/openspec-lite/project.yaml @@ -0,0 +1,64 @@ +# OpenSpec-lite: 项目级规格(轻量版) +# 目的:用“可机读约束”引导 AI 在不改动现有代码的情况下复用资源。 + +project: + name: admin-plus-template + intent: "公司内部模板库/基础库(轻量化,优先文档约束)" + stack: + framework: "Vue 3" + build: "Vue CLI 5" + language: "TypeScript" + state: "Pinia" + router: "Vue Router" + ui: "Element Plus" + +constraints: + no_code_changes_by_default: true + prefer_docs_over_refactor: true + extraction_style: + - "copy-module" # 优先整目录搬运再裁剪 + - "copy-snippet" # 仅在模块过大或依赖不清晰时使用 + + required_output_sections: + - "changed_files" # 变更文件清单 + - "extracted_modules" # 抽取模块清单(来源->目标) + - "dependency_closure" # 依赖闭包(imports/运行时/类型/alias) + - "acceptance_commands" # 验收命令 + - "risks" # 风险说明 + + forbidden_by_default: + - "changing runtime behavior in production" # 禁止默认更改生产行为 + - "introducing new global side effects" # 禁止新增全局副作用 + - "deep-importing internal paths of modules" # 禁止消费者深层路径引用 + +acceptance: + minimum_commands: + - "pnpm run serve" + recommended_commands: + - "pnpm run lint" + - "pnpm run test:unit" + - "pnpm run build" + +repository: + entrypoints: + - "src/main.ts" + - "library/index.ts" + - "src/router/index.ts" + - "src/store/index.ts" + - "src/i18n/index.ts" + - "mock/index.js" + +module_roots: + ui_components_dir: "library/components" + layouts_dir: "library/layouts" + plugins_dir: "library/plugins" + styles_dir: "library/styles" + store_modules_dir: "src/store/modules" + api_dir: "src/api" + utils_dir: "src/utils" + config_dir: "src/config" + icons_dir: "src/icon" + +notes: + - "配置读取来自 src/config/index.js(被 vue.config.js require)" + - "library/index.ts 存在插件自动全量加载;抽取时需显式列出依赖闭包" diff --git a/AI-Coding/openspec-lite/tasks/add-api-and-mock.yaml b/AI-Coding/openspec-lite/tasks/add-api-and-mock.yaml new file mode 100644 index 0000000..27e037b --- /dev/null +++ b/AI-Coding/openspec-lite/tasks/add-api-and-mock.yaml @@ -0,0 +1,25 @@ +# Task Template: 新增 API + Mock(轻量版) + +task: + id: add-api-and-mock + intent: "新增一个 API 调用函数,并同步补一个 Mock 路由(用于开发联调)" + +inputs: + required: + - domain + - function_name + - method # get/post/... + - path # 例如 /router/getList + +outputs: + files_to_change: + - "src/api/.ts" + - "mock/controller/.js" + +rules: + - "API path 与 Mock path 必须一致" + - "Mock 路由对象必须包含 url/type/response" + +acceptance: + - "开发环境请求能命中 mock" + - "API 函数能被页面调用且类型不报错(若涉及类型需补 types/)" diff --git a/AI-Coding/openspec-lite/tasks/add-page.yaml b/AI-Coding/openspec-lite/tasks/add-page.yaml new file mode 100644 index 0000000..b588bc0 --- /dev/null +++ b/AI-Coding/openspec-lite/tasks/add-page.yaml @@ -0,0 +1,28 @@ +# Task Template: 新增页面 + 路由(轻量版) +# 目标:约束 AI 在本仓库(或迁移后的新仓库)新增页面时的输出与验收。 + +task: + id: add-page + intent: "新增一个页面,并把路由挂载到 Layout(如需要,同步 meta 供菜单/面包屑使用)" + +inputs: + required: + - module # 例如 goods / userManagement + - page_name # 例如 list / detail + - route_path # 例如 /goods/list + - route_name # 例如 GoodsList + +outputs: + files_to_change: + - "src/views//.vue" + - "src/router/index.ts" + +rules: + - "页面文件必须位于 src/views// 下" + - "路由必须挂载到 asyncRoutes 的 Layout children(除非明确说明是 constantRoutes)" + - "如需要菜单展示,必须填写 meta.title;如需要图标,必须填写 meta.icon" + +acceptance: + - "访问 route_path 能渲染页面" + - "刷新后仍可访问(路由可恢复)" + - "若配置 meta,菜单/面包屑能展示正确标题" diff --git a/AI-Coding/openspec-lite/tasks/extract-component.yaml b/AI-Coding/openspec-lite/tasks/extract-component.yaml new file mode 100644 index 0000000..1107da0 --- /dev/null +++ b/AI-Coding/openspec-lite/tasks/extract-component.yaml @@ -0,0 +1,37 @@ +# Task Template: 抽取组件(library/components/Vab*) + +task: + id: extract-component + intent: "从本仓库抽取一个或多个 Vab 组件到目标项目,确保依赖闭包完整" + +inputs: + required: + - components # 例如 ["VabTabs", "VabMenu"] + - target_project_type + - extraction_style + +scope: + base_dir: "library/components" + +dependency_closure_checklist: + runtime: + - "Element Plus 组件是否使用(el-*)" + - "icons:vab-icons 与 @element-plus/icons-vue 是否需要" + - "全局样式:library/styles/vab.scss 是否是前置依赖" + app_wiring: + - "是否依赖 setupVab 的全局注册(VabIcon / 图标注册 / 插件自动加载)" + alias: + - "@ -> src" + - "@vab -> library" + +outputs: + required_sections: + - changed_files + - extracted_modules + - dependency_closure + - acceptance_commands + - risks + +acceptance: + - "目标项目可渲染组件" + - "关键交互可用(按组件规格中的验收点)" diff --git a/AI-Coding/openspec-lite/tasks/extract-layout.yaml b/AI-Coding/openspec-lite/tasks/extract-layout.yaml new file mode 100644 index 0000000..4714457 --- /dev/null +++ b/AI-Coding/openspec-lite/tasks/extract-layout.yaml @@ -0,0 +1,44 @@ +# Task Template: 抽取布局体系(library/layouts) + +task: + id: extract-layout + intent: "从本仓库抽取布局体系到目标项目,最小闭包迁移,尽量不改动原仓库代码" + +inputs: + required: + - target_project_type # vue-cli | vite | other + - extraction_style # copy-module | copy-snippet + +scope: + entrypoints: + - library/layouts/index.vue + - library/layouts/VabLayoutVertical/ + - library/layouts/VabLayoutHorizontal/ + - library/layouts/VabLayoutCommon/ + - library/layouts/VabLayoutComprehensive/ + - library/layouts/VabLayoutFloat/ + - library/layouts/VabLayoutColumn/ + +dependency_closure_checklist: + runtime: + - "Element Plus(el-backtop 等)" + - "Theme 组件(library/components/VabTheme)是否随闭包迁移" + - "事件总线注入(library/plugins/vab.ts:$pub/$sub/$unsub)是否可用" + - "Pinia settings store:src/store/modules/settings(theme/layout/collapse/device)" + - "全局样式变量:library/styles/variables/(布局 SCSS 使用的变量)" + - "Theme 变量:library/styles/variables/vab-*-variables.module.scss(settings.updateTheme() 依赖)" + alias: + - "@ -> src" + - "@vab -> library" + +outputs: + required_sections: + - changed_files + - extracted_modules + - dependency_closure + - acceptance_commands + - risks + +acceptance: + - "Layout 能渲染" + - "切换移动端/桌面端时不崩溃(resize/watch 逻辑生效)" diff --git a/AI-Coding/openspec-lite/tasks/extract-module.yaml b/AI-Coding/openspec-lite/tasks/extract-module.yaml new file mode 100644 index 0000000..38936b7 --- /dev/null +++ b/AI-Coding/openspec-lite/tasks/extract-module.yaml @@ -0,0 +1,30 @@ +# Task Template: 抽取/复用模块(轻量版) + +task: + id: extract-module + intent: "从本仓库抽取模块到目标项目,尽量不改动原仓库代码" + +inputs: + required: + - module_id + - target_project_type # vue-cli | vite | other + - extraction_style # copy-module | copy-snippet + +outputs: + required_sections: + - changed_files + - extracted_modules + - dependency_closure + - acceptance_commands + - risks + +procedure: + - step: "从 openspec-lite/manifest.yaml 选择 module_id 并列出 entrypoints" + - step: "读取入口文件,分析 import 依赖与运行时依赖(样式、插件、types、alias)" + - step: "按 extraction_style 抽取(优先 copy-module)" + - step: "在目标项目补齐依赖(package.json、alias、types include)" + - step: "给出最小验收命令并说明风险" + +acceptance: + - "目标项目可启动(serve)" + - "模块能力可用(根据 module_id 的验收点)" diff --git a/AI-Coding/openspec-lite/tasks/extract-plugin.yaml b/AI-Coding/openspec-lite/tasks/extract-plugin.yaml new file mode 100644 index 0000000..e29e159 --- /dev/null +++ b/AI-Coding/openspec-lite/tasks/extract-plugin.yaml @@ -0,0 +1,35 @@ +# Task Template: 抽取插件(library/plugins/*) + +task: + id: extract-plugin + intent: "从本仓库抽取一个或多个插件到目标项目,确保注入点与依赖闭包完整" + +inputs: + required: + - plugins # 例如 ["vab", "directive", "errorLog", "support"] + - target_project_type + - extraction_style + +scope: + base_dir: "library/plugins" + +dependency_closure_checklist: + runtime: + - "vab:Element Plus 的 ElMessage/ElLoading/ElMessageBox/ElNotification 是否全局可用" + - "directive:src/utils/permission 与权限数据来源是否齐全" + - "errorLog:src/store/modules/errorLog 与 config/errorLog 是否齐全" + - "support:__APP_INFO__ 注入方式是否一致" + wiring: + - "是否通过 setupVab(library/index.ts) 自动 app.use() 安装;若非自动,需要在入口手动 app.use()" + +outputs: + required_sections: + - changed_files + - extracted_modules + - dependency_closure + - acceptance_commands + - risks + +acceptance: + - "插件安装后,不影响应用启动" + - "涉及的注入能力/指令/错误捕获按模块规格验收" diff --git a/AI-Coding/openspec-lite/tasks/extract-theme.yaml b/AI-Coding/openspec-lite/tasks/extract-theme.yaml new file mode 100644 index 0000000..d7c6961 --- /dev/null +++ b/AI-Coding/openspec-lite/tasks/extract-theme.yaml @@ -0,0 +1,41 @@ +# Task Template: 抽取主题系统(VabTheme + settings 主题闭包) + +task: + id: extract-theme + intent: "从本仓库抽取 Theme 系统到目标项目,确保事件总线 + settings theme 闭包完整" + +inputs: + required: + - target_project_type # vue-cli | vite | other + - extraction_style # copy-module | copy-snippet + +scope: + entrypoints: + - library/components/VabTheme/ + - src/store/modules/settings.ts + - library/plugins/vab.ts + - library/styles/variables/ + +dependency_closure_checklist: + runtime: + - "Pinia settings store 是否存在并已在入口初始化" + - "vab 插件是否安装($pub/$sub/$baseLoading 注入)" + - "i18n translate 是否可用" + - "Element Plus Drawer/表单控件是否可用" + bundler: + - "settings.updateTheme() 的 require('@vab/styles/variables/vab-*-variables.module.scss') 在目标构建器中是否可用" + alias: + - "@ -> src" + - "@vab -> library" + +outputs: + required_sections: + - changed_files + - extracted_modules + - dependency_closure + - acceptance_commands + - risks + +acceptance: + - "触发 $pub('theme') 能打开 Theme Drawer" + - "修改主题并保存后,刷新页面主题持久化且样式变量生效" diff --git a/README.md b/README.md index 0ac53c9..d538a99 100644 --- a/README.md +++ b/README.md @@ -1,112 +1,165 @@ -

-VAB -

admin-plus

-
+# Admin Plus(Vue3 企业级开发模板) -## 🔈 框架使用建议 +面向公司项目的前端模板与基础库:内置 `Vue 3 + Vue CLI 5 + TypeScript + Pinia + Vue Router + Element Plus`,并集成 Mock、代码生成(Plop)、多套布局组件与常用工具链,支持在此基础上快速裁剪与复用。 -- 使用前请一定先阅读 vip 群文档及群文档中的常见问题,一般在群公告前 5 条。 -- 对于常见问题可直接使用 qq 群【消息记录】功能快速寻找到答案。 -- 如果您经过 qq 群聊天记录、翻阅文档、百度后努力尝试仍无法解决问题,可通过 vip 群寻求帮助,讨论时间法定工作日 10 点-16 点。 -- 2021 年 3 月 6 日后,main 分支支持 ts、js 混合开发,建议不熟悉 ts 的用户继续使用 js,熟悉 ts 用户可自行选择开发语言。 -- 对于热心回答群内其他成员问题的用户,所提建议将优先被采纳,并可获得部分内测版本体验资格。 -- 关于举报盗版侵权:请发送举报材料至fanhuihui1998@126.com,一经查实,官司所得收入 20%归举报人所有,80%归律师事务所所有。 -- 关于客服人员满意度评价以及相关建议:请发送材料至fanhuihui1998@126.com,邮件标题:满意度评价,邮件正文:评价依据,我们必将认真对待每一位客户的诉求。 -- 关于 bug 反馈:请发送材料至fanhuihui1998@126.com,邮件标题:bug 反馈,邮件正文:bug 截图及描述。 +> 本仓库同时提供面向 AI 的结构说明与模块索引:见 [AI-Coding/README.md](AI-Coding/README.md)。 -## 🔈 框架使用约定 +--- -- 1.购买者可将授权后的产品用于任意「符合国家法律法规」的应用平台,禁止用于黄赌毒等危害国家安全与稳定的网站,否则我们有权利收回产品授权及更新权限,并根据事态轻重追究相应法律责任。 -- 2.购买主体购买后可用于开发商业项目,不限制域名和项目数量,购买主体不可将源码分享第三方,否则我们有权利收回产品授权及更新权限,并根据事态轻重追究相应法律责任。 -- 3.购买者务必尊重知识产权,严格保证不恶意传播产品源码、不得直接对授权的产品本身进行二次转售或倒卖、开源、不得对授权的产品进行简单包装后声称为自己的产品等,无论有意或无意,我们有权利收回产品授权及更新权限,并根据事态轻重追究相应法律责任。 -- 4.购买者不可将 vip 群文档及资料分享给第三方,否则我们有权利收回产品授权及更新权限,并根据事态轻重追究相应法律责任。 -- 5.购买者购买项目不可以用来构建存在竞争性质的产品并直接对外销售否则我们有权利收回产品授权及更新权限,并根据事态轻重追究相应法律责任。 -- 6.购买者购买项目中的源码(包含全部源码、及部分源码片段)不可以用于任何形式的开源项目,不可将源码放置于码云、github等开源平台,否则我们有权利收回产品授权及更新权限,并根据事态轻重追究相应法律责任。 -- 7.购买者用于公司的项目商用时购买必须提供公司名称,用于证明购买过我们的项目来进行商业用途,防范法律风险,我们承诺对购买公司信息信息严格保密,不会泄漏到互联网或用于产品宣传。 -- 8.购买者用于个人学习需提供姓名、手机联系方式进行实名认证,如无法提供请勿下单。 -- 9.如用于外包项目,购买者购买项目中的源码不可直接对外出售,npm run build 编译后的项目不受限制。 -- 10.如果您的公司基于 Vab Admin 系列自行研发的产品(如 OA、ERP、SASS 等)需对外销售,并且产品中包含我们框架的前端源码,那么您无法购买以上版本,需联系客服购买专属定制版本(不为第三方提供前端框架代码请忽略本条)。 -- 11.虚拟物品下单后不支持退货退款。 -- 12.购买者需遵守以上约定,最终解释权归 vab 系列著作权人所有,如果您无法遵守以上约定,请勿下单。 +## 亮点 -``` -注:以上协议以 //vuejs-core.cn/authorization/ 底部为准 +- **开箱即用**:工程化配置齐全(TS/ESLint/Prettier/Stylelint/Unit Test)。 +- **可扩展的 UI/布局体系**:`library/layouts/`、`library/components/` 内置多布局与业务型基础组件。 +- **Mock 一体化**:开发环境通过 `devServer.setupMiddlewares` 自动挂载 `mock/` 路由。 +- **现代依赖栈**:`Element Plus` + `@element-plus/icons-vue`,并内置图标组件 `vab-icons`。 +- **可生成代码**:通过 `plop` 快速生成页面/CRUD/组件/mock&api。 + +--- + +## 环境要求 + +- Node.js:建议 **16/18/20 LTS**(与 Vue CLI 5 兼容) +- 包管理器:推荐 `pnpm`(项目脚本也提供 `npm` 用法) + +--- + +## 快速开始 + +### 1)安装依赖 + +```powershell +cd d:\project\Web_Template_Vue3_Dev +pnpm i ``` -## 🔗 链接 - -- 💻 常规版演示地址:[admin-plus](//vuejs-core.cn/admin-plus/) -- 📝 使用文档:(文档地址及密码请查看 vip 群群公告第一条) -- 🗃 更新日志:[Releases](https://github.com/zxwk2024/admin-plus/releases) -- 📌 付费版及 vip 群购买地址:[购买地址](//vuejs-core.cn/authorization/) - - - -## ✅ 版权须知 - -Vab Admin 系列产品受国家计算机软件著作权保护(证书号:软著登字第 7051316 号), -禁止公开及传播产品源文件、二次出售等, -违者将承担相应的法律责任,并影响自身使用。 - -## 🧑‍💻 增值服务 - -### vip 群 - -- 每位购买 Admin 的用户均可获得 1 个免费的 vip 互助群免费入群资格,可反馈 bug、协助框架问题解答,无需额外购买 - -- 免费名额之外,额外加入 vip 群 (100/人 仅限已购买框架的的公司员工加入,购买后联系 微信 zxwk-bfq 即可) - -- [购买地址,网页右下角切换付款码即可](//vuejs-core.cn/authorization/) - -### 定制开发 - -- 承接各类基于 vab 开发的前端项目 -- 承接项目范围 3K+ 至 无上限 -- 支持签订合同 -- 支持提供发票 -- 结算流程:前期款(50%)- 中期款(30%)- 尾款(20%) -- 联系方式:见当前页底部 - -### 企业一对一远程培训 - -- 承接一对一远程培训服务(支持提供发票) -- 承接时间: 周一至周六上午 10 点 - 晚上 10 点 -- 价格:400 - 10000 -- 承接方式:单次、包月、包年 -- 联系方式:见当前页底部 - -### 个人一对一技术指导 - -- 承接时间: 周一至周六上午 10 点 - 晚上 10 点 -- 价格:300 - 500 -- 承接方式:单日 -- 支持零基础远程教学(学员需学习刻苦,有上进心) -- 学员需完成老师布置的任务 -- 联系方式:见当前页底部 - -### 联系方式 - -```txt - -微信客服:zxwk-bfq (备注来意) - - -邮件标题:企业一对一远程培训 - 公司名称,定制开发 - 公司名称,一对一技术支持 - 公司名称 - -邮件内容:大致描述 + 联系方式 + 预估需要时间 + 预算 - -后续: 收到邮件后,工作人员会于第一时间回复 +如你使用 npm: +```powershell +cd d:\project\Web_Template_Vue3_Dev +npm i ``` + +### 2)启动开发环境 + +```powershell +pnpm run serve +``` + +### 3)构建 + +```powershell +pnpm run build +``` + +### 4)单元测试 / 代码质量 + +```powershell +pnpm run test:unit +pnpm run lint +pnpm run lint:eslint +pnpm run lint:prettier +pnpm run lint:stylelint +``` + +--- + +## 常用脚本(scripts) + +来自 `package.json`: + +- **`serve`**:启动开发服务器(本地 Mock 会自动挂载)。 +- **`build`**:生产构建。 +- **`build:compress` / `compress`**:构建后压缩产物(`scripts/compress.js`)。 +- **`build:website`**:站点构建(使用 `VAB_VARIABLE=website` + 压缩)。 +- **`test:unit`**:单元测试。 +- **`lint`**:Vue CLI ESLint。 +- **`lint:eslint` / `lint:prettier` / `lint:stylelint`**:更精细的格式化与风格修复。 +- **`template`**:启动 `plop` 代码生成。 + +--- + +## 项目结构(速览) + +> 更完整、可供 AI 检索复用的索引见 [AI-Coding/README.md](AI-Coding/README.md)。 + +```text +src/ 应用主代码(入口、路由、状态、国际化、业务页面) +library/ 模板基础库(全局样式、插件集合、构建扩展等) +library/components/ 可复用基础组件(Vab* 体系) +library/layouts/ 多套布局(含主 Layout 入口) +mock/ 本地 Mock 服务(devServer 中间件挂载) +types/ 全局类型声明与业务类型 +tests/unit/ 单元测试 +scripts/ 构建后处理脚本(如压缩) +public/ 静态资源与 HTML 模板 +``` + +--- + +## 关键约定与入口 + +### 应用入口 + +- `src/main.ts` + - 创建应用并挂载:`createApp(App)` + - 初始化顺序:`setupVab` → `setupI18n` → `setupStore` → `setupRouter` + - 生产环境可自动启用 Mock:当 `baseURL` 不是外链地址时(见 `src/main.ts` 的判断) + +### 路由 + +- `src/router/index.ts` + - `constantRoutes`:如登录/注册/404 等基础路由 + - `asyncRoutes`:业务路由(包含 `Layout` 作为壳) + +### 状态管理 + +- `src/store/index.ts`:Pinia 初始化与注入。 + +### 国际化 + +- `src/i18n/index.ts` + - `createI18n({ legacy:false })` + - 当前语言读取自 `useSettingsStore(pinia)` + +### Mock + +- `mock/index.js` + - 挂载到开发服务器中间件(在 `vue.config.js` 的 `devServer.setupMiddlewares`) + - 基于 `mockjs` 返回数据,并支持文件变更热更新 + +--- + +## 别名(Alias) + +来自 `vue.config.js` / `tsconfig.json`: + +- `@` → `src` +- `~` → 项目根目录 +- `/#` → `types` +- `@vab` → `library` +- `@gp` → `library/plugins/vab` + +--- + +## 代码生成(Plop) + +项目提供 `plop` 生成器(见 `plopfile.js`): + +- `view`:页面 +- `curd`:CRUD +- `component`:组件 +- `mock&api`:Mock 与 API 片段 + +运行: + +```powershell +pnpm run template +``` + +--- + +## 贡献与团队协作建议 + +- 建议以“业务模块”为单位在 `src/views/` 组织页面。 +- API 按域拆分在 `src/api/`,与 `mock/controller/` 保持同名与路径一致,方便对照。 +- 公共能力优先沉淀在 `library/`(全局插件/样式/构建扩展)与 `library/components/`(可视组件)。