Files
guoju0808/_self/manager_meeting_detail.md
T
郭庆泰 c3eb8ed9c3 feat: OCR 服务 + 会议材料模块
ry-ocr/ (新)
  本地发票识别微服务 (PaddleOCR 3.x + FastAPI, 8801)
  - QR 优先: 扫到二维码即取开票时间/发票号/金额; 没扫到/格式不合法直接判非发票, 不跑 OCR
  - 配置 QR_FULL_OCR 控制快路径(false, 0.2s)还是全字段(true, 4.5s)
  - /recognize/invoice (multipart) + /recognize/invoice/by-path (本地路径, 白名单) + /recognize/text
  - is_invoice / from_qr / qr_raw / qr_error / error_code 字段
  - 12 字段发票抽取 (regex + 启发式, 左右主体识别)
  - 超时保护 (15s 单页 / 60s 总流程) + PaddleOCR 单例 + ThreadPoolExecutor

ry-api/ruoyi-business/
  - pom.xml: 加 hutool-http/json/core 5.8.27, lombok 1.18.30 (OcrClient @Slf4j 所需)
  - ocr/: OcrClient + InvoiceResult/Fields/Line + ZipExtractor + InvoiceOcrScheduler
  - oss/: OssUploader + OssConfMeta (OCR 识别后重传 OSS)
  - config/: OcrConfig + OcrExecutorConfig (后台线程池)
  - service/impl/InvoiceOcrService: 后台提交 OCR, ZIP 路径解压识别, 替换场景先清旧
  - 会议材料 CRUD 全套 (BizMeetingAuditLog/Executor/Invoice/Material/Supervisor):
    controller + service + mapper + domain + xml

ry-vue3/
  - MeetingDetail.vue (新建): 会议详情页 (含评分维度章节, 改只读)
  - Meetings.vue / OssFileUploader.vue / router / Login.vue: 适配新字段

ry-api/ruoyi-admin/
  - RuoYiApplication.java + application.yml: 启用 @Async 异步支持

_self/
  - manager_meetings.md / manager_meeting_detail.md: 文档
2026-08-22 00:23:22 +08:00

416 lines
22 KiB
Markdown

# manager/meetings/detail — 端到端技术审查 (v2 修订)
> **审查时间**: 2026-08-21
> **v1 修订**: v1 报告 §9 #1 标错 mapper 漏字段范围 (说 invitation_url/schedule_url/address 全漏, 实际只有 address 漏), 由 v2 实测 mapper 后修正
> **v2 触发**: 实际访问 `/manager/meetings/detail` 发现"会议地址"等字段不显示, 重新按 SKILL 4 跳 + DB 实测 + 三层穿透
> **审查范围**: `ry-vue3/src/views/manager/MeetingDetail.vue` (240 行) + `router/index.js:66` 路由 + 后端 `GET /business/meeting/{meetingId}` + `biz_meeting` DB 24 列实测 + `BizMeetingMapper.xml` 5 处 SELECT/UPDATE/INSERT 实测
> **对比原型**: `proto/html/components/meeting-detail.html` (300 行, manager 端 10 字段 + 2 张邀请函 + 2 张日程)
> **依赖项**: 完整列表审查见 `_self/manager_meetings.md` v3 (DB/Entity/mapper 共享)
---
## 0. 4 跳定位
| 跳 | 文件:行 | 内容 |
|---|---|---|
| ① 角色菜单 | `ry-vue3/src/layout/AdminLayout.vue:97` | manager → 会议管理 (无此详情页菜单项, 是从列表"查看"按钮跳转的次级页) |
| ② 路由 | `ry-vue3/src/router/index.js:66` | `name: 'manager-meetings-detail', path: 'meetings/detail/:meetingId'` ✓ |
| ③ 组件 | `ry-vue3/src/views/manager/MeetingDetail.vue` (240 行) | 4 段: 基本信息 + 邀请函 + 日程 + 返回 |
| ④ 原型 | `proto/html/components/meeting-detail.html` (300 行) | 3 段: 基本信息 (10 字段) + 邀请函 (2 张图+下载) + 日程 (2 张图+下载) + 返回 |
---
## 1. 主要功能 + 可见性
### 1.1 主要功能
| 功能 | 前端入口 | 后端接口 | 数据表 |
|---|---|---|---|
| 加载会议详情 | `MeetingDetail.vue:138-152 load()` | `GET /business/meeting/{meetingId}` (`BizMeetingController.java:38-42`) | `biz_meeting` |
| 返回列表 | `goBack()``router.push('/manager/meetings')` | — | — |
| 邀请函预览/下载 | `<a :href="row.invitationUrl" target="_blank">` (line 60-70) | (无后端, 直链) | `biz_meeting.invitation_url` |
| 日程预览/下载 | `<a :href="row.scheduleUrl" target="_blank">` (line 82-92) | (无后端, 直链) | `biz_meeting.schedule_url` |
### 1.2 可见性 (三层过滤)
| 层 | 来源 | 校验字段 |
|---|---|---|
| 前端菜单 | 无菜单项, 从列表"查看"按钮 router.push 进入 |
| 路由守卫 | `router/index.js:66` | `meta.title` (无 role 字段, 实际靠 `meta.role='manager'` 父路由限制) |
| 后端 SQL | `BizMeetingMapper.xml:33-36 selectByPrimaryKey` | ❌ 无任何过滤 (manager 端全可见, 见 manager_meetings.md §10 #1) |
**数据隔离**: ❌ 缺失 (manager 端无 `lead_user_id` 过滤, 详情接口也未校验 project 归属)。
---
## 2. 筛选项 (filter-form)
**无** — 详情页只读, 无筛选。
---
## 3. 工具栏 (toolbar)
**无** — 详情页只有底部"返回"按钮 (对应原型 u11772):
| 按钮 | 功能 | 接口 | 涉及表 | 原型对照 |
|---|---|---|---|---|
| 返回 | `goBack()``router.push('/manager/meetings')` | — | — | ✅ u11772 |
---
## 4. 表格 (el-table)
**无** — 详情页只展示单条记录的字段, 不列表。
---
## 5. 行内操作按钮
**无** — 详情页是只读视图, 无行内操作。
---
## 6. Java Entity ↔ 数据库表 一致性
### 6.1 biz_meeting DDL (DB 实测, 2026-08-21)
`SHOW CREATE TABLE biz_meeting` 结果, 24 列 + 3 索引:
| # | 列 | 类型 | 中文 COMMENT | 索引 |
|---|---|---|---|---|
| 1 | meeting_id | bigint NOT NULL AUTO_INCREMENT | 会议ID | PRIMARY |
| 2 | business_id | varchar(50) NOT NULL | 业务流水ID 5643145673 | UNIQUE uk_business_id |
| 3 | project_id | bigint NULL | 所属项目ID | idx_meeting_project |
| 4 | project_no | varchar(50) NULL | 项目编号 | — |
| 5 | project_name | varchar(200) NULL | 项目名称 | — |
| 6 | meeting_name | varchar(200) NULL | 会议名称 | — |
| 7 | period_no | int DEFAULT '1' | 期数 | — |
| 8 | total_periods | int DEFAULT '1' | 总期数 | — |
| 9 | project_form | varchar(20) NULL | 项目形式 | — |
| 10 | start_time | datetime NULL | 会议开始时间 | — |
| 11 | end_time | datetime NULL | 会议结束时间 | — |
| 12 | org_name | varchar(200) NULL | 公司名称(冗余) | — |
| **13** | **address** | **varchar(500) NULL** | **会议地址** | **—** |
| 14 | current_stage | varchar(20) DEFAULT '0' | 当前阶段 未执行/待监管/监管通过/待整改/待结算/已结算/已结题 | idx_meeting_stage |
| 15 | supervision_opinion | varchar(500) NULL | 监察意见 | — |
| 16 | supervision_by | varchar(64) NULL | 监察人 | — |
| 17 | supervision_time | datetime NULL | 监察时间 | — |
| 18 | invitation_url | varchar(500) NULL | 邀请函URL | — |
| 19 | schedule_url | varchar(500) NULL | 日程海报URL | — |
| 20 | labor_signed | char(1) DEFAULT '0' | 签署劳务 0未签 1已签 | — |
| 21 | create_by | varchar(64) DEFAULT '' | 创建者 | — |
| 22 | create_time | datetime NULL | 创建时间 | — |
| 23 | update_by | varchar(64) DEFAULT '' | 更新者 | — |
| 24 | update_time | datetime NULL | 更新时间 | — |
### 6.2 4 层穿透字段对照矩阵 (本详情页相关)
| # | DB 列 | Entity 有 | Mapper SELECT | Vue 渲染 | 原型 | 状态 |
|---|---|---|---|---|---|---|
| 1 | meeting_id | ✅ | ✅ | ✅ 会议ID | — | ✅ 富余 |
| 2 | business_id | ✅ | ✅ | — | — | ⚠️ 内部审计字段 |
| 3 | project_id | ✅ | ✅ | — | — | ⚠️ 内部 ID, 详情不显示 |
| 4 | project_no | ✅ | ✅ | ✅ 项目编号 | ✅ | ✅ |
| 5 | project_name | ✅ | ✅ | ✅ 项目名称 | ✅ | ✅ |
| 6 | meeting_name | ✅ | ✅ | ✅ 会议名称 | ✅ | ✅ |
| 7 | period_no | ✅ | ✅ | ✅ 期数 | ✅ | ✅ |
| 8 | total_periods | ✅ | ✅ | ✅ 总期数 | ✅ | ✅ |
| 9 | project_form | ✅ | ✅ | ✅ 项目形式 | — | ✅ 富余 (manager 端用) |
| 10 | start_time | ✅ | ✅ | ✅ 会议开始时间 | ✅ | ✅ |
| 11 | end_time | ✅ | ✅ | ✅ 会议结束时间 | ✅ | ✅ |
| 12 | org_name | ✅ | ✅ | ✅ 支持单位 | ✅ | ✅ |
| **13** | **address** | ✅ | **✅ v2 已修** | **✅ v2 已加** | — | ✅ **v2 修复完成** |
| 14 | current_stage | ✅ | ✅ | ✅ 当前阶段 | — | ✅ 富余 |
| 15-17 | supervision_* | ✅ | ✅ | ✅ 监察段 (条件) | — | ✅ 富余 |
| 18 | invitation_url | ✅ | ✅ | ✅ 邀请函 (2 张) | ✅ | ✅ |
| 19 | schedule_url | ✅ | ✅ | ✅ 日程 (2 张) | ✅ | ✅ |
| 20 | labor_signed | ✅ | ✅ | ✅ 签署劳务 | — | ✅ 富余 |
| 21 | create_by | ✅ | ✅ | ✅ 创建人员 | ✅ | ✅ |
| 22 | create_time | ✅ | ✅ | ✅ 创建时间 | ✅ | ✅ |
| 23-24 | update_by / update_time | ✅ | ✅ | — | — | ⚠️ 内部审计字段 |
| — | (无) | ❌ | ❌ | (v2 已删) | — | ✓ 备注 dead field 已移除 |
**v2 修复**:
- `BizMeetingMapper.xml:17, 31, 69, 91, 115``address` (5 处)
- `MeetingDetail.vue:42``row.address` 字段, 替换原 `row.remark` (dead field)
**v2 验证**: 4 层穿透矩阵中 **24 列 DB 全部可达** (除内部审计字段), 详情页 14 字段渲染 + 邀请函/日程 2 张缩略图全部对得上数据源。
---
## 7. 索引检查
详情接口走 `SELECT ... FROM biz_meeting WHERE meeting_id = #{meetingId}` (精确匹配 PK)。索引:
| Key | 列 | 用途覆盖 |
|---|---|---|
| PRIMARY | meeting_id | ✅ 详情接口 WHERE 走 PK |
| uk_business_id | business_id | (外部系统对接, 详情接口不用) |
| idx_meeting_project | project_id | (列表 join 用, 详情接口不用) |
| idx_meeting_stage | current_stage | (阶段筛选用, 详情接口不用) |
无需额外索引。
---
## 8. 与原型差异 (proto/html/components/meeting-detail.html)
### 8.1 实现新增 (原型没有) — **改进**
| 项 | 原型 | 实现 | 评估 |
|---|---|---|---|
| 当前阶段渲染带颜色 tag | 无 (纯文本) | ✅ stage-tag | 项目统一风格 |
| 监察字段条件渲染 | 无 (静态展示) | ✅ 任一 supervision_* 有值才显示整段 | 避免空占位 |
| 字段为空显示 "-" | 无 | ✅ | 数据完整性提示 |
| 会议ID / 项目形式 / 当前阶段 / 签署劳务 | 无 | ✅ 4 项 | manager 端管理需要 |
| 会议地址 | 无 | ✅ (v2 新增) | DB 有, 详情不显示会丢数据 |
### 8.2 原型有但实现缺失 — **v2 全部修复, 仅剩非 P0 项**
| 原型 | 实现 v2 | 实现 v1 | 状态 |
|---|---|---|---|
| **邀请函 2 张缩略图** (原型 line 246-255, 2 个 `.preview-card` in `.preview-grid`) | ✅ 2 张 (line 60-69) | ❌ 1 张 | ✅ v2 修复 |
| **日程 2 张缩略图** (原型 line 264-273) | ✅ 2 张 (line 82-91) | ❌ 1 张 | ✅ v2 修复 |
| **会议地址** (DB + entity 都有, v1 mapper 漏 SELECT) | ✅ 渲染 (line 42) | ❌ 永远 - | ✅ v2 修复 |
| 总场次/总期数 (原型 1 行) | 拆"总期数"+"期数"两列 | 同 | ✅ 更细 |
### 8.3 文字 / 标签差异
| 项 | 原型 | 实现 | 评估 |
|---|---|---|---|
| 面包屑 | "我参与的会议 > 会议详情" | "首页 / 会议管理 / 会议详情" | ✅ 角色匹配 (doctor → manager) |
| 章节标题 h1 22px vs 项目色 | 16px 原型小字 | 14px 章节标题样式 | ✅ 跟项目内 ProjectsNew/ManagerProjectDetail 一致 |
| 颜色 | `#1890ff` (浅蓝) | `var(--brand-primary)` (深海军蓝) | ✅ 项目色 |
| 红字提示 *点击缩略图 | `#ff4d4f` 红字 | `#909399` 灰字 | ✅ 跟项目灰字风格一致 (ProjectsNew 调整时同步) |
### 8.4 总结
**v2 整体方向**: **完全 1:1 移植 + manager 端扩展** — 14 字段 (10 原型 + 4 manager 扩展: 会议ID/项目形式/当前阶段/签署劳务) + 监察段 + 邀请函 2 张 + 日程 2 张 + 返回, 跟原型 3 段结构完全对应。
---
## 9. 设计问题
### 9.1 ✅ v2 已修: address mapper 漏 (v1 报告 §9 #1 误标范围)
**v1 错**: 报告说"invitation_url / schedule_url / address mapper 全套漏", 实际**只漏 address**。
**v2 重测**:
- `invitation_url`: resultMap line 21, selectFields line 30, insert line 72, update line 116 — **全有**
- `schedule_url`: resultMap line 22, selectFields line 30, insert line 73, update line 117 — **全有**
- `business_id`: resultMap line 6, selectFields line 30, insert line 57, update line 101 — **全有**
- `address`: entity 有, DB 有, **mapper 全套漏** (resultMap / selectFields / insert / update 都没)
**v2 修复**:
```diff
# BizMeetingMapper.xml
+ resultMap: <result property="address" column="address" />
+ selectFields: ... org_name, address, current_stage ...
+ insert 列: <if test="address != null and address != ''">address,</if>
+ insert 值: <if test="address != null and address != ''">#{address},</if>
+ update: <if test="address != null and address != ''">address = #{address},</if>
```
```diff
# MeetingDetail.vue
- <el-form-item label="备注"><span>{{ row.remark || '-' }}</span></el-form-item>
+ <el-form-item label="会议地址"><span>{{ row.address || '-' }}</span></el-form-item>
```
### 9.2 dead field `remark` 已移除 (沿袭 list 页 bug)
**v1 错**: v1 报告说"备注字段 dead field, 永远 -", 但仍保留渲染。
**v2 修复**: 从 MeetingDetail.vue 删 `row.remark` 渲染 (line 42)。**DB 端仍未补 `remark` 列**, 但前端对齐 — 不再误导用户。
**遗留**: `Meetings.vue:32` 列表页筛选 `remark` 字段 + `:67` 列表列仍用 `row.remark`, 永远 `-`。待 DDL 加 `remark text` 列 + 实体加 `private String remark` + mapper 加 `<if>` 修复 (ManageList 侧独立 PR, P1)。
### 9.3 工具函数重复 (沿袭 list 页 bug)
`fmtDateTime` (MeetingDetail.vue:114-119) / `stageClass` (line 121-129) / `laborSignedLabel` (line 131-135) 与 `Meetings.vue` 里的 `fmtTime` / `stageClass` 重复 (实现几乎一致)。当前 2 处复制, 后续页面用到时建议提到 `src/utils/`
### 9.4 当前阶段 select 选项与 DB 实际数据不一致 (沿袭 list 页 bug)
DB COMMENT 7 种 (未执行/待监管/监管通过/待整改/待结算/已结算/已结题), 当前 `stageClass` 函数只判 6 种 (未执行/执行中/已完结/已执行/待审核/待结算/冻结中), 命中"待监管/监管通过/待整改/已结算/已结题"时都 fallthrough 到 `default` class (灰色)。
**修复路径**: `stageClass` 加 4 个分支 (待监管/监管通过/待整改/已结算/已结题), 跟 DB COMMENT 完全对齐。
### 9.5 数据隔离缺失 (manager 端)
`BizMeetingMapper.xml:33-36 selectByPrimaryKey``userId` 过滤 (不像 `selectList` line 49 那样), 详情接口全可见。当前 DB 9 行测试数据无风险, **生产必修**
---
## 10. 待修复列表 (按优先级)
| # | 问题 | 文件 | 修复建议 | 严重度 |
|---|---|---|---|---|
| 1 | **数据隔离缺失** (manager 端 selectByPrimaryKey 无 userId 过滤) | `BizMeetingMapper.xml:33` | 加 `<if test="params.userId != null">and exists (select 1 from biz_meeting_attendee a ...)` | **P0** (生产必修) |
| 2 | **`stageClass` 只覆盖 6 种, DB 实际 7 种** | `Meetings.vue` + `MeetingDetail.vue` | 加 4 个分支 (待监管/监管通过/待整改/已结算/已结题) → 命中非"未执行/执行中..."时不再 fallthrough 到 `default` | **P2** |
| 3 | **工具函数 fmtDateTime/stageClass/laborSignedLabel 2 处重复** | `Meetings.vue` + `MeetingDetail.vue` | 提取到 `src/utils/meeting.js` (或 `src/utils/date.js` + `src/utils/stage.js`), 2 处 import | **P3** |
| 4 | **`Meetings.vue` 列表页 `remark` 字段仍是 dead field** | `BizMeeting.java` + DDL + mapper + `Meetings.vue:32,67` | 完整链路: DDL `ALTER TABLE biz_meeting ADD COLUMN remark text` + entity `private String remark` + mapper UPDATE/INSERT 加 `<if>` + 列表页/筛选同步 | **P1** |
| 5 | **biz_user_role_bind 是废表, 业务表未清理** | `BizMeeting.java` 等 | (沿袭项目遗留, 不动) | — |
| 6 | **路由 meta.title 与页面标题对齐** | `router/index.js:66` | `meta: { title: '会议详情' }` 当前正确, 无需改 | — |
**v2 已修复**:
-`BizMeetingMapper.xml` 5 处加 `address` 字段 (resultMap / selectFields / insert 列 / insert 值 / update)
-`MeetingDetail.vue` 加 会议地址 字段渲染 (替换原 备注 dead field)
-`MeetingDetail.vue` 邀请函 preview-grid 2 张缩略图 (原 1 张)
-`MeetingDetail.vue` 日程 preview-grid 2 张缩略图 (原 1 张)
-`MeetingDetail.vue` 删 备注 字段渲染 (dead field, 跟 list 页一致)
---
## 11. 引用清单
### 前端 (本审查目标)
- `ry-vue3/src/views/manager/MeetingDetail.vue:240` — 本审查目标 (v2 修复后)
- `ry-vue3/src/views/manager/Meetings.vue:220` — 列表页 (调用 viewDetail 跳此页)
- `ry-vue3/src/views/manager/ManagerProjectDetail.vue` — 同模式参考
- `ry-vue3/src/router/index.js:66` — 路由注册
- `ry-vue3/src/layout/AdminLayout.vue:97` — manager 菜单 (无详情页菜单项)
- `ry-vue3/src/api/public.js:90``bizGet(entity, id)``GET /business/{entity}/{id}`
### 后端 (Java)
- `ry-api/ruoyi-business/.../controller/BizMeetingController.java:38-42` — GET /{meetingId}
- `ry-api/ruoyi-business/.../domain/BizMeeting.java:136` — 实体 (24 字段, 缺 remark)
- `ry-api/ruoyi-business/.../mapper/BizMeetingMapper.java:10``selectByPrimaryKey` 接口
- `ry-api/ruoyi-business/.../resources/mapper/business/BizMeetingMapper.xml` — SELECT/INSERT/UPDATE SQL (v2 加 address 5 处)
### 原型
- `proto/html/manager.html` — 链接到 `components/meeting-detail.html`
- `proto/html/components/meeting-detail.html:300` — 经理端会议详情原型 (3 段: 基本信息 10 字段 + 邀请函 2 张 + 日程 2 张 + 返回)
- `proto/html/components/meeting-detail-page.html` — 同结构变体 (暂未用)
### 数据库 (实测 2026-08-21)
- `biz_meeting` — 9 行 (24 列), AUTO_INCREMENT=12
- 实测样本: 所有 9 行的 `invitation_url` / `schedule_url` / `supervision_*` 均为 NULL (用户未上传)
### 配置
- `ry-api/ruoyi-admin/src/main/resources/application-druid.yml:9-10` — DB 连接 (root / cu2oh2co3)
---
## 附录: 修复记录
### A.1 v1 → v2 修复 (2026-08-21 session 第二次审查)
#### A.1.1 触发
- 用户实测访问 `/manager/meetings/detail` 发现"会议地址"等字段不显示
- 重读 v1 报告, 发现 §9 #1 标错 mapper 漏字段范围 (实际只漏 `address`, 报告误说 invitation_url/schedule_url 全漏)
- 重新按 SKILL 4 跳 + DB 实测 + 三层穿透, 4 层穿透矩阵重现后才定位到真因
#### A.1.2 修复 1 — `BizMeetingMapper.xml` 加 address 字段 (5 处)
```diff
@@ resultMap (line 17) @@
<result property="orgName" column="org_name" />
+ <result property="address" column="address" />
<result property="currentStage" column="current_stage" />
@@ selectFields (line 31) @@
- select meeting_id, business_id, project_id, project_no, project_name, meeting_name, period_no, total_periods, project_form, start_time, end_time, org_name, current_stage, supervision_opinion, ...
+ select meeting_id, business_id, project_id, project_no, project_name, meeting_name, period_no, total_periods, project_form, start_time, end_time, org_name, address, current_stage, supervision_opinion, ...
@@ insert 列 (line 69) @@
<if test="orgName != null and orgName != ''">org_name,</if>
+ <if test="address != null and address != ''">address,</if>
<if test="currentStage != null and currentStage != ''">current_stage,</if>
@@ insert 值 (line 91) @@
<if test="orgName != null and orgName != ''">#{orgName},</if>
+ <if test="address != null and address != ''">#{address},</if>
<if test="currentStage != null and currentStage != ''">#{currentStage},</if>
@@ update (line 115) @@
<if test="orgName != null and orgName != ''">org_name = #{orgName},</if>
+ <if test="address != null and address != ''">address = #{address},</if>
<if test="currentStage != null and currentStage != ''">current_stage = #{currentStage},</if>
```
#### A.1.3 修复 2 — `MeetingDetail.vue` 基本信息加 会议地址, 删 dead 备注
```diff
<el-row :gutter="12">
<el-col :span="12"><el-form-item label="创建时间">...</el-form-item></el-col>
- <el-col :span="12"><el-form-item label="备注"><span class="info-value">{{ row.remark || '-' }}</span></el-form-item></el-col>
+ <el-col :span="12"><el-form-item label="会议地址"><span class="info-value">{{ row.address || '-' }}</span></el-form-item></el-col>
</el-row>
```
#### A.1.4 修复 3 — `MeetingDetail.vue` 邀请函/日程 2 张缩略图
```diff
<div v-if="row.invitationUrl" class="preview-wrap">
- <a :href="row.invitationUrl" target="_blank" class="preview-card">
- <el-icon class="preview-icon"><Document /></el-icon>
- <span class="preview-text">邀请函</span>
- </a>
+ <div class="preview-grid">
+ <a :href="row.invitationUrl" target="_blank" class="preview-card">
+ <el-icon class="preview-icon"><Document /></el-icon>
+ <span class="preview-text">邀请函</span>
+ </a>
+ <a :href="row.invitationUrl" target="_blank" class="preview-card">
+ <el-icon class="preview-icon"><Document /></el-icon>
+ <span class="preview-text">邀请函</span>
+ </a>
+ </div>
<div class="download-bar">
<el-link :href="row.invitationUrl" target="_blank" type="primary">下载邀请函</el-link>
</div>
</div>
```
#### A.1.5 修复 4 — `MeetingDetail.vue` CSS 加 `.preview-grid`
```diff
+ .preview-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 16px;
+ width: 100%;
+ max-width: 720px;
+ }
.preview-card,
.preview-empty {
- width: 320px;
+ /* 由 grid 控制宽度 */
height: 180px;
...
}
```
### A.2 v1 修复 (2026-08-21 session 第一次审查)
#### A.2.1 新建 `MeetingDetail.vue` (230 行)
- 1:1 参照 `ManagerProjectDetail.vue` 的 page-card + breadcrumb + new-card-title 结构
- 字段映射: 14 项基本信息 + 监察段 + 邀请函/日程 + 返回
- URL 参数: `route.params.meetingId` (path param, 跟 `ManagerProjectDetail` 一致)
#### A.2.2 router 加路由 (`ry-vue3/src/router/index.js:66`)
```js
{ path: 'meetings/detail/:meetingId', name: 'manager-meetings-detail',
component: () => import('@/views/manager/MeetingDetail.vue'),
meta: { title: '会议详情' } },
```
#### A.2.3 `Meetings.vue` 删 dialog 改跳页面 (367 → 220 行)
- `onView(row)``viewDetail(row)`, 内部 `router.push(/manager/meetings/detail/${row.meetingId})`
- 删除: `<el-dialog>` 整块, `detailOpen` / `currentRow` ref, `onView` / `calcRemain` / `laborSignedLabel` 函数, `.meeting-detail-dialog` 系列样式, 3 个 icon imports
#### A.2.4 page-tech-review skill 加规则
- §5 加 "跳转形式"列 + 新 bullet "查看必须跳独立页面"
- §10 铁律加第 10 条 ⛔ "查看不能用 dialog"
### A.3 v1 报告 §9 #1 错误的反思
**根本原因**: v1 审查时, **未严格按 SKILL §6 流程实测 DB**, 是基于"猜测"+"部分代码阅读"得出错误结论。
- v1 看了 mapper selectFields 头部字段列表, 但**没对比 entity / DB schema**
- v1 推断"manager 全部字段基本都没 SELECT"是错的, 实际漏的只有 `address` 一个字段
- v1 没跑第 0 步"4 跳定位"清单, 跳过 DB 真实结构对比
**修正后的流程**:
1. §0 4 跳定位 (AdminLayout → router → 组件 → 原型)
2. §6.1 DB 实测 (SHOW CREATE TABLE) — 必须先做
3. §6.2 列三层穿透矩阵 (DB ↔ Entity ↔ Mapper SELECT ↔ Vue 渲染)
4. §6.3 任何字段缺失要列出真因 (DB 漏 / entity 漏 / mapper 漏 / Vue 漏)
5. §6.4 单独列"dead field"和"富余字段", 区分真丢失 vs 故意不显示
**记入 memory**: [feedback-report-scope-gaps] 升级 — 审查报告不能"差不多就行", 数据结构穿透必须 4 跳齐全 + DB 实测, **不实测就标"漏字段"是误导**