遮罩底色被 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 的 §一·四十二。
8.1 KiB
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 |
快速入口
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)
十条最容易写错的约定
- 没有 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第 ⑧ 项会静态核对这件事。 - 权限判断:
useUserStore().has('码')/hasAny(...)(没有v-hasPerm指令); 权限码字符串必须与后端PermissionDef逐字一致(progress:viewAll是驼峰)。 - 状态色:
n-tag的type必须过@/utils/tag.ts的tagOf()——apis/xt/*.ts里的展示映射仍是 Arco 色词(green/arcoblue/gold), 直接透传给n-tag会静默变灰且不报错。 - 图标:用
@/icons/AppIcon.vue+ 图标名;图标数据由scripts/extract-pc-icons.py生成到src/icons/icon-data.ts(37 个), 缺图标是空占位、不报错 → 图标集升级后要重跑该脚本。 n-tab-pane的标签用#tab插槽,不要用tab="文字"属性 —— naive-ui 2.45.3 的属性写法只渲染出一个空注释节点,整排 tab 一个字都不显示(不报错)。- 卡片式版面:白卡片靠全局的
--xt-bg灰底浮起来;卡片外观(描边/阴影/圆角/卡片头色带) 由src/styles/index.scss的.n-card.n-card统一给,页面不要再自己写; 卡片内部分区用.section-title+ 浅灰小格子,不要卡里套卡。 工具条控件同一行尺寸要一致(统一默认尺寸,别混size="small")。 - 品牌标:几何标在
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第 ⑨ 项会核对这件事。 - 详情用抽屉,不要为详情新开路由:
NDrawer+NDrawerContent+NDescriptions, 模板抄src/views/audit/AuditDetailDrawer.vue。列表页的"详情"入口是操作列里的一个text按钮(size="small",见第 6 条的行内按钮约定)。 ⚠️ 抽屉打开时该调哪个接口要看清楚:列表接口可能刻意不返回某个字段 (例:/tenants的contactPhone恒为null,脱敏值只有/tenants/{id}给), 拿行数据凑详情会显示成"没有联系电话"。 - 页面内的页签用工具条上的分段按钮,不要用
NTabs/NTabPane: ①NTabPane的内容要额外 flex 化才能维持.page-card的高度链, 否则表格高度算成 0、一行数据都看不见(本仓踩过); ② 见第 5 条,naive 2.45.3 的tab="..."属性写法本身也是坏的。 两个按钮做的分段控件在视觉上就是页签,且不动高度链。 多个页签各自的list/total/pageNum/loading要分开维护,切换时重置到第 1 页。 NFormItem一旦用了#feedback插槽,校验错误就被完全遮住 —— naive 的渲染是children || feedback ? feedback : renderExplains(2.45.3 的FormItem.mjs), 插槽存在时renderExplains永不渲染,用户看到的是「红框 + 一句无关的正常提示」, 现象上等价于「点了保存没反应」。所以凡是有#feedback的表单,validate()失败必须自己弹:
} 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。