* docs(xispeak): 归档前端页面 QA 审查报告(用户视角) * docs(fixcontact): 归档前端页面 QA 审查报告(用户视角) * docs(inspectTask): 归档前端页面 QA 审查报告(工程师+用户视角) * docs(bqtakepulse): 归档前端页面 QA 审查报告(工程师+用户视角) * docs(bgpartymatter): 归档前端页面 QA 审查报告(工程师+用户视角)
15 KiB
15 KiB
前端页面工程师视角审查报告 — 党委会/所务会事项(bgpartymatter)
审查信息
- 页面/路由:
/bg/bgpartymatter/BgPartymatterList(党委会台账,菜单「党委会决策事项」);所务会复用BgOfficematterList(meetingType=office,菜单「所务会决策事项」) - 审查日期:2026-08-11
- 审查视角:前端资深工程师(浏览器实测 + 代码审查结合,覆盖 API 契约、权限、状态一致性、表单校验、性能/异常等工程师专项维度)
- 相关文件:
src/views/bg/bgpartymatter/BgPartymatterList.vuesrc/views/bg/bgpartymatter/components/BgPartymatterForm.vuesrc/views/bg/bgpartymatter/components/BgPartymatterModal.vuesrc/views/bg/bgpartymatter/BgPartymatter.data.ts、BgPartymatter.api.ts- 后端:
backend/supervison-java/.../bgpartymatter/controller/BgPartymatterController.java、jeecg-boot-base-core/.../base/controller/JeecgController.java
- 测试账号:
admin(仅 1 个账号,多角色/多部门/多密级对比场景未覆盖,见「未覆盖项」) - 证据目录:
qa-tmp/bgpartymatter/scripts/*.json(eng_04~eng_09、02_modal、12_adddel_final、05_flow 等) - 配套文档:
qa-report/bgpartymatter/QA_REPORT_UX.md(用户视角报告,可并列对比) - 说明:全程无头浏览器实测(Playwright,dev 端口 3100,登录走 mock、业务接口走真实后端 8080),登录账号 admin/123456。
总体结论
- P0 × 1 | P1 × 2 | P2 × 7
- 一句话总结:党委会/所务会台账的搜索、详情、流程、新增、删除都能走通且状态一致,但导出功能 100% 不可用(后端稳定 500,前端静默无任何反馈)——这是唯一的交付阻塞项,已定位到 AutoPoi 对不可变集合执行
Iterator.remove()的根因;顶部统计标签不随筛选/增删改刷新、详情弹窗字段"看似可编辑实被透明遮罩拦截"是两大 P1 体验/一致性问题。
P0 - 必须修复
| # | 维度 | 问题描述 | 证据 | 位置 |
|---|---|---|---|---|
| 1 | 后端调用/功能 | 导出(导出 Excel)完全不可用:点击工具栏「导出」→ 无下载、无 toast、无任何反馈;后端 exportXls 稳定返回 500。党委会、所务会两页同一 exportConfig 与同一后端接口,均复现。根因(已定位):后端 JeecgController.exportXls 将 service.list(queryWrapper) 的查询结果直接作为数据集合传给 AutoPoi 导出;AutoPoi ExcelExportServer.createSheet() 第 198-202 行对数据集合迭代器执行 its.next(); its.remove(); 分块清理,当数据集合为不可变集合时 Iterator.remove() 抛 UnsupportedOperationException;第 213 行 catch 用 e.getCause() 包裹(该异常无 cause → null),把真实原因吞掉,统一抛成 ExcelExportException: Excel导出错误,响应 trace 无 "Caused by"(与实测一致)。实测链路:0 行导出=200,任意 ≥1 行导出=500 且与字段内容无关(最小行、全字段行均失败);用与 BgPartymatter 全部 @Excel 字段一致的副本类独立复现(C:\Users\sz04\AppData\Local\Temp\ap_repro):可变 ArrayList → 成功(8704 字节),不可变集合 → 完全相同的 500。修复方向:① 前端 exportConfig 切换到后端已有的 EasyExcel 导出端点 exportXlsByEasyExcel(BgPartymatterController.java:490-495,同模块 exportXlsHeaders 模板下载已实测可用,见 P0 修复建议);② 或后端导出前把数据集合复制为可变 List;③ 同时前端应把导出失败透出错误提示而非静默。 |
实测:exportXls 响应 HTTP 500 body ExcelExportException: Excel导出错误(qa-tmp/bgpartymatter/scripts/eng_04_trace.json、eng_09 再复现 500 且 export_toasts=[]);所务会页同样 500(eng_05_empty_office.json);独立复现成功/失败对比(ap_repro/MainRepro2.java vs MainReplica.java);AutoPoi 源码 ExcelExportServer.java:198-213 |
BgPartymatterList.vue:303-307(exportConfig)、后端 BgPartymatterController.java:281-287、JeecgController.java:62-88(:74 service.list、:86 DATA_LIST)、AutoPoi ExcelExportServer.java:198-213 |
P1 - 强烈建议
| # | 维度 | 问题描述 | 证据 | 位置 |
|---|---|---|---|---|
| 1 | 状态一致性 | 顶部统计标签(未开始/督办中/已完成)不随筛选、搜索、增删改刷新:搜索无结果/有结果、新增一条、删除一条后,列表数据都在变(总条数 3→4→3 正确变化),但三个统计标签始终不变(全程 未开始 3 / 督办中 0 / 已完成 0),与列表对不上,用户无法判断哪个是当前数据。根因:fetchStatusStats 仅在 onMounted 调用一次;searchQuery、searchReset、handleSuccess、新增/删除成功回调均未再触发统计刷新。 |
实测:eng_09_engineer_final.json —— 统计初始/新增后重查后/删除后重查后均为 ['未开始 3',...],而列表总条数 3→4→3(events.after_api_add_reload.has_new_row=true、after_delete.residue=0);eng_07_listdump.json —— 搜索前/搜索无结果后统计不变 |
BgPartymatterList.vue:100-104(标签渲染)、:446-465(fetchStatusStats)、:463(仅 onMounted 调用)、:440-444(searchQuery)、:509-513(handleSuccess)、:789-796(searchReset) |
| 2 | 用户体验/交互 | 详情弹窗字段"看似可编辑、实际点不动":详情模式下表单上方有一层透明遮罩 .jeecg-form-detail-effect(实测 1100×580 透明 div,pointer-events:auto)拦截全部点击,但控件本身未灰化(下拉控件 opacity=1、cursor=pointer、肉眼无只读提示);点「密级」下拉实测不弹出;「确认」按钮被 jee-hidden 隐藏。用户反复点击无反应,误以为系统坏了/自己没点中。根因:handleDetail 设 disableSubmit=true → JFormContainer :disabled 渲染遮罩 + Modal 隐藏确认按钮,但只读态缺视觉表达(控件未置灰、无"只读"标识)。 |
实测:eng_09_engineer_final.json —— detail_overlay_count=1、detail_overlay_style={pointerEvents:'auto', bg:'rgba(0,0,0,0)', 1100×580}、detail_select_dd_open=false(点密级下拉不弹)、detail_confirm_visible=false、detail_enabled_inputs=6;02_modal.json —— 详情模式 11 只读 / 6 可编辑输入并存;关闭后页面可交互、无残留遮罩 |
BgPartymatterList.vue:486-490(handleDetail)、BgPartymatterModal.vue:2-6(jee-hidden)、BgPartymatterForm.vue:3(JFormContainer :disabled)、:256(disabled=formDisabled) |
P2 - 建议
| # | 维度 | 问题描述 | 证据 | 位置 |
|---|---|---|---|---|
| 1 | 反馈 | 新增成功无 toast:新增提交成功、列表出现新行,但没有任何"保存成功"提示;删除却有"删除成功!",反馈不一致。根因:submitForm 用 createMessage.success(res.message),后端返回的 res.message 为空串,success() 收到空串不渲染。 |
实测:eng_09_engineer_final.json 的 API add {"success":true,"message":"添加成功!"}(后端有 message 但前端未取到/为空);12_adddel_final.json —— add 无 toast、delete 有「删除成功!」 |
BgPartymatterForm.vue:403-411 |
| 2 | 表单校验文案 | 校验文案与字段名不一致:主办部门字段漏选时,提示「请选择责任部门」,但字段 label 是「主办部门」,用户对不上号。 | 实测:02_modal.json form_item_errors(含「请选择责任部门」) |
BgPartymatterForm.vue:232-241(validator message 写死"责任部门") |
| 3 | 表单校验体验 | 序号字段只读却报「请输入序号」:序号输入框是只读的(自动生成),但必填校验文案是「请输入序号」,用户无法手动消除该错误(实际需回去填"议题序号"触发自动生成)。 | 实测:02_modal.json、07_formmap.json |
BgPartymatterForm.vue:23-26(只读)+ :228(校验"请输入序号") |
| 4 | 布局/性能 | 表格过宽需横向拖动:21 列总宽约 2808px,可视区约 1200px,中部字段(事项目录编码/抄告单编号/密级等)需反复横拖才能看到,看漏字段风险高。 | 实测:.ant-table-body scrollWidth=2808、clientWidth=1200(eng_02_listdata.json / 测量输出) |
BgPartymatter.data.ts:7-153(21 列) |
| 5 | 可发现性 | 「完成状态」筛选默认藏在「展开」折叠里:初始工具栏不显示,需点「展开」才出现,首次用户找不到状态筛选。 | 实测:toggle 文案"展开→收起"(04_expand_filter.png) |
BgPartymatterList.vue:39-52(toggleSearchStatus 默认 false) |
| 6 | 空态引导 | 搜索无结果空态只有「暂无数据」,无"换个条件/清除筛选"引导,也无一键清空入口。 | 实测:eng_09 race_final 空表(03b_search_empty.png) |
BgPartymatterList.vue(BasicTable 默认空态) |
| 7 | 控制台异常 | 列表页交互期间控制台出现 Invalid prop: type check failed for prop "onChange". Expected Function, got Array(<ASelect onChange= [, ]>),以及点击「导出」时 500 触发 Unhandled error during execution of component event handler。前者疑似筛选/搜索组件把多个 handler 合并成数组传给 ASelect(组件 prop 契约不严谨);后者证明导出失败在前端是未处理异常、被静默吞掉。 |
实测:eng_09_engineer_final.json console(19 条:5 个 app-shell Failed to resolve component + 7 条 ASelect onChange + 1 条 500 resource + 1 条 unhandled export handler) |
控制台证据(eng_09_engineer_final.json);export 相关 BgPartymatterList.vue:303-307 |
通过项(做得好的地方)
- 列表刷新正常:新增后手动点「查询」,列表立即出现新行(总条数 3→4);删除后「查询」,行消失(4→3)。列表数据链路(list API + 查询/重置)一致、无残留。
- 竞态安全:快速连续输入 3 个不同关键词(
QA_TEST_ENG_RACE_01 → 督办 → QA_ZZZ_NO_MATCH_RACE,无间隔),最终表格与最后一个关键词一致(0 行)、无旧响应串台/残留渲染,未出现 out-of-order 覆盖。搜索即输即查无竞态问题。 - 详情弹窗回显完整:详情标题「详情」,19 项字段回显;关闭后页面完全可交互,无残留遮罩/状态污染。
- 模板下载可用:「下载模板」成功下载
党委会-导入模板.xls(EasyExcelexportXlsHeaders路径,与走 AutoPoi 的「导出」形成对照——问题只在 AutoPoi 导出,不在模板)。 - 删除链路完整:行内更多 → 删除 → 二次确认 → 「删除成功!」toast → 列表刷新。新增/删除后列表均刷新。
- 数据分流正确:党委会(party)/所务会(office)数据严格隔离,不串台。
- 流程弹窗可用:「再次督办」发起流程弹窗标题/部门预填正确;「查询督办流程」流程列表可打开(引用
QA_REPORT_UX.md任务 6 实测证据)。 - 校验覆盖:必填校验能定位到字段;序号自动生成(会议时间+议题序号联动)有效。
- 导出 0 行场景正常:无数据时导出返回 200(进一步佐证根因只在"有数据的迭代"路径)。
未覆盖项 / 待实测(不列入 P0/P1/P2 判定)
| # | 项目 | 原因 / 现有线索 |
|---|---|---|
| 1 | 多角色/多部门/多密级权限对比(按钮级 v-auth 隐藏、数据权限过滤) |
仅 admin 一个账号,无法对比;exportXls 接口有 @RequiresPermissions,前端按钮有 v-auth,但未用第二账号实测不同角色下的可见性/越权 |
| 2 | 「模板校验」「模板导入」真实文件流程(选 Excel → 校验通过/报错提示) | headless 无法模拟系统文件选择框;对隐藏 input 塞 .txt 无任何 toast,结果不明确(BgPartymatterList.vue:746-784) |
| 3 | 后端 service.list() 返回不可变集合的具体包装层(是哪个拦截器/配置把 MP 的 selectList 结果变成不可变) |
前端侧证据已足够(0 行 200 / ≥1 行 500 / 独立复现),但精确到后端哪个类包装需后端日志/断点确认;已确认 BgPartymatterServiceImpl 为纯 MP ServiceImpl 无 list() 覆盖、JeecgController.exportXls 无包裹、运行时 AutoPoi 为 1.4.11(trace 行号与源码逐行吻合) |
| 4 | 「完成状态」筛选选中后的实际过滤行为(BPM 状态 dict 映射) | 未单独实测(筛选在「展开」里,见 P2-5);代码走 JDictSelectTag @change="searchQuery",推断正常 |
测试过程记录
- 环境:前端 dev
http://127.0.0.1:3100(运行中,未动);后端:8080/jeecg-boot(真实);登录admin/123456(mock 登录 + 真实业务接口)。脚本见qa-tmp/bgpartymatter/scripts/。 - 核心操作与产物:
eng_04_trace.py/eng_05_empty_office.py:直接导出抓 500 全量 body(party/office)、0 行导出对比 →eng_04_trace.json、eng_05_empty_office.json(0 行 200 / ≥1 行 500)。eng_07_listdump.py:搜索无结果前后统计标签对比 →eng_07_listdump.json(统计不变)。eng_08_token2.py:从请求头提取 X-Access-Token(localStorage 无 token,token 在内存/请求头注入)。eng_09_engineer_final.py:综合验证(登录 → 列表 → API 新增 → 查询刷新 → 快速搜索竞态 → 详情遮罩 → 导出 → 模板 → API 删除)→eng_09_engineer_final.json。- 复用既有产物:
02_modal.json(新增必填校验+详情字段态)、12_adddel_final.json(新增/删除 toast)、05_flow.json(流程弹窗+导出 500)、07_formmap.json(表单字段映射)。
- 关键网络请求:
GET /jeecgboot/bgpartymatter/bgPartymatter/list?...→ 200(列表,正常)POST /jeecgboot/bgpartymatter/bgPartymatter/add→ 200{"success":true}(新增)DELETE /jeecgboot/bgpartymatter/bgPartymatter/delete?id=...→ 200{"success":true}(删除)GET /jeecgboot/bgpartymatter/bgPartymatter/exportXls?...→ 500(导出,P0)GET /jeecgboot/bgpartymatter/bgPartymatter/exportXlsHeaders?...→ 200 下载党委会-导入模板.xls(模板)
- console 摘要:5 条 app-shell
Failed to resolve component(AppSearch/Notify/SettingDrawer 等,首页框架级,非本页引入);7 条ASelect onChange类型警告(P2-7);1 条 500 resource + 1 条 export 未处理异常(P0 佐证)。 - 测试数据卫生:本次会话创建
QA_TEST_ENG_RACE_01×4(含多次中断运行残留)与既有的QA_ENG_BISECT_*×5、清空后测试、assacas(office),已全部经删除 API 清理并复核:party/office 列表按QA_/QA_TEST/QA_ENG过滤均 0 残留。测试数据一律置于qa-tmp/bgpartymatter/(gitignore),不入库。