From dbda9f76fb458540bcbac7d7fb46f5090bdf9cd6 Mon Sep 17 00:00:00 2001 From: zxr <271055687@qq.com> Date: Wed, 22 Jul 2026 04:55:36 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E7=BB=9F=E4=B8=80=E7=9F=A5?= =?UTF-8?q?=E8=AF=86=E5=BA=93=E6=8E=A5=E5=8F=A3=E5=85=AC=E5=85=B1=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .superpowers/sdd/task-3-report.md | 74 ------------------------------- src/api/kb/category.ts | 2 +- src/api/kb/document.ts | 2 +- src/api/kb/faq.ts | 34 +++----------- src/api/kb/favorite.ts | 3 +- src/api/kb/review.ts | 3 +- src/api/kb/trash.ts | 2 +- src/api/kb/types.ts | 7 +++ 8 files changed, 19 insertions(+), 108 deletions(-) delete mode 100644 .superpowers/sdd/task-3-report.md create mode 100644 src/api/kb/types.ts diff --git a/.superpowers/sdd/task-3-report.md b/.superpowers/sdd/task-3-report.md deleted file mode 100644 index 3af0274..0000000 --- a/.superpowers/sdd/task-3-report.md +++ /dev/null @@ -1,74 +0,0 @@ -# Task 3:前端权限数据与菜单路由兼容 - -## 实现 - -- AppStore 新增 `permissionCodes: string[]`,在权限响应过滤前保存全部非空权限码并去重。 -- 动态菜单在进入 `buildTree` 前严格筛选 `type === 1`;路由转换入口与子路由递归同时防御性过滤非菜单节点。 -- `clearServerMenu()` 同步清空 `serverMenu` 与 `permissionCodes`,现有退出登录链路无需改动即可完成权限清理。 -- 新增 `usePermissionCodes` Hook,统一提供响应式权限码集合与 `hasPermission(code)`,不解析角色名称。 -- 删除权限响应、树构建、路由转换及视图模块映射的相邻调试输出和无用导入。 - -## 变更文件 - -- `src/store/modules/app/index.ts` -- `src/store/modules/app/types.ts` -- `src/router/menu-data.ts` -- `src/hooks/usePermissionCodes.ts` - -## 验证 - -1. `pnpm exec prettier src/store/modules/app/index.ts src/store/modules/app/types.ts src/router/menu-data.ts src/hooks/usePermissionCodes.ts --write` - - 结果:成功,随后冻结源码。 -2. 首轮 `pnpm exec vue-tsc --noEmit --incremental false` - - 结果:全仓 828 条错误,改动文件 1 条;定位为 Hook 中 Pinia 索引签名导致 `permissionCodes.value` 被推断为 `unknown`。 - - 处理:仅在 Hook 响应式边界补充显式 `string[]` 类型,重新格式化并再次冻结源码。 -3. 最终 `pnpm exec vue-tsc --noEmit --incremental false` - - 结果:退出码 2,全仓错误行 827,与已确认基线一致;四个改动文件错误行 0,本次新增类型错误为 0。 -4. `pnpm build -- --outDir C:\Users\27105\AppData\Local\Temp\front-kb-task3-build-20260721` - - 结果:退出码 0,Vite 7.3.1 成功转换 9055 个模块并完成生产构建。 - - 清理:临时构建目录已删除;误启动的默认 `dist` 产物也已删除,仓库内无构建产物残留。 -5. 内联 Node + 最小 Vite SSR 加载器模拟 `type=1` 菜单和 `type=2` 按钮权限响应,直接调用实际 `buildTree` 与 `transformMenuToRoutes`。 - - 结果:`permission_codes=kb:article:list,kb:article:create`、`menu_permissions=1`、`routes=1`、`empty_path=false`、`redirect=false`、`button_in_routes=false`,退出码 0。 -6. `git diff --check` - - 结果:退出码 0。 - -## 自审 - -- 完整权限码在菜单过滤前保存,按钮权限不会因菜单过滤丢失。 -- `type=2` 不进入树构建,也无法从递归路由转换入口生成空路径或重定向路由。 -- 退出登录继续沿用现有 `userStore.logoutCallBack() -> appStore.clearServerMenu()`,权限码同步清空。 -- Hook 仅按权限码精确匹配,不依赖用户角色;未新增测试文件,未修改生产固定密钥或无关路由。 - -## Concerns - -- 全仓类型检查仍因已确认的 827 条基线错误返回退出码 2;本任务四个改动文件错误为 0,生产构建成功。 - -## 审查修复 - -### 实现 - -- 将 `ServerMenuItem.type` 收紧为必填 `number | string`。 -- 新增统一的 `isMenuPermission` predicate,仅接受数值 `1` 或字符串 `"1"`。 -- AppStore 构树前过滤、顶层路由转换和递归子路由转换统一调用该 predicate;数值/字符串 `2`、缺失 `type` 和其他值均严格排除。 - -### 验证 - -1. `pnpm exec vue-tsc --noEmit --incremental false` - - 结果:全仓保持 827 条基线错误,任务四个改动文件错误 0。 -2. `pnpm build -- --outDir C:\Users\27105\AppData\Local\Temp\front-kb-task3-review-build-20260721` - - 结果:退出码 0,Vite 7.3.1 成功转换 9055 个模块并完成生产构建。 -3. 内联 Node + 最小 Vite SSR 加载器直接调用实际 `isMenuPermission`、`buildTree` 和 `transformMenuToRoutes`。 - - 覆盖:数值 `1`、字符串 `"1"`、数值 `2`、字符串 `"2"`、缺失 `type`,并同时验证顶层和递归子路由。 - - 结果:`predicate=true,true,false,false,false`、完整权限码 5 个、菜单权限 2 个、顶层路由 2 个、递归子路由 2 个、`empty_path=false`、`rejected_in_routes=false`,退出码 0。 -4. `git diff --check` - - 结果:退出码 0。 - -### 自审 - -- `type` 在接口层必填,运行时仅 `1` 和 `"1"` 能进入菜单链路;缺失值没有兼容放行。 -- 三个过滤位置共享同一 predicate,不存在数值/字符串判断漂移。 -- 权限码仍在过滤前完整保存,未扩展无关 API 或路由重构,源码冻结后未再修改。 - -### Concerns - -- 工具策略拦截了临时构建目录的原生 PowerShell 删除命令,未采用替代删除方式;待清理路径为 `C:\Users\27105\AppData\Local\Temp\front-kb-task3-review-build-20260721`。 diff --git a/src/api/kb/category.ts b/src/api/kb/category.ts index f12def6..9e1176f 100644 --- a/src/api/kb/category.ts +++ b/src/api/kb/category.ts @@ -1,5 +1,5 @@ import { request } from '@/api/request' -import type { KbReply } from './faq' +import type { KbReply } from './types' /** 公共分类状态。 */ export type CategoryStatus = 'active' | 'inactive' diff --git a/src/api/kb/document.ts b/src/api/kb/document.ts index 103b652..86a6076 100644 --- a/src/api/kb/document.ts +++ b/src/api/kb/document.ts @@ -1,5 +1,5 @@ import { request } from '@/api/request' -import type { KbReply } from './faq' +import type { KbReply } from './types' /** 文档状态。 */ export type DocumentStatus = 'draft' | 'published' | 'reviewed' | 'rejected' diff --git a/src/api/kb/faq.ts b/src/api/kb/faq.ts index a678a66..ddcaa95 100644 --- a/src/api/kb/faq.ts +++ b/src/api/kb/faq.ts @@ -1,4 +1,9 @@ import { request } from '@/api/request' +import type { KbReply } from './types' + +export { fetchCategoryTree as fetchGeneralCategoryTree } from './category' +export type { CategoryTreeNode } from './category' +export type { KbReply } from './types' /** FAQ 审核状态。 */ export type FaqStatus = 'draft' | 'published' | 'reviewed' | 'rejected' @@ -7,14 +12,6 @@ export type FaqPriority = 'low' | 'medium' | 'high' /** FAQ 列表可见范围。 */ export type FaqScope = 'my' | 'all' -/** 知识库服务统一响应。 */ -export interface KbReply { - code: number - message: string - details: T - timeseq: number -} - /** FAQ 资源。 */ export interface Faq { id: number @@ -96,24 +93,6 @@ export interface UpdateFaqData extends FaqFormData { id: number } -/** 公共分类树节点。 */ -export interface CategoryTreeNode { - id: number - name: string - description: string - type: 'general' - icon: string - color: string - parent_id: number - level: number - path: string - sort_order: number - status: 'active' | 'inactive' - doc_count: number - faq_count: number - children: CategoryTreeNode[] -} - /** FAQ 状态筛选项。 */ export const faqStatusOptions: Array<{ label: string; value: FaqStatus }> = [ { label: '草稿', value: 'draft' }, @@ -196,6 +175,3 @@ export const unfavoriteFaq = (id: number) => resource_type: 'faq', resource_id: id, }) - -/** 获取公共分类树。 */ -export const fetchGeneralCategoryTree = () => request.get>('/Kb/v1/category/tree') diff --git a/src/api/kb/favorite.ts b/src/api/kb/favorite.ts index 53dd85c..26d0deb 100644 --- a/src/api/kb/favorite.ts +++ b/src/api/kb/favorite.ts @@ -1,6 +1,7 @@ import { request } from '@/api/request' import type { Document } from './document' -import type { Faq, KbReply } from './faq' +import type { Faq } from './faq' +import type { KbReply } from './types' export type ResourceType = 'document' | 'faq' diff --git a/src/api/kb/review.ts b/src/api/kb/review.ts index 9bf1a21..45e6210 100644 --- a/src/api/kb/review.ts +++ b/src/api/kb/review.ts @@ -1,6 +1,7 @@ import { request } from '@/api/request' import type { Document } from './document' -import type { Faq, KbReply } from './faq' +import type { Faq } from './faq' +import type { KbReply } from './types' export type ReviewResourceType = 'all' | 'document' | 'faq' diff --git a/src/api/kb/trash.ts b/src/api/kb/trash.ts index 263f6a4..f98b6ab 100644 --- a/src/api/kb/trash.ts +++ b/src/api/kb/trash.ts @@ -1,5 +1,5 @@ import { request } from '@/api/request' -import type { KbReply } from './faq' +import type { KbReply } from './types' export type TrashResourceType = 'document' | 'faq' diff --git a/src/api/kb/types.ts b/src/api/kb/types.ts new file mode 100644 index 0000000..833f9bb --- /dev/null +++ b/src/api/kb/types.ts @@ -0,0 +1,7 @@ +/** 知识库服务统一响应。 */ +export interface KbReply { + code: number + message: string + details: T + timeseq: number +}