Files
lovife 623e6393d5 fix(pc): 弹窗遮罩改毛玻璃并清掉 9 处提示折行
遮罩底色被 Naive 写死在 css-render 里(Modal / Drawer 主题没有 mask token,
themeOverrides 改不到),只能上 CSS;加 backdrop-filter 后遮罩后面的页面糊掉、
弹层本体仍是纯白,NModal / NDrawer / useDialog 共用 .n-modal-mask 一条规则覆盖。

折行的根因是格子宽度写死(半宽 264px / span 8 192px / 定宽 160px),与窗口宽度无关:
能压短的压短;「文件名预览」那处是 .n-form-item-blank 属于 flex 行、把提示挤成 3 行,
改成包一层块级容器;材料预览失败的占位在 108px 小格里曾折 3 行且被裁掉。

约定写进 frontend-pc/AGENTS.md 第 11 条与 code-css skill;实测与刻意不改的清单见
docs/交接摘要.md 的 §一·四十二。
2026-10-14 15:34:40 +08:00

115 lines
8.1 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.
# AGENTS.md
面向 AI 编码助手的项目指南(PC 管理端)。规范以 `.agents/` 为唯一来源。
> ⚠️ **2026-10 已重搭**:从 **Gi Admin Pro + Arco Design Vue** 换成 **纯 Vue 3 + Naive UI 2.42**。
> 上一版基于 Gi/Arco 的 skills 已全部重写 —— **不要再按 Arco 的组件名(`a-table` / `a-form`)、
> `v-hasPerm`、auto-import、Stylelint 去写代码**。
## 常驻规则(Agent 行为准则)
> 源文件:`.agents/rules/agent.md`(修改后请同步本节)
- 始终使用**简体中文**与用户沟通。
- 未经用户明确要求,**不要**创建说明文档、执行 git commit/push、修改 git 配置。
- 改动范围最小化;优先复用项目已有抽象,禁止凭空引入新架构。
- 改代码前先阅读项目既有实现;任务完成后视情况运行 lint / typecheck。
- **按需读取** skills,不要预加载全部。
**编码前按任务加载 skill:**
| 任务 | 技能 |
|------|------|
| 写/改 `.vue` | `.agents/skills/code-vue/SKILL.md` |
| 写/改样式 | `.agents/skills/code-css/SKILL.md` |
| 写/改 `.ts` / composable | `.agents/skills/code-ts/SKILL.md` |
| 项目约定、Naive UI 组件与目录 | `.agents/skills/project-guide/SKILL.md` |
| CRUD 列表/表单 | `.agents/skills/project-crud/SKILL.md` |
| 接口调用(`src/apis/xt/`) | `.agents/skills/project-api/SKILL.md` |
| 提交代码(git commit) | `.agents/skills/git-commit/SKILL.md` |
## 快速入口
```bash
pnpm dev # 开发(/api 代理到 http://localhost:8080,没有 Mock)
pnpm build # 生产构建
pnpm typecheck # vue-tsc(**当前 0 错**,有报错就是新引入的)
pnpm lint # ESLint(@antfu/eslint-config)
```
- 路径别名:`@` → `src/`
- **开发没有 Mock**:`pnpm dev` 把 `/api` 代理到 `http://localhost:8080`
(见 `vite.config.ts` 的 `server.proxy`)→ **后端不起着就没有数据**
- 接口模块在 `src/apis/xt/`(按业务域分文件),统一从 `@/apis/xt` 或 `@/apis/xt/<域>` 引
- 演示账号:`admin / Admin@123` + 租户码 `demo`(⚠️ **不是** Gi 模板 README 里的 `admin/123456`)
## 十条最容易写错的约定
1. **没有 auto-import**:naive-ui 组件、Vue API、工具函数**全部显式 `import`**,
标签统一写 **PascalCase**(`<NButton>`)。
⚠️ 漏 import 是**静默故障**:写小写 `<n-button>` 而没 import 时,Vue 把它当未知自定义元素渲染,
**不报错、typecheck/lint 全绿,只是样式整块丢掉**(2026-10 实测踩到 4 个文件 6 处)。
`python scripts/check-wiring.py` 第 ⑧ 项会静态核对这件事。
2. **权限判断**:`useUserStore().has('码')` / `hasAny(...)`(**没有 `v-hasPerm` 指令**);
权限码字符串必须与后端 `PermissionDef` **逐字一致**(`progress:viewAll` 是驼峰)。
3. **状态色**:`n-tag` 的 `type` 必须过 **`@/utils/tag.ts`** 的 `tagOf()` ——
`apis/xt/*.ts` 里的展示映射仍是 Arco 色词(`green`/`arcoblue`/`gold`),
直接透传给 `n-tag` 会**静默变灰**且不报错。
4. **图标**:用 `@/icons/AppIcon.vue` + 图标名;图标数据由
`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` 灰底浮起来;卡片外观(描边/阴影/圆角/卡片头色带)
由 `src/styles/index.scss` 的 `.n-card.n-card` 统一给,**页面不要再自己写**;
卡片内部分区用 `.section-title` + 浅灰小格子,**不要卡里套卡**。
工具条控件同一行尺寸要一致(统一默认尺寸,别混 `size="small"`)。
7. **品牌标**:几何标在 `src/assets/images/logo.svg`(**不要换回文字字形标** —— 形状的出处与
构造参数写在该文件注释里);页签用 `public/favicon.svg`(加粗版),改一个要同步另一个,
外加侧栏/登录页的引用尺寸与 `docs/prototype/admin-web.html` 的 `.mark`。
⚠️ **SVG 的注释里不能出现连续两个减号**(XML 规范禁止):会让整个文件 not well-formed,
表现是图片裂开、**控制台一句话都不报**,typecheck/lint 也全绿。
`python scripts/check-wiring.py` 第 ⑨ 项会核对这件事。
8. **详情用抽屉,不要为详情新开路由**:`NDrawer` + `NDrawerContent` + `NDescriptions`,
模板抄 `src/views/audit/AuditDetailDrawer.vue`。列表页的"详情"入口是操作列里的一个
`text` 按钮(`size="small"`,见第 6 条的行内按钮约定)。
⚠️ 抽屉打开时该调哪个接口要看清楚:**列表接口可能刻意不返回某个字段**
(例:`/tenants` 的 `contactPhone` 恒为 `null`,脱敏值只有 `/tenants/{id}` 给),
拿行数据凑详情会显示成"没有联系电话"。
9. **页面内的页签用工具条上的分段按钮,不要用 `NTabs` / `NTabPane`**:
① `NTabPane` 的内容要额外 flex 化才能维持 `.page-card` 的高度链,
否则表格高度算成 0、**一行数据都看不见**(本仓踩过);
② 见第 5 条,naive 2.45.3 的 `tab="..."` 属性写法本身也是坏的。
两个按钮做的分段控件在视觉上就是页签,且不动高度链。
多个页签各自的 `list` / `total` / `pageNum` / `loading` 要分开维护,切换时重置到第 1 页。
10. **`NFormItem` 一旦用了 `#feedback` 插槽,校验错误就被完全遮住** ——
naive 的渲染是 `children || feedback ? feedback : renderExplains`(2.45.3 的 `FormItem.mjs`),
插槽存在时 `renderExplains` **永不渲染**,用户看到的是「红框 + 一句无关的正常提示」,
现象上等价于「点了保存没反应」。所以**凡是有 `#feedback` 的表单,`validate()` 失败必须自己弹**:
```ts
} catch (errors) {
message.error(firstError(errors) ?? '请检查表单填写')
return
}
```
⚠️ `firstError` **必须摊平后再取**:naive 的 `Form.validate()` reject 的是**嵌套数组**
(`Form.mjs` 把每层 `result.errors` 整个 push 进去),`errors[0].message` 恒为 `undefined`,
兜底文案会把每条错误都吞成「请检查表单填写」。写法抄 `src/views/tenant/TenantEditModal.vue`。
⚠️ 这个插槽**不接收任何参数**(内部用 `resolveWrappedSlot(slot, wrapper)` = `wrapper(slot())`),
所以**没有**「`#feedback="{ status }"` 里顺手渲染错误」这种写法 —— 想内联显示错误只能给
`NFormItem` 的 `feedback` **prop** 传渲染函数,那需要自写包装组件,别顺手试。
11. **提示(`#feedback` / `__hint`)要在自己的格子里单行放下**:放不下就**改短**,或把这一项改成
`:span="24"`。格子的**可用宽度是写死的**(弹窗宽度写死、两列网格、`span 8`),**窗口拉宽没有用**。
2026-10 实测过一轮:提示折成 2~3 行时读起来根本不像一句话(最惨的一处被 flex 挤到 191px、折了 3 行)。
12px 字的经验值:`span 12`(620px 弹窗)≈ **23 个汉字**、`span 8`(680px 弹窗)≈ **16 个**、
`.uf__grid` 两列 ≈ **21 个**、全宽表单项(520px 弹窗)≈ **39 个**
(11px 的 `.org-drawer__fallback` 在 160px 定宽列里 ≈ 14 个)。
⚠️ **`.n-form-item-blank` 是 flex 行**(`form-item` 是 grid、blank 区是 flex)→ 同一个 `NFormItem`
里放两个元素会被并排压缩;要"提示在上、内容在下"必须包一层块级容器
(写法见 `src/views/task/TaskFormModal.vue` 的 `__preview-box`)。
⚠️ **弹窗遮罩的毛玻璃只能用 CSS**:Naive 把遮罩色写死在 css-render 里,Modal / Drawer 主题
**没有 mask token**,`App.vue` 的 `themeOverrides` 改不到它 —— 规则在 `src/styles/index.scss`,
细节见 `.agents/skills/code-css/SKILL.md`。