624a05aa66
需求:「设计开发平台运营人员对租户的管理功能,还有日志的详情查看功能。」 ⚠️ **口径变更**(覆盖 2026-10 走查的旧结论):views/tenant/index.vue 原先写着「列表**没有操作列**: 不提供编辑 / 停用入口(客户 2026-10 走查确认的口径)」。本轮用户明确选了全量范围,所以列表现在 有操作列(详情 / 编辑 / 停用·启用)。旧注释是**改写而不是删掉**,并写明推翻理由 —— 运营的真实 诉求是:① 到期日变了要能自己改,而不是每次找开发;② 欠费 / 停止合作时要能当场停掉; ③ 每周核对"谁快到期了"时要能点进详情看用量。 1) 租户管理:操作列用 tenant:view 门控「详情」、tenant:manage 门控「编辑 / 停用 / 启用」, 拿不到管理权限时整列显示"只读",而不是留一个点了报 403 的按钮。停用走 NPopconfirm 二次确认 且文案写明后果("停用后该校所有学生与老师会立刻无法登录");只发 {status} 一个字段 —— PUT 是部分更新,回传整行会把列表里看不见的字段覆盖成空。 2) 详情抽屉 TenantDetailDrawer:打开时**同时**调 getTenantDetail 与 getTenantUsage —— 列表接口的 contactPhone **恒为 null**(电话以密文入库,列表不能成为批量拉取联系方式的通道), 脱敏值只有详情接口给,拿行数据凑详情会显示成"没有联系电话"。用量区写明"学生数与存储是**实时值**, 周期只影响计费项",否则会以为"2026Q3 的学生数"是历史值;notes 如实全列(它解释"为什么这几项是 0")。 统计周期只给本月 / 上月 / 本季三个固定选项,不做自由文本输入。 3) 编辑弹窗 TenantEditModal:类型 4 选项、状态 3 选项;数字下拉的校验规则必须带 type: 'number' (只写 required 会"看起来填了但校验不过",而且 #feedback 会把报错遮住);联系电话留空 = 不修改; 租户编码只读展示并注明"出现在扫码落地页与对接文档里,开通后不可修改"。不做"清空到期日" (MyBatis-Plus 忽略 null 更新,清不掉就不在界面上承诺)。 4) 日志页:操作日志 / 登录日志两个模式,各自维护 list / total / pageNum / pageSize / loading / 筛选条件,切换时重置到第 1 页。⚠️ 页签用工具条上的**分段按钮**而不是 NTabs / NTabPane: ① NTabPane 的内容要额外 flex 化才能维持 .page-card 的高度链,否则表格高度算成 0、 **一行数据都看不见**(本仓踩过);② naive 2.45.3 的 tab="..." 属性写法本身也是坏的, 必须用 #tab 插槽。 5) 日志详情 LogDetailDrawer:变更前后**完整不截断** + break-all + 各一个复制按钮; extraJson 格式化成 <pre>(解析失败原样展示);traceId 带复制;request_param / duration_ms 如实写"本版本未采集"(这两列从来没有写入点),不编。抽屉**不再打接口** —— 列表 SELECT 已经 把抽屉要的字段都带出来了。 6) 「单位」列只对平台显示(0 → 「平台」),两张表统一用「单位」这个词:这一列要装得下 tenant_id = 0 的「平台」,表头写「租户」而格子里写「平台」是自相矛盾的;且与筛选栏两个下拉的 占位符、租户管理页的「单位名称」一致。登录日志刻意**不要「归属地」列**(location 恒为 null, 整列空白看起来像功能坏了)。 7) 日志页的模块下拉换成导出的 MODULE_OPTIONS / TARGET_TYPE_OPTIONS:原先那 4 项里 AUDIT / EXPORT **后端一个写入点都没有**,而真会写的 FILE(代传材料)与 ROLE(6 个角色动作)**不在里面** —— 选中死项只会得到一张空表,让人以为"这段时间没有审核动作"。 8) 菜单显示名「操作日志」→「日志」(key 保持 'log' 不动:后端 BuiltinRole 的菜单键与 check-wiring 第 ⑥ 项都按它核对,改 key 会让平台角色的菜单静默少一项)。 同步 frontend-pc/AGENTS.md 与 .agents/:「最容易写错的约定」由 7 条扩到 **9 条** —— 新增 第 8 条「详情用抽屉、不要为详情新开路由」(含"列表接口可能刻意不返回某个字段"的坑)与 第 9 条「页面内的页签用工具条分段按钮」(写明高度链的原因)。 验证:pnpm typecheck **0 错**、pnpm lint **0 错 0 警告**、pnpm build 通过。 浏览器实测(平台账号,5173 + 8080):租户页出现操作列且三档到期配色正确、卡片上内边距仍是 16px、 高度链未破(表格 570px / 日志表 528px);详情抽屉显示脱敏电话 138****8000 —— 而库里存的是 **历史明文** 13800138000,也就是说这条同时验了"读路径容错"(否则是永久 500);停用二次确认文案 正确;清空单位名称提交后弹窗不关且错误可见;登录日志同一张表里同时出现「示范职业学校」 (tenant_id=1)与「平台」(tenant_id=0)两种映射;**学校管理员视角两张表都没有「单位」列**。
85 lines
5.6 KiB
Markdown
85 lines
5.6 KiB
Markdown
# 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 页。
|