Files
xiaotutongxue/frontend-pc/.agents/skills/project-guide/SKILL.md
T
lovife e94e76ba0f fix(pc): 找回被 #feedback 吞掉的表单校验错误 + 收集任务文案精简
naive 的 NFormItem 一旦用了 #feedback 插槽,renderExplains 就永不渲染
(children || feedback ? feedback : …),校验错误被完全遮住 —— 用户看到的是
"红框 + 一句无关的正常提示",现象上等价于"点了保存没反应"。RoleFormModal 里
早就记录过这个机制,但只在自己那个文件绕开了,没固化成约定,于是扩散到 7 处。

- 7 处 validate() 的 catch 改成 message.error(firstError(errors) ?? 兜底)
  (login / org / role / user / task 建改任务 / 材料规则 / 表单设计)
- ⚠️ firstError 必须摊平后再取:naive reject 出来的是嵌套数组,
  errors[0].message 恒为 undefined,兜底文案会把每条错误都吞成"请检查表单填写"
- 批次号在"修改"时置灰(task_code = 批次号 + 4 位序号,且同时是文件目录名,
  而服务端 update 无条件写 batch_no、没有"编码已生成"的保护)
- 删掉「本期范围」区块(收集模式 / 认定类型恒定,请求体里仍写死 1)
- 清掉教材式描述:extras_json / extra_ 前缀 / 幂等 / 接收能力 / 识别结果只用于提示
- 规则沉淀进 frontend-pc/AGENTS.md 第 10 条与交接摘要 §一·四十
2026-10-13 15:06:34 +08:00

5.6 KiB
Raw Blame History

name, description
name description
project-guide PC 管理端的技术栈、目录结构、菜单与权限模型,以及十条最容易写错的约定。开始任何 PC 端改动前加载本技能。

项目总览(PC 管理端)

技术栈

项 值
框架 Vue 3.5 + TypeScript + Vite 7
UI Naive UI 2.42(2026-10 从 Gi Admin Pro + Arco 重搭,已无 Arco/Gi 依赖)
状态 Pinia(pinia-plugin-persistedstate)
请求 axios(src/utils/http.ts 统一封装)
样式 SCSS(无 Stylelint)
检查 pnpm typecheck(vue-tsc,当前 0 错)、pnpm lint(@antfu/eslint-config)

目录

src/
├── apis/xt/          接口模块(按业务域分文件)+ types.ts(通用类型)
├── composables/      pagination.ts(列表页分页器统一参数)
├── config/           xt-routes.ts(**业务菜单表:菜单 + 路由 + 分组**)
├── icons/            AppIcon.vue + icon-data.ts(由 scripts/extract-pc-icons.py 生成)
├── layout/           AppSider / AppHeader / index.vue
├── router/           index.ts(本地菜单表按后端 menuKeys 过滤后注册)
├── stores/           user.ts(登录态/权限/菜单)、acting-tenant.ts(平台代管学校)、app.ts
├── styles/           index.scss(只放跨页面样式)
├── utils/            http.ts / naive.ts / tag.ts / format.ts / auth.ts / rsa.ts
└── views/            页面(每个菜单一个目录,行内抽屉与弹窗同目录)

菜单与权限模型(后端决定可见性)

  • 前端 config/xt-routes.ts 是唯一菜单表:key(菜单键)↔ 路由 ↔ 视图 ↔ 分组。
  • 能不能看由后端决定:/auth/me 返回 menus: string[],前端只按它过滤; 绕过前端也只会拿到 40301。
  • 能不能做看权限码:useUserStore().has('码'),字符串必须与后端 PermissionDef 逐字一致 (progress:viewAll 是驼峰,不是 progress:view:all —— 写错只会恒为 false,不报错)。
  • 平台运营要先选学校(stores/acting-tenant.ts → 请求头 X-Tenant-Id); 不选就不发请求,否则会把各租户数据混在一起且看起来正常。

十条最容易写错的约定

  1. 没有 auto-import:组件与 API 全显式 import,标签写 PascalCase。 漏 import 是静默故障(未知自定义元素 → 样式整块丢掉,typecheck/lint 全绿); python scripts/check-wiring.py 第 ⑧ 项会核对。
  2. 没有 v-hasPerm:权限判断写 user.has('码')。
  3. n-tag 的 type 必须过 @/utils/tag.ts(Arco 色词直接透传会静默变灰)。
  4. 图标要重跑脚本:scripts/extract-pc-icons.py → src/icons/icon-data.ts(37 个); 缺图标是空占位、不报错 → 图标集升级后必须重跑。
  5. n-tab-pane 的标签用 #tab 插槽,不要用 tab="文字" 属性 —— naive-ui 2.45.3 的属性写法只渲染出空注释节点,整排 tab 一个字都不显示(不报错)。
  6. 卡片式版面:白卡片靠全局 --xt-bg 灰底浮起来;卡片外观由 styles/index.scss 统一给, 页面不要再自己写;卡片内部分区用 .section-title + 浅灰小格子,不要卡里套卡。 详见 code-css 的「设计基线」一节。
  7. 品牌标是几何标:src/assets/images/logo.svg(方 = 卷宗,环 = 在线)。 不要换回文字字形标;页签用 public/favicon.svg(加粗版),改一个要同步另一个。 ⚠️ SVG 注释里不能出现连续两个减号(XML 规范禁止)→ 整个文件 not well-formed, 图片裂开而控制台一句话都不报;python scripts/check-wiring.py 第 ⑨ 项会核对。
  8. 详情用抽屉、不新开路由:NDrawer + NDrawerContent + NDescriptions, 抄 src/views/audit/AuditDetailDrawer.vue;入口是操作列里的 text 按钮(size="small")。 ⚠️ 先确认列表接口给没给详情要的字段 —— 例:/tenants 的 contactPhone 恒为 null, 脱敏值只有 /tenants/{id} 给;拿行数据凑详情会显示成"没有联系电话"。
  9. 页面内的页签用工具条分段按钮,不要 NTabs:NTabPane 要额外 flex 化才能维持 .page-card 高度链,否则表格高度算成 0、一行都看不见(本仓踩过); 且第 5 条那个 tab="..." 的坑也在这条路上。各页签的 list/page 状态分开维护。
  10. #feedback 会遮住校验错误:NFormItem 只要用了这个插槽, renderExplains(校验错误)就永不渲染(children || feedback ? feedback : …), 用户看到的是「红框 + 一句无关的正常提示」=「点了保存没反应」。 所以带 #feedback 的表单,validate() 失败必须自己 message.error(firstError(errors) ?? '请检查表单填写')。 ⚠️ firstError 必须摊平后再取(naive reject 的是嵌套数组,errors[0].message 恒为 undefined);写法抄 src/views/tenant/TenantEditModal.vue。 ⚠️ 该插槽不接收任何参数,没有 #feedback="{ status }" 这种写法 —— 想内联渲染错误只能给 feedback prop 传渲染函数(要自写包装组件)。

开发

pnpm install
pnpm dev          # → http://localhost:5173,/api 代理到 8080(后端不起着就没有数据)
pnpm typecheck && pnpm lint

演示账号:admin / Admin@123 + 租户码 demo(不是 Gi README 里的 admin/123456)。