Files
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

89 lines
5.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: project-guide
description: 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](../code-css/SKILL.md) 的「设计基线」一节。
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** 传渲染函数(要自写包装组件)。
## 开发
```bash
pnpm install
pnpm dev # → http://localhost:5173,/api 代理到 8080(后端不起着就没有数据)
pnpm typecheck && pnpm lint
```
演示账号:`admin / Admin@123` + 租户码 `demo`(**不是** Gi README 里的 `admin/123456`)。