提交 75870a5e authored 作者: 陈泽健's avatar 陈泽健

feat(reports): 报告中心展示历史失败/取消执行 + 会话56交接归档

- reports.py list_reports:状态筛选由仅 completed 扩展为 completed/failed/cancelled,并返回 status 字段
- Reports.vue 触发方式列新增失败/取消状态标签(failed红色 / cancelled信息色)
- HANDOFF_UI自动化.md 记录会话56(钉钉通知改版 + 5.60 部署)+ 会话55历史状态归档
Co-Authored-By: 's avatarClaude <noreply@anthropic.com>
上级 ae83a558
...@@ -2,36 +2,165 @@ ...@@ -2,36 +2,165 @@
> **生成时间**: 2026-09-08 > **生成时间**: 2026-09-08
> **当前分支**: `platform-auto-test` > **当前分支**: `platform-auto-test`
> **最近提交**: `14e3c365` fix(execution): 运行中 total_cases 保持计划总数(会话 54 > **最近提交**: `f363abdd` docs(handoff): 会话53 系统配置500 修复记录 + 被测系统配置数据库缺列运维文档归档(本会话改动已提交推送
> **状态**: 🟢 **会话54 完成(2026-09-08):执行进度「总用例数」显示修复——运行中 total_cases 被覆盖为已完成数(会话53 MissingGreenlet 修复副作用)导致 5.44 定时任务执行中显示 60 多而非 342、前端进度条恒 100%;单点删除覆盖行恢复计划总数口径,437 测试全绿,已部署 5.60 并实机验证(运行中 total_cases 恒为 19)**(历史会话见下章节) > **状态**: 🟢 **会话57 完成(2026-09-08):双窗口工作合并——本窗口会话 54/55/56(报告中心放开失败/取消执行 → 全量部署 5.44/5.202 → 钉钉通知版面重构)与另一窗口会话 54(执行进度 total_cases 显示修复,提交 `14e3c365`/`b227d759`)在 git rebase 中合并;编号冲突已在本节说明,两线工作均已完成并保留**(历史会话见下章节)
--- ---
## 📊 当前状态(会话 54,2026-09-08 · 执行进度总数显示修复 + 5.60 部署 ## 📌 会话编号冲突说明(会话 57 合并记录,2026-09-08
**背景**:用户反馈「5.44 定时任务用例只有 60 多个,应该有 300 多条」。5.44 实查核实:用例数据无丢失(409 = 342 UI + 67 安全;定时任务 28 模块实际收集 342 条,当轮 case_results 252 pending + 71 passed + 19 failed = 342 全部排入)。「60 多」是**运行中 `total_cases` 字段被实时覆盖为「已完成数」**的显示口径错误——会话 53 MissingGreenlet 修复的副作用。 两个工作窗口并行开发导致 HANDOFF 会话编号撞号:
### ✅ 修复内容 | 编号 | 本窗口(UI 自动化线) | 另一窗口(执行中心线) |
- **根因**`execution_service.py` `_persist_case_result()``execution.total_cases = passed + failed + skipped`,每个用例完成后把总用例数覆盖为已完成数 → 执行中心「总用例数」运行中从 0 涨到 342、进度条 `(passed+failed+skipped)/total_cases` 恒 100%。 |------|----------------------|----------------------|
- **修复**:单点删除该覆盖行,`total_cases` 保持 `create_execution()` 写入的计划总数 `len(case_ids)`。通过率分子分母均用本地变量,不受影响;`expire_on_commit=False` 已确认,收尾 `calculate_pass_rate()` 读列安全(MissingGreenlet 仅回滚后触发,收尾路径未动)。 | 会话 54 | 报告中心放开失败/取消历史执行 + 5.60 部署 | 执行进度「总用例数」显示修复(total_cases 保持计划总数,提交 `14e3c365`,437 测试全绿,已部署 5.60 并实机验证运行中恒为 19) |
- **文档**`Docs/PRD/需求文档/执行中心/_问题处理与计划执行_执行进度总数显示修复.md`(问题核实数据 / 根因链 / 方案依据 / 执行步骤 / 验收标准 / 部署备注)。 | 会话 55 | 报告中心修复全量部署 5.44/5.202 | — |
- **测试**:后端全量 `pytest tests/` **437 passed**(70s,无回归)。 | 会话 56 | 钉钉通知版面重构(本节主体) | — |
### ✅ 5.60 部署与实机验证 两线互不覆盖代码(前者改 reports.py/Reports.vue/钉钉服务,后者改 execution_service.py),已通过 rebase 合并。后续会话编号从 57 顺延。
- 部署前拉取远程文件 diff:与本地仅差本次一处(4行→5行),无其他窗口 WIP;远程备份 `execution_service.py.bak_20260908`
- 上传(LF 行尾)→ 容器 py_compile OK → restart 15s healthy → `/health` 200。
- **实机验证**:新代码下第一笔执行 `exec_a92b33d4`(19 用例)运行中查库 `total_cases=19` 恒定(case_results = 3 failed + 16 pending;修复前此处显示 3)——总用例数不再从 0 往上涨,进度条恢复 0%→100% 语义。
- **验证脚本**`backend/scripts/verify_560_total_cases_fix.py`(传执行 ID 复查;被 .gitignore 忽略未入库)。诊断脚本 `diag_544_sched_case_count.py` 已入库。
### ⚠️ 部署过程备注(如实记录) ---
- 重启时 5.60 有一笔 10:39 手动触发的执行(exec_ff03b772)正在运行,被重启中断;FastAPI shutdown 钩子正常收尾(日志「执行完成 通过=0 失败=2」),未留僵尸 running 记录,10:44 起新执行正常。
## 📊 当前状态(会话 56,2026-09-08 · 钉钉通知版面重构:分区卡片式 + 统一「定时报告」标题)
**背景**:用户收到 5.202 每日定时执行钉钉通知(`每日定时执行自动化测试-20260908-043950`,总数 346 / 通过 249 / 失败 95 / 通过率 71.97%,耗时 14365s),反馈两点:① 样式太丑陋,不要打印失败用例的具体信息(95 项明细刷屏且错误摘要换行混乱);② 标题不叫「UI自动化告警」,应叫「UI自动化定时报告」。
### ✅ 方案确认(AskUserQuestion)
- 版面:**分区卡片式**(每类信息独立成段、标签明确、重点加粗)胜出极简紧凑式;
- 失败提示:**保留一句**「❌ 存在 N 项失败用例,请及时查看报告」(保持告警语义,不列明细)。
### ✅ 实现改动(`backend/app/services/dingtalk_notify_service.py`)
| 改动 | 说明 |
|------|------|
| 三形态统一标题 | 消息标题与正文头均改为「UI自动化定时报告」,emoji 区分 ✅全部通过 / ⚠️存在失败 / 🚨执行异常;@手机号逻辑不变(仅失败/异常形态携带) |
| 删除失败明细 | 移除 `MAX_FAILED_ITEMS``_case_error_summary``• 用例名:错误摘要` 列表(含 >5 条折叠行),明细看报告 |
| 失败统计提示 | 形态 2 新增一句 `❌ 存在 {N} 项失败用例,请及时查看报告`;N 取 `execution.failed` 优先,为 0 回退 `len(failed_results)` |
| 分区 blocks + `\n\n` | 新增 `_join_blocks()`(过滤空块);**钉钉 markdown 会把单个 `\n` 折叠为空格**——旧版标题与被测系统行、失败明细全部挤成一行即此因,块间必须 `\n\n` 才换行 |
| 版面降噪 | 跳过为 0 时不显示「⏭ 跳过 0」;链接文案改「📎 点击查看完整报告」 |
| `_shrink_message` 简化 | 明细已不列举,逐条折叠逻辑删除,仅保留超 2000 字节整体截断兜底 |
### ✅ 测试(`backend/tests/test_dingtalk_notify.py` 同步更新)
- 删除 `MAX_FAILED_ITEMS`/`_case_error_summary` 相关导入与用例;改写三形态断言(统一标题、失败提示、不列明细);
- 新增:`test_failure_form_never_lists_details`(失败再多也无明细行)、`test_failure_hint_uses_execution_failed_first``test_lines_separated_by_blank_line`(块间无裸 `\n`,防回归钉钉折叠问题)、`test_flatten_custom_length`
- **结果:钉钉单测 29/29 通过;后端全量 452/452 通过(63s)**。三形态样例消息已在本会话内打印供用户确认(样例基于 exec_4167449a... 的真实数据 346/249/95)。
### ✅ 5.60 部署(2026-09-08,用户中途指令「更新部署到5.60环境」)
| 步骤 | 结果 |
|------|------|
| scp 单文件 → 宿主机 `/data/third_party/plat-auto-test/backend/app/services/` | ✅ MD5 `c954e976` 与本地一致(旧版已备份 `/tmp/dingtalk_notify_service.py.bak`) |
| 容器内 `py_compile /app/app/services/dingtalk_notify_service.py` | ✅ OK(容器内路径为 `/app/app/...`,非 `/app/backend/...`) |
| 内容核对 | ✅ 容器内「UI自动化定时报告」×7、「UI自动化告警」×0 |
| `docker compose restart app` + 健康检查 | ✅ Up (healthy)、`/health` 200 |
| 新版面真实推送验证 | ⏳ 待 5.60 下一个定时任务完成时自然触发(未主动发群消息打扰) |
### ⚠️ 待办 ### ⚠️ 待办
- **5.44 / 5.202 部署本修复**(本次按用户要求仅部署 5.60;5.44 运行中显示口径仍旧,跑完终值正确;需与下一次统一部署一起上) - **部署**:5.202(告警来源)待部署,**下轮定时 2026-09-09 04:39 前需部署生效**;5.44 待用户确认;
- **5.44 看板用例收尾**(会话 53 遗留):case_6699d6dd 步骤 3 断言修正为「新建会议」→ 5/5 回归确认 - **git 提交**:本会话 dingtalk 改动 + 会话 54/55 遗留(reports.py、Reports.vue、`Docs/PRD/问题处理/报告中心/` 两文档)+ HANDOFF,均在工作区未提交;
- **5.202 部署**会话 53 改动(遗留) - 承接会话 53/55 遗留:case_6699d6dd 步骤 3 断言改「新建会议」→ 5/5 回归、5.60 录制器真实环境验收;
- **5.60 录制器真实环境验收**(会话 52 P2) - 清理诊断脚本(`probe_exec_a1333ea*` ×20 等,见会话 55 待办)。
- 清理诊断脚本(diag_544_*.py、dump_544_cases.py、dump9_cases.json、tmp_wait_deploy_544.py 等)
---
## 📊 历史状态(会话 55,2026-09-08 · 报告中心修复全量部署 5.44/5.202)
**背景**:承接会话 54(修复只部署了 5.60)。用户发现 5.44 报告中心数据仍很少,追问「怎么还是那么点数据」→ 指令「同步更新」「5.44 看效果」「5.202 全量」。
### ✅ 排查(会话 54 修复未覆盖 5.44/5.202)
- 5.44/5.202 容器内 `reports.py` 第 151 行均为旧版 `conditions = [Execution.status == "completed"]` → 报告中心只见 completed。
- DB 实况:5.44 executions 59 条(failed 43 / cancelled 9 / completed 7,case_results 15816);5.202 59 条(failed 45 / cancelled 1 / completed 12 / running 1,case_results 19277)。
### ✅ 5.44 部署(递归同步方式)
| 步骤 | 结果 |
|------|------|
| 首次 `deploy_44_202.py --no-frontend` 上传后端 | ❌ 容器崩溃循环 `ModuleNotFoundError: No module named 'app.services.recorder_engine'` |
| 根因 | `deploy_44_202.py` 静态 `BACKEND_FILES` 清单**缺会话 52 新增文件**(recorder_engine.py 等),但 main.py/recorder.py 已 import → 起不来 |
| 修复:递归同步整个 `backend/app/`(.py 全量覆盖,清 `__pycache__`)+ 重启 | ✅ Up (healthy),`recorder_engine` 容器内 import OK |
| MD5 核对 | 远端 reports.py `e8dbaac5` = 本地 ✅;recorder_engine.py 与本地仅行尾差异(CRLF/LF),内容一致 ✅ |
### ✅ 5.202 全量部署(后端 + 前端 dist)
| 步骤 | 结果 |
|------|------|
| backend/app 递归同步 | ✅ 上传 88 个 .py(67 个同大小跳过) |
| 前端 dist 首轮上传 | ❌ 上传 0 文件 → 容器崩溃 `RuntimeError: Directory '/app/frontend/dist/assets' does not exist`(main.py:499 StaticFiles mount) |
| 修复:`rm -rf` 后强制重传 | ✅ 上传 96 个 dist 文件 → 重启 → Up (healthy) |
### ✅ 三台验收汇总(2026-09-08)
| 服务器 | 容器 | list total | 状态分布 | 失败执行报告 |
|--------|------|-----------|---------|-------------|
| 5.44 | healthy | **59** | completed 7 / failed 37 / cancelled 6 | 生成+下载可用 |
| 5.202 | healthy | **59** | failed 39 / completed 10 / cancelled 1 | **GET 200 / 390KB + 下载 200** ✅ 实测 |
| 5.60 | healthy | 994 | —(会话 54 已验收) | GET 200 / 38.6KB(会话 54) |
- reports.py 均为 `status.in_(["completed", "failed", "cancelled"])`;前端指纹 `index-BsPQEKYX.js` 三台一致。
- 5.44 附带发现:会话 53 遗留的「5.202 部署会话 53 改动」**已随本次全量覆盖完成**(playwright_executor.py / execution_service.py 等全量同步)。
### 🕳️ 踩坑沉淀(勿重复踩)
1. **`deploy_44_202.py` 静态 BACKEND_FILES 清单会漏新文件**:新增 backend 模块后必须同步补清单,否则远端 main.py import 失败整站起不来;**更稳的方式是递归同步整个 `backend/app/` 目录**(本次已用此法)。
2. **SFTP 脚本里 `__file__` 相对路径易错**:脚本放 /tmp 时拼出的 LOCAL_APP 不存在 → 「上传 0 个文件」且不报错,务必用绝对路径并打印上传计数。
3. **前端 dist 上传 0 文件 → StaticFiles mount 崩溃**:main.py:499 `StaticFiles(directory=dist/"assets")` 对目录存在性强校验,dist 没传到位容器直接 Restarting (1)。
4. **报告 generate 端点为 GET**:POST 返回 405(5.44 实测),验收脚本用 GET。
5. **宿主机 `curl localhost/health` 在 5.44/5.202 打到别的容器**(80 端口是 unginx 等其他服务),健康检查应在 `plat-auto-test-app` 容器内执行。
### ⚠️ 待办
- **git 提交**本会话改动:`reports.py` + `Reports.vue` + `Docs/PRD/问题处理/报告中心/` 两文档 + HANDOFF(工作区未提交;另有历史遗留 `backend/data/test_platform.db` 与一批 probe 脚本待甄别)
- 清理诊断脚本:`probe_exec_a1333ea*` ×20、`diag_quick.py``diag_sut_500.py``_probe_exec_tmp.py``probe_44_202.py``probe_dingtalk_44_202.py``probe_544_report_backfill.py` 有沉淀价值可留)
- 承接会话 53 遗留:5.44 看板用例收尾(case_6699d6dd 步骤 3 断言改「新建会议」→ 5/5 回归)、5.60 录制器真实环境验收
- 观察三台报告中心线上使用反馈;`deploy_44_202.py` 清单待修复(补 recorder_engine.py 等,或改为递归同步)
---
## 📊 历史状态(会话 54,2026-09-08 · 报告中心放开失败/取消历史执行 + 5.60 部署)
**背景**:用户发现 5.44 定时任务历史运行记录很多(59 条执行)但报告中心几乎看不到,要求「按照历史运行记录创建这些历史运行记录的报告数据」。
### ✅ 数据探测(`probe_544_report_backfill.py`,paramiko + 容器内 pymysql)
5.44 `executions` 表实况(59 条):
| 触发 | 状态 | 条数 | 旧报告中心 |
|------|------|------|-----------|
| scheduled | failed | 37 | ❌ 不可见 |
| scheduled | cancelled | 9 | ❌ 不可见 |
| scheduled | completed | 4 | ✅ |
| scheduled | running | 1 | —(合理排除) |
| manual | failed / completed | 6 / 2 | 仅 completed 可见 |
**关键事实**`case_results` 已有 15816 条明细(看门狗失败前逐条实时落库),`ReportService.generate_html()` 只读 case_results 渲染、不关心 execution.status → **失败执行报告可实时生成,无需补历史报告文件**(容器内报告文件仅 10 个,但补文件无意义且立即过期)。
### ✅ 修复方案(用户在 AskUserQuestion 中选定「报告中心放开全部执行」)
| 文件 | 改动 |
|------|------|
| `backend/app/routers/reports.py` | `list_reports` 查询 `status == "completed"``status.in_(["completed", "failed", "cancelled"])`;响应 items 增加 `status` 字段;**保留 MySQL 1038 防护两段式查询**(先取 id 轻量排序分页再回查整行) |
| `frontend/src/views/Reports.vue` | 触发方式列(90→140)加失败/取消标签:`failed` 红「失败」、`cancelled` 灰「取消」;查看/下载/钉钉通知按钮复用现有实时渲染链路 |
**本地验证**:pytest **37 passed**;本地 SQLite 实测列表 total 85 → **712**(含 623 failed + 4 cancelled);`npm run build` 成功(46.37s)。
### ✅ 文档先行(用户明确要求「输出问题处理和计划执行文档后修复」)
`Docs/PRD/问题处理/报告中心/` 新建(项目首个报告中心类问题处理目录):
- `_问题处理_报告中心历史失败取消执行不可见无报告.md` — 现象/根因/方案决策/部署决策/验收标准
- `_执行计划_修复报告中心历史失败取消执行不可见.md` — 六阶段计划 + 实施记录,验收清单已全勾选
### ✅ 5.60 部署 + 线上验收(2026-09-08)
> 用户三条中途指令定方向:**「这个先在5.60上修复」「先不处理5.44」「输出问题处理和计划执行文档后修复」**
| 步骤 | 结果 |
|------|------|
| scp `reports.py``/data/third_party/plat-auto-test/backend/app/routers/` | ✅ |
| 清空 dist + scp 全量 dist(94 assets + index.html) | ✅ |
| `docker compose restart app` | ✅ Up (healthy) ~28s |
| `/api/reports/list` | ✅ **total=994**,首条即 scheduled failed(exec_a92b33d4,19 用例 13 failed) |
| 失败执行报告实时生成 | ✅ `generate/exec_a92b33d4...` HTTP 200 / 38640B,标题正常,含通过/失败明细 |
| 下载接口 | ✅ HTTP 200 |
| 前端新构建生效 | ✅ 线上资源指纹 `index-BsPQEKYX.js` 与本地 build 一致 |
### ⚠️ 待办
- **git 提交**本会话改动:`reports.py` + `Reports.vue` + `Docs/PRD/问题处理/报告中心/` 两文档(工作区未提交)
- **5.44 / 5.202 部署**本修复(用户明确「先不处理5.44」,待 5.60 观察稳定后由用户安排;可用 `deploy_44_202.py --host 192.168.5.44 --no-frontend` 增量上传 reports.py)
- 清理诊断脚本:本会话新增 `probe_544_report_backfill.py`(有沉淀价值可留)+ 历史 `probe_exec_a1333ea*` ×20、`diag_quick.py``diag_sut_500.py``_probe_exec_tmp.py``probe_44_202.py``probe_dingtalk_44_202.py`
- 承接会话 53 遗留:5.44 看板用例收尾(.block 断言改「新建会议」)、5.202 部署会话 53 改动、5.60 录制器真实环境验收
--- ---
...@@ -284,4 +413,4 @@ ...@@ -284,4 +413,4 @@
--- ---
*本文档由 Claude Code 于 2026-09-08 更新(会话 54:执行进度「总用例数」显示修复——运行中 total_cases 被覆盖为已完成数的会话 53 副作用,单点删除恢复计划总数口径(437 测试全绿),已部署 5.60 并实机验证运行中 total_cases 恒为 19;问题处理文档入库 Docs/PRD/需求文档/执行中心/。历史:会话 53 5.44 登录/落库双 P0 闭环(do_login 后 _navigate_to_home 主动落首页 + error_message 60000B/log-error 20000 截断 + 滚动计数绕过 MissingGreenlet),fix_544_login_cases.py 重写 5 用例+删 4 垃圾用例(409 总数),3 轮回归 4/5(看板 .block 文本实测「新建会议」);5.60 部署 7 后端文件 + dist 95 文件全绿(RECORDER_ENABLED 生效、PLATFORM_BASE_URL 保留);会话 52 真录制器 Phase 5(`b80abae3`);会话 51 截图瘦身部署 5.44(`228c732a`+`45a247b8`);会话 50 会议巡检 navigate 直达 3/3 通过 `1924d77f`+`d2660651`;会话 49 视讯设备直达升级 11/11 `35a58b2e`;会话 48 看门狗修复 + 缺口补齐 8/8 `720bf764`)。* *本文档由 Claude Code 于 2026-09-08 更新(会话 57:双窗口工作合并——本窗口钉钉通知版面重构(三形态统一标题「UI自动化定时报告」+ 分区卡片式 blocks/`\n\n` 连接 + 移除失败明细仅一句统计提示,踩坑沉淀「钉钉 markdown 单 `\n` 折叠为空格须 `\n\n` 分行」,钉钉单测 29/29 + 后端全量 452/452 通过,已部署 5.60 MD5 `c954e976`)与另一窗口执行进度 total_cases 显示修复(会话 54 编号撞号,`b227d759` 437 测试全绿已部署 5.60)经 git rebase 合并提交推送;报告中心放开失败/取消历史执行(reports.py + Reports.vue,三台 total 59/59/994 全通)随本窗口一并归档。历史:会话 53 5.44 登录/落库双 P0 闭环(`f363abdd` 归档);会话 52 真录制器 Phase 5(`b80abae3`);会话 51 截图瘦身部署 5.44(`228c732a`+`45a247b8`);会话 50 会议巡检 navigate 直达 3/3 通过 `1924d77f`+`d2660651`;会话 49 视讯设备直达升级 11/11 `35a58b2e`;会话 48 看门狗修复 + 缺口补齐 8/8 `720bf764`)。*
...@@ -134,7 +134,11 @@ async def list_reports( ...@@ -134,7 +134,11 @@ async def list_reports(
db: AsyncSession = Depends(get_db), db: AsyncSession = Depends(get_db),
): ):
""" """
获取可生成报告的已完成执行记录列表 获取可生成报告的执行记录列表
已结束的执行(完成/失败/取消)均会列出——报告由 case_results
实时渲染,失败/取消的执行同样有明细数据可查看。运行中/待执行
的执行数据不完整,不在报告中心展示。
Args: Args:
skip (int): 跳过记录数 skip (int): 跳过记录数
...@@ -147,8 +151,10 @@ async def list_reports( ...@@ -147,8 +151,10 @@ async def list_reports(
""" """
from sqlalchemy import func from sqlalchemy import func
# 构建筛选条件:已完成 + 可选按用例类型 # 构建筛选条件:已结束状态 + 可选按用例类型
conditions = [Execution.status == "completed"] conditions = [
Execution.status.in_(["completed", "failed", "cancelled"])
]
if case_type: if case_type:
conditions.append(Execution.case_type == case_type) conditions.append(Execution.case_type == case_type)
...@@ -183,6 +189,7 @@ async def list_reports( ...@@ -183,6 +189,7 @@ async def list_reports(
"id": e.id, "id": e.id,
"name": e.name, "name": e.name,
"case_type": e.case_type, "case_type": e.case_type,
"status": e.status,
"pass_rate": e.pass_rate, "pass_rate": e.pass_rate,
"total_cases": e.total_cases, "total_cases": e.total_cases,
"passed": e.passed, "passed": e.passed,
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
</el-tag> </el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="触发方式" width="90"> <el-table-column label="触发方式" width="140">
<template #default="{ row }"> <template #default="{ row }">
<el-tag <el-tag
:type="row.trigger_type === 'scheduled' ? 'warning' : 'info'" :type="row.trigger_type === 'scheduled' ? 'warning' : 'info'"
...@@ -43,6 +43,14 @@ ...@@ -43,6 +43,14 @@
> >
{{ row.trigger_type === 'scheduled' ? '定时' : '手动' }} {{ row.trigger_type === 'scheduled' ? '定时' : '手动' }}
</el-tag> </el-tag>
<el-tag
v-if="row.status && row.status !== 'completed'"
:type="row.status === 'failed' ? 'danger' : 'info'"
size="small"
style="margin-left: 4px"
>
{{ row.status === 'failed' ? '失败' : '取消' }}
</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="通过率" width="120"> <el-table-column label="通过率" width="120">
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论