docs(xispeak): 归档前端页面 QA 审查报告并忽略 qa-tmp 临时产物

- qa-report/xispeak/QA_REPORT.md 提交入库(P0×1 / P1×4 / P2×5)
- .gitignore 忽略 qa-tmp/、__pycache__/、*.pyc,测试临时产物不入库

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
wsm
2026-08-07 15:16:39 +08:00
co-authored by Claude Opus 4.7
parent 04d25adfff
commit b5d3032f0c
2 changed files with 120 additions and 0 deletions
+5
View File
@@ -6,6 +6,11 @@ dist-electron
.cache
localDoc
# QA test temporary artifacts (scripts, screenshots, caches) — reports live in qa-report/ and are committed
qa-tmp/
__pycache__/
*.pyc
tests/server/static
tests/server/static/upload
+115
View File
@@ -0,0 +1,115 @@
# 前端页面审查报告 — 习总书记重要讲话指示批示(xispeak)
## 审查信息
- 页面/路由:`/bg/xispeak/bgXiSpeakList`(组件 `src/views/bg/xispeak/BgXiSpeakList.vue`
- 审查日期:2026-08-07
- 审查方式:frontend-page-qa skill 四阶段流程(代码静态审查 + Playwright 浏览器实测 + checklist 系统化走查)
- 环境:dev 端口 3100(mock 开启),后端 `http://localhost:8080/jeecg-boot`,账号 admin/123456
- 相关文件:
- `src/views/bg/xispeak/BgXiSpeakList.vue`(树形列表 + 查询 + 流程操作)
- `src/views/bg/xispeak/BgXiSpeak.data.ts`(列定义 + 高级查询)
- `src/views/bg/xispeak/BgXiSpeak.api.ts`(接口层)
- `src/views/bg/xispeak/components/BgXiSpeakForm.vue`(新增/编辑表单)
- `src/views/bg/xispeak/components/BgXiSpeakFeedbackForm.vue`(反馈子表单)
- `src/views/bg/xispeak/components/BgXiSpeakBPMForm.vue`(流程表单)
- `src/views/bg/xispeak/components/BgXiSpeakFeedbackViewModal.vue`(查看办理情况)
- `src/views/bg/xispeak/components/BgXiSpeakFeedbackList.vue`(孤儿组件)
- 后端 `backend/.../xispeak/controller/BgXiSpeakController.java`
## 总体结论
- **P0 × 1 P1 × 4 P2 × 5**
- 一句话总结:核心 CRUD(新增→编辑→详情→删除)、查看办理情况、批量删除、导出均可用且无 console 错误;但存在 1 个功能性 P0(**添加下级无法建立树层级,子记录退化为根记录**)与 4 个 P1(**占位文本写入业务字段、多部门筛选语义错误、JBR 批量流程部分失败状态丢失、编辑反馈覆盖部门**),不建议直接交付。
---
## P0 - 必须修复
| # | 维度 | 问题描述 | 证据 | 位置 |
|---|------|---------|------|------|
| 1 | 逻辑/操作顺序 | **"添加下级"无法创建子节点,树形层级功能整体失效**。"添加下级"把 `{ pid: parentId }` 传入表单,但 `edit()` 对无 id 的新记录直接重置 formData 并丢弃 pid,子记录以 `pid=''` 保存(后端落库为 `pid="0"`),变成平铺根记录;父节点永远不出现展开图标,无法构建层级。 | 实测 `probe_pid.py`:添加下级保存时 add 请求体 `pid=""``probe_tree.py`rootList 返回子记录 `"pid":"0"`,父节点刷新前后均无展开图标,子行无缩进 | `BgXiSpeakForm.vue:255-262``edit()` 重置丢弃 pid)· 触发 `BgXiSpeakList.vue:543-546` / `:633` |
---
## P1 - 强烈建议
| # | 维度 | 问题描述 | 证据 | 位置 |
|---|------|---------|------|------|
| 1 | 逻辑/表单 | **占位模板文本被硬编码为表单默认值并写入业务字段**。新增弹窗打开时"企业负责同志批示情况"已填 `经单位主要负责同时签批同意,由xx部做好贯彻落实`、"学习传达研究部署情况"已填 `x月x日召开xxxx年第x次党委会议`,提交时原样入库。用户不手动改就把占位假数据写进记录;同时这两个字段的必填校验形同虚设。 | 实测 `probe_defaults.py`:默认值截图 + 提交请求体 `elmComment=经单位主要负责同时签批同意…, status=x月x日召开xxxx年第x次党委会议`CRUD 回显列内容含占位文本 | `BgXiSpeakForm.vue:163-164` |
| 2 | 后端调用 | **牵头部门多选筛选为 AND 语义**。前端把多个部门 id 逗号拼接成 `implDept=A,B`,后端在循环里逐个 `queryWrapper.apply("FIND_IN_SET(...)")`,生成 `A AND B AND …` 条件——一条记录必须同时包含所有选中部门才命中,选 2+ 部门时几乎查不到数据。应为 OR。 | 代码路径 `BgXiSpeakController.java:94-102`(循环 apply);前端拼接 `BgXiSpeakList.vue:916-923` | `BgXiSpeakController.java:94-102` |
| 3 | 操作顺序 | **JBR 批量发起流程部分失败时状态不保存、列表不刷新**`successCount === total` 守卫放在循环内,任一部门流程发起失败则 `bpmStatus`/`supervisionCount` 不更新、不 reload,已成功的流程成为"孤儿流程"且无部分成功提示。 | `probe_flows.py` 未覆盖(需真实流程引擎);代码 `BgXiSpeakList.vue:740-793` | `BgXiSpeakList.vue:783-787` |
| 4 | 状态一致性 | **编辑反馈记录时部门被当前登录人部门无条件覆盖**`edit()` 加载记录后无条件调用 `fillDeptFromLogin()`,把 `responDeptid/responDeptname` 覆盖为登录人部门;跨部门编辑/审批他人的反馈并保存会造成部门归属数据错乱。 | 代码 `BgXiSpeakFeedbackForm.vue:294` + `230-249` | `BgXiSpeakFeedbackForm.vue:294` |
---
## P2 - 建议
| # | 维度 | 问题描述 | 证据 | 位置 |
|---|------|---------|------|------|
| 1 | 性能/体验 | **搜索无防抖,逐字触发 rootList**。JInput `@change="searchQuery"` 每个按键发一次查询;实测输入 6 个字符新增 6 次 rootList 请求。 | `probe_flows.py`:输入前 rootList REQ=26 字符后=8 | `BgXiSpeakList.vue:12` + `:900-902` |
| 2 | 安全/整洁 | **流程表单 14 处 console.log 调试输出**,含 `@props`、流程变量、部门过滤中间结果等,生产环境泄漏调试信息。 | 代码 `BgXiSpeakBPMForm.vue:311,312,596,603,607,612,618,624,627,659,662,664,671,686` | `BgXiSpeakBPMForm.vue`14 处) |
| 3 | 逻辑 | **反馈表单日期处理死代码 + 调试日志**`value.format('YYYY-MM-DD HH:mm:ss')` 对 Date 对象调用 `.format()` 会抛 TypeError(当前 axios 返回字符串未触发,属潜在崩溃点);另有 3 处 console.log。 | `BgXiSpeakFeedbackForm.vue:281-289``value.format`);`:238,243,248` | `BgXiSpeakFeedbackForm.vue:281-289` |
| 4 | 整洁 | **List.vue 死代码**:未使用 import `JDictSelectTag`、未使用 computed `showSdwLeaderSelect`、未使用函数 `handleFormJoinChange`、line 142 import 缩进错乱。 | 全文件 grep 仅定义处出现 | `BgXiSpeakList.vue:148,202,937,142` |
| 5 | 整洁 | **孤儿组件** `BgXiSpeakFeedbackList.vue` 未被任何源码引用;`searchReset()` 调用 `formRef.value.resetFields()``formRef` 未定义,一旦被挂载使用即崩溃。 | `BgXiSpeakFeedbackList.vue:141`;全局 grep 仅 checklist 文档引用 | `BgXiSpeakFeedbackList.vue:141` |
---
## 通过项(做得好的地方)
- **CRUD 全链路正常**:新增→保存→列表回显→编辑→详情→删除全部跑通,数据一致,无 console 错误。
- **空表单校验**:9 项必填错误准确提示并指向字段,提交前整体校验 + `scrollToField` 聚焦。
- **危险操作二次确认**:单条删除 Popconfirm(含"同时删除关联流程"说明文案)、批量删除 Modal.confirm,文案清晰。
- **查看办理情况**:动态标题"反馈信息(共 N 条,已完成 N 条)",空态显示"暂无数据",接口正常。
- **批量删除**:勾选→批量操作→确认→删除成功,选中后按 ids 传递。
- **导出**`exportXls` 请求正常发出并返回 200。
- **接口契约**add/edit/delete/rootList/statusStats/withDrawXiSpeak/queryFeedbackCounts 方法、URL、参数实测全部正确。
- **权限控制**:新增/导出/模板校验/模板导入等按钮均有 `v-auth` 指令;导出走配置 URL。
- **列表列宽**:长文本列均有 `ellipsis`,操作列 `fixed: right` 不换行。
- **空响应兜底**`bgXiSpeakFeedbackList` 无 mainId 时 `Promise.resolve({})` 兜底,不崩溃(`BgXiSpeak.api.ts:117-122`)。
---
## 未覆盖项(需人工复核或真实环境验证)
- **流程发起**(发起落实措施收集流程 / 发起经办人执行反馈流程):需要真实 Flowable 引擎 + 部门审批人数据,本次未实测。JBR 部分失败场景据此推断代码逻辑,建议真实环境复测。
- **树展开真实数据**:DB 中无既有树形数据,且 P0(添加下级)导致无法生成层级,树展开仅验证到"无展开图标"。
- **导出文件内容**:headless 下导出经新窗口触发,仅确认请求 200,未核验 Excel 内容。
- **上传类按钮**(模板校验/模板导入/下载模板):未实测,依赖 `/upload` 代理(3300 端口本次未运行,产生 `ERR_PROXY_CONNECTION_FAILED`,与 xispeak 逻辑无关)。
- **多分辨率/暗黑主题**headless 固定 1440×900,视觉细节需人工复核。
- **长文本/特殊字符(含 XSS)注入、大数据量、断网异常**:未专项注入测试。
- **双账号权限/密级过滤对比**:仅使用 admin 单账号。
---
## 测试过程记录
### 操作步骤与截图
测试脚本与证据存于 `qa-tmp/xispeak/`(临时产物,gitignored;本报告为唯一提交的测试文档):
| 脚本 | 覆盖 | 截图/产物 |
|------|------|----------|
| `probe_crud2.py` | 空表单校验 → 全字段填充 → 保存 → 列表回显 → 编辑 → 详情 → 删除 | `crud_test2/shots/*.png` |
| `probe_flows.py` | 查看办理情况 → 添加下级 → 搜索逐字输入(竞态)→ 清理 | `flows_test/shots/*.png` |
| `probe_extra.py` | 父/子记录创建 → 展开 → 督办状态筛选 → 导出 | `extra_test/shots/*.png` |
| `probe_batch2.py` | 勾选 → 批量删除 → 导出请求捕获 | `batch_test2/shots/*.png` |
| `probe_tree.py` | 添加下级后展开图标(刷新前后)→ rootList hasChild 字段 | `tree_test/shots/*.png` |
| `probe_pid.py` | 添加下级 add 请求体 pid 值 | 输出见运行记录 |
| `probe_defaults.py` | 新增弹窗默认值 + 空表单校验 + 提交入参 | 输出见运行记录 |
| `probe_secret.py` / `probe_fill*.py` | 密级/责任领导/部门选择弹窗结构 | 输出见运行记录 |
### console 报错
- xispeak 业务操作全程 **0 个 error**(各脚本 console.json)。
- 唯一 error 为 `ERR_PROXY_CONNECTION_FAILED`:来自 `/upload` 代理(3300 端口 mock 服务未运行),与 xispeak 逻辑无关。
### 关键网络请求(probe_flows.py 汇总)
```
GET /jeecgboot/bg/xispeak/bgXiSpeak/statusStats 200
GET /jeecgboot/bg/xispeak/bgXiSpeak/rootList 200 (x10)
GET /jeecgboot/bg/xispeak/bgXiSpeak/loadTreeRoot 200 (x2)
GET /jeecgboot/bg/xispeak/bgXiSpeak/queryFeedbackCounts 200 (x4)
POST /jeecgboot/bg/xispeak/bgXiSpeak/add 200
GET /jeecgboot/bg/xispeak/bgXiSpeak/listBgXiSpeakFeedbackByMainId 200
POST /jeecgboot/bg/xispeak/bgXiSpeak/withDrawXiSpeak 200
DELETE /jeecgboot/bg/xispeak/bgXiSpeak/delete 200
GET /jeecgboot/bg/xispeak/bgXiSpeak/exportXls 200 (probe_batch2)
```
### 测试数据卫生
- 所有测试数据使用 `QA_TEST_` / `QA_` / `QA_BATCH` / `QA_DEFAULTS` / `QA_PARENT` / `QA_CHILD` 前缀,测试后均通过 UI 删除清理(各脚本末步"清理"均归零)。无残留测试数据。