feat(pc,platform): 验收走查修复(平台按学校代管 + 菜单/403/日志范围/分页)
客户在验收里点出来的问题,一次改完。两段改动:
一、平台运营与租户范围(后端为主)
- 新增请求头 X-Tenant-Id:平台运营是唯一跨租户身份,租户拦截器对它整体关掉了过滤,
于是「组织架构 / 账号管理 / 工作台」拿到的是所有租户混在一起的数据 —— 而只有一所
学校有数据时,它和学校管理员看到的完全一样,看不出错(这是最危险的一点)。
现在这些页面必须先选定学校,服务端再把本次请求的租户上下文换成目标租户:
只认平台身份(其它身份带头一律忽略)、目标租户必须存在且启用。
- OrgService.tree / UserService.list 补显式 tenant_id 条件(原来只靠拦截器,而拦截器对
平台不生效);TaskService.seesWholeTenant 改用新的 LoginUser.isPlatformStaff() ——
isPlatformAdmin 回答"当前租户是不是平台租户",代管时它为 false,会让平台退化成普通
用户(任务列表掉进"班主任只看自己带的班"分支,得到空列表);PLATFORM_ADMIN 补
TASK_VIEW(PRD §3.2「查看填报进度看板:平台运营 ✅」)。
- 操作日志对平台永远是空的:SQL 写死 tenant_id = 当前租户,而平台的 tenantId 是 0。
按契约 §13.4 补 tenantId 参数与 log:viewAll:平台默认看全量、可指定某所学校;
学校管理员恒为本租户,传别家返回 40301(不静默忽略,否则会让人以为"筛过了")。
- 开放接口"推送重试"从未生效(F-112):定时扫描缺 @InterceptorIgnore,异常被 log.warn
吞掉;同一方法里"接入方已停用"那条 update 也没有租户上下文,会每 10 秒重试到永远。
E2E 补了"真的重试了"的断言(原来只断言"安排了下次重试")。
二、PC 端走查体验
- 去掉 3 条无用菜单(名单管理 / 表单设计器 / 材料规则):功能都在「收集任务」行内入口,
菜单里那一条点进去只有指路;后端菜单键同步删除,entryAt 那套机制一并删掉。
- 班主任点工作台必弹 403:工作台无条件调了要 org:view / progress:viewAll 的两个接口。
403 走 HTTP 状态码,try/catch 拦不住它的弹窗 —— 改成按权限决定发不发请求。
- 操作日志:分页器被裁在可视区外(新增 .g-list-card 让表体自己滚、表头钉住、分页器留在
卡片底部)+ 去掉页面上那段免责声明;收集任务的状态切换改成单独一行 tab;去掉顶栏假数据
铃铛;配色按客户要求还原。
顺带修两处"检查器自己不严":check-wiring 新增第 ⑦ 项(PC 端引用的权限码必须存在于
PermissionDef —— 本轮我自己把 progress:viewAll 写成 progress:view:all,判断恒为 false,
而编译 / typecheck / lint / E2E 全都不红);verify-docs 的表格列数检查不再把 GFM 转义
竖线当成列分隔符。两个检查器都拿"故意写错的样例"试过,确认真的会红。
验证:typecheck / mvn package / check-wiring(未接线声明 0 条)/ verify-docs(22 项)全过;
e2e-m0 36、e2e-m1-orgs 48、e2e-m0-user 55、e2e-m5-logs 44、e2e-m5-notify-wiring 43、
e2e-m1-tenant-domain 6、e2e-m8-open 61 全绿;浏览器按角色逐个真点复验。
详见 docs/交接摘要.md §一·三十三 与 §一·三十四。
This commit is contained in:
+252
@@ -2527,3 +2527,255 @@ CI/CD 与 test/prod 环境 → 压测 → 用户手册与客服话术 → 缩略
|
||||
3. **降级逻辑越完善,接线类的错越安静**:F-109 的 NPE 被 try/catch 吞掉、
|
||||
F-104 的沉默被 `if (!tasks.isEmpty())` 挡住、F-106 的错只在"点菜单"时才出现 ——
|
||||
所以"从入口走一遍"这件事只能靠 E2E,不能靠 review。
|
||||
|
||||
---
|
||||
|
||||
## §一·三十三 验收走查后的三项修正 + 平台「选择学校」(2026-10-09)
|
||||
|
||||
> 起点是客户自己点着看了一圈,报了三件事:**菜单里有几条没用**、
|
||||
> **班主任点工作台弹 403**、**平台运营与学校管理员看到的组织架构一模一样**。
|
||||
> 三件事都不是"功能没做",而是"做完了但口径不对" —— 与前几轮同一族。
|
||||
|
||||
### 一、去掉 3 条无用菜单(客户明确要求)
|
||||
|
||||
| 菜单 | 为什么去掉 |
|
||||
|---|---|
|
||||
| 名单管理 | 名册按任务,入口在「收集任务」每行的「名单管理」 |
|
||||
| 表单设计器 | 表单按任务,入口在任务行的「表单设计」 |
|
||||
| 材料规则 | 材料按任务,入口在任务行的「材料规则」 |
|
||||
|
||||
⚠️ **这一条推翻了 §一·二十三 里"顶层菜单保留「待建」页更诚实"的取舍** ——
|
||||
当时的理由是"说'还没做'而功能在隔壁会让人得出错误结论"。客户看了之后的判断是:
|
||||
一条点进去只有指路、没有任何操作的菜单,**本身就是导航噪音**。于是直接删掉。
|
||||
|
||||
- 后端 `BuiltinRole` 的 `menus` 同步删除这三个键(`check-wiring.py` ⑤ 双向核对,
|
||||
只删一边会红)。⚠️ **权限码一个都没动**:`task:manage` / `form:manage` /
|
||||
`material:manage` / `roster:*` 照旧,删的只是菜单键(可见性)。
|
||||
- PC 端 `XT_MENUS` 删掉三条;**顺带删掉 `entryAt` 机制**("功能已就绪 + 跳转按钮"
|
||||
那段)—— 它当初就是为这三条写的,留着就是一段永远走不到的代码。
|
||||
「待建」页现在只服务「角色与授权」(二期)。
|
||||
- 班主任/副班主任/审核员的菜单也一起去掉了 `roster`:他们本来也点不进去(没有 `roster` 页)。
|
||||
|
||||
### 二、班主任点工作台必弹 403 —— 前端调了"自己没有权限"的接口
|
||||
|
||||
| 接口 | 要求权限 | 班主任有吗 |
|
||||
|---|---|---|
|
||||
| `GET /orgs/tree`(工作台的"组织节点"卡片) | `org:view` | ❌ |
|
||||
| `GET /audit/statistics`(数据看板的"待审/审核情况") | `progress:view:all` | ❌(只有 `progress:view:own`)|
|
||||
|
||||
工作台是**所有角色共用的落地页**,而这两个调用是无条件的 —— 于是班主任一进首页就连吃两个 403。
|
||||
|
||||
⚠️ **`try/catch` 拦不住它**:403 走的是 HTTP 状态码,`utils/http.ts` 的传输层分支
|
||||
**先 `Message.error('拒绝访问(403)')` 再 reject**,异常被 catch 了、提示仍然弹出来。
|
||||
前端唯一正确的做法是**按权限决定发不发这个请求**:
|
||||
|
||||
- 卡片值如实显示 `—`,并把原因写在卡片脚注("需要「组织查看」权限");
|
||||
- 「审核情况」对没有 `progress:view:all` 的角色显示一句说明,而不是一张空表。
|
||||
|
||||
> 教训(与 §一·三十二 的接线检查同一族):**"共用页面 + 接口级权限"是一对必踩的组合**。
|
||||
> 页面对所有角色都渲染,但里面的接口各要各的权限 —— 这类错只有"换个角色登进去点一遍"
|
||||
> 才看得见,E2E 全绿也抓不到(E2E 一直用管理员身份跑工作台)。
|
||||
|
||||
### 三、平台运营的「组织架构 / 账号管理」:新增 `X-Tenant-Id`
|
||||
|
||||
**根因**:平台运营(`tenantId = 0`)是唯一跨租户身份,租户拦截器对它
|
||||
**整体关闭过滤**(`MybatisPlusConfig.FillTenantLineHandler.ignoreTable`)。
|
||||
于是 `/orgs/tree` 返回的是**所有租户混在一棵树里**,`/users` 是**所有租户的账号**。
|
||||
⚠️ 这**看不出错**:当时只有 demo 一所学校有数据,平台看到的正好就是那 22 个节点 ——
|
||||
和学校管理员一模一样。等第二所学校建了组织,它会看到两所学校的班级混在同一层。
|
||||
|
||||
**做法(客户选定"先选学校,再看该校数据")**:
|
||||
|
||||
```
|
||||
前端:平台运营的「组织架构 / 账号管理」页顶部多一个「选择学校」下拉(GiTenantPicker)
|
||||
→ 选中后所有请求带 X-Tenant-Id: <tenantId>
|
||||
后端:ActingTenantResolver 解析这个头(support/,与 TenantDomainResolver 并列)
|
||||
→ AuthInterceptor 把 LoginUser 的 tenantId 换成目标租户
|
||||
→ isPlatformAdmin() 随之变 false → 租户过滤自动生效(这是机制,不是副作用)
|
||||
```
|
||||
|
||||
**四条必须守住的**(都写了断言,`scripts/_verify-fixes.py`):
|
||||
|
||||
1. **只认平台身份**:学校管理员/班主任带这个头一律忽略 —— 否则改一个数字就能越租户;
|
||||
2. 目标租户必须存在且启用,否则 `40001`(不区分"不存在"与"停用");
|
||||
3. 不带这个头时**平台行为完全不变**(租户列表、全量操作日志照旧跨租户);
|
||||
4. `OrgService.tree()` 与 `UserService.list()` 补了**显式** `tenant_id` 条件 ——
|
||||
原来它们只靠拦截器,而拦截器对平台是不生效的;显式写死之后
|
||||
"平台没选学校"得到的是**空树/自己的账号**,而不是"所有人的数据"。
|
||||
|
||||
⚠️ 角色可选范围也跟着走:给某所学校建账号时只能发学校那套角色
|
||||
(发 `PLATFORM_ADMIN` 会被服务端的租户白名单拒掉,报 40301)。
|
||||
|
||||
### 四、`OpenPushService` 的"推送重试"从来没重试过(**已修,F-112**)
|
||||
|
||||
后端每 10 秒刷一次堆栈:`OpenPushService 扫描推送重试失败 → 未登录或登录已失效`。
|
||||
|
||||
- 根因:`OpenPushService.retryDue()` 的扫描查询 `pushLogMapper.dueRetries()`
|
||||
**没有租户上下文**,而该方法**漏了 `@InterceptorIgnore(tenantLine="true")`** ——
|
||||
同一个文件里 `findBySubmissionIgnoreTenant` 有,注释还正好写着这条规律
|
||||
("先找归属、再摆上下文"里找归属那一步必须能无租户执行)。
|
||||
- 后果:`docs/08` 记的**第 8 项定时任务「开放接口推送重试」从未真正重试**,
|
||||
只是每 10 秒打一行 warn(异常被 `catch (RuntimeException) → log.warn` 吞掉)。
|
||||
- **顺带修掉同一个方法里的第二处**:`接入方已删除/停用` 那条分支直接
|
||||
`pushLogMapper.updateById(row)`,它同样没有租户上下文 —— 少了上下文这句 update 会抛 40101
|
||||
被同一个 catch 吞掉,于是那条记录**每 10 秒重试一次、永远停不下来**。
|
||||
改成用**推送记录自己的 `tenant_id`** 摆上下文再写。
|
||||
- **E2E 补了"真的发生了"的断言**:`e2e-m8-open.py` 原来只断言"失败后**安排了**下次重试"
|
||||
(`next_retry_at` 被写上),现在多一条 —— 等 16 秒(扫描间隔 10s + 首次退避 1s + 余量),
|
||||
断言 `attempts` **真的增加**。**"安排了"与"发生了"是两件事**,只断言前者的话,
|
||||
60/60 全绿也照样漏掉这个 bug(F-104 是同一族的第二次)。
|
||||
修后 `e2e-m8-open.py` → **61/61**。
|
||||
|
||||
### 五、平台运营的**工作台**也要"选择学校"
|
||||
|
||||
客户要求"工作台的数据看板也按学校看"。改完之后连着暴露了两个**更深的问题**:
|
||||
|
||||
| # | 现象 | 根因 | 修法 |
|
||||
|:--:|---|---|---|
|
||||
| 1 | 平台选了学校后,任务下拉**空**(`/tasks` 40301) | 切到代管租户后 `isPlatformAdmin()` 变 false,权限判定**只认平台角色声明的权限** —— 而 `PLATFORM_ADMIN` 里**没有 `TASK_VIEW`**。它以前被"平台=超管"(`PermissionChecker.has` 对平台恒真)这个捷径掩盖着 | 给 `PLATFORM_ADMIN` 补 **`TASK_VIEW`**(纯读:任务列表/详情/统计)。PRD §3.2「查看填报进度看板:平台运营 ✅」正是这件事;`TASK_MANAGE` / `TASK_CLONE` 依旧不给(PRD 里平台是「—」) |
|
||||
| 2 | 补了权限后 `/tasks` 返回 200,但**还是 0 条** | `TaskService.seesWholeTenant()` 写的是 `isPlatformAdmin() \|\| SCHOOL_ADMIN/AUDITOR` —— 代管时前者为 false,于是**掉进"班主任只看自己带的班"那条分支**,而平台在该校没有班级 → 空列表(看起来像"这所学校没有任务") | 新增 `LoginUser.isPlatformStaff()`(= `isPlatformAdmin() \|\| hasRole("PLATFORM_ADMIN")`),并把这条判据换成它 |
|
||||
|
||||
⚠️ **`isPlatformAdmin()` 与 `isPlatformStaff()` 的区别要记住**(两者回答不同的问题):
|
||||
|
||||
- `isPlatformAdmin()` = "当前上下文的租户**就是平台租户**" → 它是租户拦截器要不要
|
||||
**整体关掉过滤**的开关(跨租户看数据);
|
||||
- `isPlatformStaff()` = "这个人**是平台运营**" → 数据范围是全量,但**租户过滤照常生效**。
|
||||
|
||||
**凡是"按身份决定数据范围"的判断用后者**;否则平台一代管某校就会**退化成普通用户**
|
||||
(空列表、少一半数据,而界面上没有任何提示)。前端也一并按学校取:工作台顶部同样放
|
||||
`GiTenantPicker`(与组织架构/账号管理共用 `useActingTenantStore`),任务列表、任务统计、
|
||||
审核统计都带上 `X-Tenant-Id`。
|
||||
|
||||
### 六、⭐ 把"权限码写错"变成命令:`check-wiring.py` 第 ⑦ 项
|
||||
|
||||
本轮我自己踩了一个**最该被工具抓住**的错:给工作台加"按权限决定发不发请求"时,
|
||||
把 `progress:viewAll` 写成了 **`progress:view:all`**(多一个冒号)。后果:
|
||||
那个判断**恒为 false** →「审核情况」整块**对所有人**都不显示 ——
|
||||
而编译、typecheck、lint、后端日志、E2E **全都不红**(字符串对不上是纯运行期的事;
|
||||
"权限不足就少显示一块"看起来还很像设计)。
|
||||
|
||||
所以给 `check-wiring.py` 加了第 ⑦ 项:**PC 端引用的权限码必须存在于 `PermissionDef`**
|
||||
(认两种写法:`permissions.includes('x')` 与 `v-hasPerm="['x']"`)。
|
||||
|
||||
两个"检查器自己"的细节值得记:
|
||||
|
||||
1. **它第一版报了 5 条假阳性** —— 全都来自 `directives/hasPerm.ts` 里的
|
||||
**注释与示例字符串**(模板自带的 `home:btn:add`)。这与本脚本 2026-10-08 修过的
|
||||
第 ③ 条(Java 注释里的提及被当成接线)是同一个教训:
|
||||
**检查器必须能区分"代码"与"解释代码的文字"**。修法:先按引号感知地剥掉 JS 注释
|
||||
(`strip_js_comments`,不能见 `//` 就砍),并且 `v-hasPerm` **只在 `.vue` 里找**
|
||||
(那是模板语法,`.ts` 里的同名文本必然是示例或报错文案)。
|
||||
2. **加完要拿一个故意写错的码试一次**(本仓库的老规矩):把 `progress:viewAll` 改回错的
|
||||
→ 检查器红、指向 `dashboard/index.vue:196`;改回来 → 0 条。**它真的会红,才算加上。**
|
||||
|
||||
### 七、平台运营的「操作日志」永远是空的(**已修**)
|
||||
|
||||
客户点出来的现象:**平台运营进「操作日志」一条都没有**(页面正常、不报错、就是空表)。
|
||||
|
||||
- 根因:`LogController` 恒把 `me.tenantId()` 传给查询,SQL 又写死 `WHERE l.tenant_id = #{tenantId}` ——
|
||||
而平台运营的 tenantId 是 **0**,日志却都挂在各租户下 → **恒为 0 条**。
|
||||
- 而契约 §13.4 **本来就把 `tenantId` 定义成查询参数**,PRD §3.2 对平台运营要求的也是
|
||||
「**全量**操作日志 ✅」(学校管理员才是「🔸 本租户」)。也就是说:**参数没实现、权限码只挂了一半**。
|
||||
- 修法(两个接口一起:`/logs/operations` 与 `/logs/logins`):
|
||||
1. 注解改成 **`@RequirePerm({LOG_VIEW_TENANT, LOG_VIEW_ALL})`**(任一即可);
|
||||
2. 新增 `LogController#logScopeTenantId(tenantId)` —— 有 `log:viewAll` → **默认全量**、
|
||||
显式传 `tenantId` 时只看那一所;只有 `log:viewTenant` → **恒为本租户**,
|
||||
传别人的租户 **`40301`**(⚠️ **不静默忽略**:这一屏是拿去回答合规问题的,
|
||||
"看起来筛过了"比报错更糟);
|
||||
3. 两条手写 SQL 的租户条件改成可选(`WHERE 1 = 1` + `<if test="tenantId != null">`)。
|
||||
- 前端:平台运营多一列「**租户**」(它看的是跨租户全量,不显示单位就分不清这条属于谁;
|
||||
学校管理员永远是本租户,不显示这一列),单位名取自 `/tenants`,取不到就退回 `#id`。
|
||||
- 按客户要求,**同时删掉了这一屏顶部那段说明**(脱敏值/指纹、IP 可伪造、真正的证据是…)——
|
||||
那段话本身没错,但它是"给审查者看的免责声明",挂在日常操作界面上属于噪音。
|
||||
⚠️ 结论性的知识(IP 可伪造、登录日志一期只有 PC)**保留在 `apis/xt/log.ts` 的注释与契约里**,
|
||||
不是删掉了事。
|
||||
|
||||
### 八、验证
|
||||
|
||||
| 手段 | 结果 |
|
||||
|---|---|
|
||||
| `scripts/_verify-fixes.py`(本轮临时脚本,**34 条断言**) | 全过。含 4 条安全边界(非平台带头不生效、非法值 40001、`0` 视为不切换、平台跨租户能力不变)、4 条**写路径**断言(平台代管新建组织落在租户 1、不带头落在租户 0、代管时建班主任账号绑的是**租户 1 的角色行**、代管时发 `PLATFORM_ADMIN` 被 40301 拒)、**8 条日志范围断言**(平台不带头=全量 1250 条、有行、每条带 tenantId、`tenantId=159`→0 条;学校管理员=本租户、指定别家 → 40301;登录日志同口径) |
|
||||
| `scripts/_check-platform-tasks.py` | 平台不带头的任务列表 31 条(跨租户);带 `X-Tenant-Id: 1` → 31 条(该校的);带 `159`(没有任务的租户)→ **0 条** → 头真的在按租户收窄 |
|
||||
| 浏览器(真点,非"看代码") | ① 班主任工作台:**不再弹 403**,组织节点/待审显示 `—` 并写明原因,左侧菜单只剩「收集任务 / 审核」;② 学校管理员菜单:名单管理/表单设计器/材料规则三条消失,其余不变;③ 平台「组织架构」:未选学校时提示"请先选择学校",选定 demo 后拿到该校的组织树;切到「账号管理」**选择被保留**(共用 store);④ 平台**工作台**选定学校后:组织节点、任务下拉、待审 **3**、审核情况表出数(改前是空/`—`);⑤ 学校管理员工作台复验:照旧显示(说明权限码那个错**对所有人**的影响都已修回);⑥ 平台「操作日志」:**出数**、多一列「租户」(显示"示范职业学校")、顶部那段说明**已消失**;学校管理员同一页:只有本租户、**没有**「租户」列、说明同样已消失 |
|
||||
| `pnpm typecheck` | 通过 |
|
||||
| `mvn -DskipTests package` | 通过(jar 已重启) |
|
||||
| E2E 回归 | `e2e-m0` 36/36(含 A8/A9/A10 菜单断言)、`e2e-m1-orgs` 48/48、`e2e-m0-user` 55/55、`e2e-m5-notify-wiring` 43/43(dict/notify 菜单仍在)、**`e2e-m5-logs` 44/44**(日志接口本尊)、`e2e-m1-tenant-domain` 6/6(就在我改动的 `AuthInterceptor` 旁边)、`e2e-m8-open` **61/61**(含新增的"真的重试了"断言) |
|
||||
| `check-wiring.py` / `verify-docs.py` | 未接线声明 **0 条**(第 ⑦ 项:PC 端 7 个权限码全部命中 `PermissionDef`);文档一致性 22 项全过 |
|
||||
|
||||
> ⚠️ 三条**自己踩出来的**教训(都属于"看起来正常,其实不生效"):
|
||||
> ① 把"平台看到 22 个节点""租户 5 个"**写死**在验证脚本里 —— 跑完 E2E 之后它自己变红了
|
||||
> (E2E 会往 demo 租户里加组织、加租户),而**代码一行没改**。改成从库里算期望值(F-110 同族)。
|
||||
> ② 「选择了学校」必须放在 store 而不是各页面 `ref`:否则切页面要重选,
|
||||
> 更糟的是**两个页面各看一所学校**,看起来像数据不一致。
|
||||
> ③ **平台的"超管"捷径会掩盖权限缺口**:只要平台还在自己的租户上下文里,
|
||||
> `PermissionChecker` 对它恒真,缺哪个权限码都看不出来;一旦开始代管某所学校,
|
||||
> 缺口立刻变成 403 或空列表。**要验平台,就得在"代管态"下验一遍。**
|
||||
> ④ 同一族的第四例:**"按租户写死"的接口,对平台就是"永远查不到"**(本轮的操作日志)。
|
||||
> 平台运营的 tenantId 是 0,任何 `WHERE tenant_id = 当前租户` 都会静默返回空。
|
||||
> 已在 `OrgService.tree` / `UserService.list` / `TaskService.seesWholeTenant` / `LogController` 四处统一处理;
|
||||
> **下次新增"租户内"的查询接口时,先问一句:平台运营调它会返回什么?**
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## §一·三十四 PC 端四项走查修复(2026-10-09 晚)
|
||||
|
||||
> 客户在验收里又点了 4 件事,三件已改;**第 3 件(配色)改了之后客户要求整体还原** ——
|
||||
> 第四节专门记了这条"不要再动",免得下一个会话好心重做一遍。
|
||||
|
||||
### 一、平台运维「操作日志」:去掉用户 ID + 分页器根本看不见
|
||||
|
||||
| 现象 | 根因 | 修法 |
|
||||
|---|---|---|
|
||||
| 「操作人」列是 `张校长 / ID 2` 两行 | 数字 ID 对看日志的人没有意义(要 ID 的时候查库更快) | 只留账号/姓名(`operatorName`);接口照旧返回 `operatorId`,只是不显示 |
|
||||
| **分页器"没有正确展示"** | 它**在 DOM 里一直是好的**(`共 1245 条 … 20 条/页`),但被裁在可视区之外:`GiPageLayout` 的内容区是 `overflow: hidden`(页面本身不滚),而「一页 20 条 + 工具栏 + 提示条」比屏幕高 → 卡片连着分页器一起被切掉,怎么滚都看不到 | 新增全局类 **`.g-list-card`**(`styles/global.scss`):卡片撑满内容区 → 表体自己滚 → 分页器钉在卡片底部;表格改 `:scroll="{ x, y: '100%' }"` |
|
||||
| 「N 条/页」改了以后数字对不上 | Arco 表格在 `pagination` 非 false 时**自己也会再切一次数据**(内部 `flattenData` 会 `slice`),只改本地每页条数、不重新取数,就会得到"分页器说 50 条/页、表里还是 20 条" | 补 `@page-size-change` → 回到第 1 页重新取数 |
|
||||
|
||||
⚠️ `y: '100%'` 不是可选项:**字符串**形式的 `y` 才会让 Arco 把表头与表体**拆开**
|
||||
(内部 `splitTable`,表头钉住、表体自己滚)。不写它时 `.arco-table-content` 的
|
||||
`overflow-y` 是 `hidden`(那是给横向滚动条留的),多出来的行会被**直接裁掉且无法滚动**。
|
||||
|
||||
### 二、收集任务:状态切换改成单独一行的 tab
|
||||
|
||||
原来「全部 / 草稿 / …」是和「新建任务 / 批次号 / 搜索」挤在同一行的一排按钮 ——
|
||||
一排长得一样的按钮分不清"哪个是当前视图、哪个是动作"。
|
||||
现在 `<a-tabs hide-content>` 单独占一行,数据源仍是 `statusFilter`
|
||||
(tab 的 key ↔ status 的映射只在 `STATUS_TABS` 一处定义,别再写第二份)。
|
||||
|
||||
⚠️ **加了这一行之后,任务页也踩了同一个分页器坑**(卡片变高 → 分页器被顶出可视区):
|
||||
同一处 `.g-list-card` 一并修掉。**改这类"往卡片里加东西"的活,先看分页器还在不在视野里。**
|
||||
|
||||
### 三、通知铃铛:按客户要求先去掉
|
||||
|
||||
删掉顶栏铃铛与 `HeaderRightBar/Notice.vue`(那是 Gi 模板的假数据:写死 9 条、点了没反应,
|
||||
和站内信没有任何关系)。将来要做,数据源是「通知记录」(`/collect/notify`,接口与页面都已就绪),
|
||||
**不要恢复成写死的假列表**。
|
||||
|
||||
### 四、配色:改过一版腾讯云深蓝外壳,**客户要求整体还原**(不要再改)
|
||||
|
||||
本轮先按"左侧菜单栏 + 顶栏用腾讯云控制台深蓝、不要纯白"改了一版
|
||||
(新增 `--xt-shell-*` 变量、`.g-shell`、菜单恒定 `theme="dark"`、并删掉设置抽屉里的「深色菜单」开关),
|
||||
**客户看到后明确要求还原配色**,已按文件 `git checkout` 回原样:外壳仍是纯白,
|
||||
`isMenuDark` / `.app-menu-dark` / 「深色菜单」开关**都保持原样**。
|
||||
|
||||
> **不要再"顺手"改配色。** 要改先问。
|
||||
|
||||
### 五、这一轮踩到的两个坑(都值得记)
|
||||
|
||||
1. **HMR 下 `Drawer.open({ content: () => h(SettingDrawerPanel) })` 不会热更新**:
|
||||
该组件只出现在闭包里、不在任何模板里 —— 改了它,页面看起来毫无变化,
|
||||
很容易把"没生效"误判成"改错了/改不动"。
|
||||
**验证前先真重载**(`about:blank` → 目标 URL;同 URL 再 open 不一定会重新拉模块)。
|
||||
2. **演示账号不是 `frontend-pc/README.md` 里那个**:那里的 `admin / 123456` 是 Gi 模板的旧值,
|
||||
本项目是 **`admin / Admin@123` + 租户码 `demo`**
|
||||
(`python scripts/dev-login.py` 用的就是它)。账号在多个单位下重名时,
|
||||
登录页才会出现「租户码」输入框(服务端 `40012`)。
|
||||
|
||||
### 六、未做(同一族的问题,留给下一轮)
|
||||
|
||||
「通知记录」与「账号管理」两页的分页器**会被同样地裁掉**
|
||||
(实测分页器顶边 1612 / 886,视口高 900)。修法是给这两页的卡片加同一个 `.g-list-card`
|
||||
并把表格改成 `y: '100%'`,本轮没动(客户只报了操作日志那一处)。
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user