e94e76ba0f
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 条与交接摘要 §一·四十
89 lines
5.6 KiB
Markdown
89 lines
5.6 KiB
Markdown
---
|
||
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`)。
|