17 KiB
体检管理页面设计
日期:2026-05-26 状态:待审核 需求来源:用户提出新增独立的体检管理界面
1. 背景与目标
现状
- 体检信息管理目前嵌入在中试服务申请单详情弹窗的 Tab 3 中(
ApplicationDetailDialog.vue) biz_pilot_checkup表按pilot_application_id关联,一条申请单对应一条体检记录- 后端 API 只有
get_checkup_by_application(按申请单ID查单条),无分页列表接口 - 运营人员需要进入每个申请单才能操作体检,效率低
目标
新增独立的体检管理页面,以患者为维度管理体检全流程:
- 查看所有需要体检的中试服务订单中的患者
- 批量选择患者,统一登记体检机构和体检项目
- 对单个患者上传体检报告、登记实际项目和价格
- 标记已体检、费用结算
关键数据模型变更
当前 biz_pilot_checkup 只有 pilot_application_id,没有 pilot_patient_id。需要增加该字段,使体检记录对应到具体患者。
2. 数据模型变更
2.1 biz_pilot_checkup 新增字段
ALTER TABLE biz_pilot_checkup
ADD COLUMN pilot_patient_id VARCHAR(64) DEFAULT NULL COMMENT '中试患者ID'
AFTER pilot_application_id;
影响范围:
backend/app/models/models.py—BizPilotCheckup类新增pilot_patient_id列backend/app/crud/pilot_checkup.py—_row_to_dict_checkup、create_checkup、update_checkup需处理该字段- 前端 API 和表单数据需同步
2.2 兼容性
- 现有申请单详情弹窗中的体检 Tab 继续工作(查询时
pilot_patient_id为空则按pilot_application_id匹配) - 新的体检管理页面同时使用
pilot_application_id和pilot_patient_id
3. 后端 API 变更
3.1 新增:分页列表
GET /web/admin/pilot-checkup/list
查询参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| page | int | 是 | 页码 |
| pageSize | int | 是 | 每页条数 |
| checkupStatus | string | 否 | 体检状态:dtj/ytj |
| feeStatus | string | 否 | 费用状态:wjs/yjs |
| checkupInstitutionId | string | 否 | 体检机构ID |
| applicationNo | string | 否 | 申请单号(模糊搜索) |
| patientName | string | 否 | 患者姓名(模糊搜索) |
| channelId | string | 否 | 渠道ID |
返回数据:JOIN biz_pilot_patient + biz_pilot_application + biz_checkup_institution,返回:
{
"id": "checkup_id",
"pilotApplicationId": "...",
"pilotPatientId": "...",
"applicationNo": "ZSSQ202605260001",
"channelName": "XX医院",
"patientName": "张三",
"sex": "1",
"age": 45,
"phoneNo": "138xxxx",
"checkupInstitutionId": "...",
"institutionName": "XX体检中心",
"reserveTime": "2026-05-28 10:00:00",
"reserveFee": 500.00,
"actualCheckupTime": "",
"actualCheckupFee": null,
"checkupStatus": "dtj",
"feeStatus": "wjs",
"reportAttachments": "",
"checkupRemark": "",
"createTime": "2026-05-26 14:00:00"
}
3.2 新增:未登记体检的患者列表
用于体检管理页面的"待登记"患者数据源——查询所有 need_checkup=1 但尚未创建 biz_pilot_checkup 记录的患者:
GET /web/admin/pilot-checkup/pending-patients
查询参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| page | int | 是 | 页码 |
| pageSize | int | 是 | 每页条数 |
| applicationNo | string | 否 | 申请单号(模糊搜索) |
| patientName | string | 否 | 患者姓名(模糊搜索) |
| channelId | string | 否 | 渠道ID |
返回数据:JOIN biz_pilot_patient + biz_pilot_application,返回患者信息 + 申请单信息。
3.3 新增:批量创建体检记录
POST /web/admin/pilot-checkup/batch-save
请求体:
{
"patientIds": ["patient_id_1", "patient_id_2"],
"checkupInstitutionId": "institution_id",
"reserveTime": "2026-05-28 10:00:00",
"reserveItems": [
{ "projectName": "血常规", "projectFee": 50.00 },
{ "projectName": "肝功能", "projectFee": 120.00 }
]
}
为每个 patientId 创建独立的 biz_pilot_checkup 记录 + biz_pilot_checkup_reserve_item 记录。
3.4 现有 API 适配
CheckupSaveBody新增可选字段patient_idcreate_checkup同时写入pilot_patient_id_row_to_dict_checkup返回pilotPatientId字段get_checkup_by_application保持不变(向后兼容)
4. 前端设计
4.1 路由
在 sqxp.ts 的 PilotService 路由组下新增:
{
id: '80000000000000000000000000000011',
name: 'CheckupManagement',
path: 'checkup-management',
component: '/pilot-service/checkup-management/index',
meta: {
title: '体检管理',
keepAlive: true,
menuCode: 'pilot-service:checkup-management:list'
}
}
菜单位置:「中试服务管理」→「体检管理」,排在「体检机构及项目价格」之后。
4.2 文件结构
web-admin/src/views/pilot-service/checkup-management/
├── index.vue # 主列表页
├── CheckupRegisterDialog.vue # 登记体检弹窗(批量选择患者 + 填写机构/项目/时间)
├── CheckupDetailDialog.vue # 详情弹窗(查看信息 + 上传报告 + 登记实际项目)
4.3 列表页 (index.vue)
模板参考:channel-service-order/index.vue(复杂列表模式 P07)
页面布局:
┌──────────────────────────────────────────────────┐
│ 体检管理 │
├──────────────────────────────────────────────────┤
│ 筛选条件: │
│ [体检状态▼] [费用状态▼] [体检机构▼] │
│ [申请单号____] [患者姓名____] [渠道____] │
│ [查询] [重置] │
├──────────────────────────────────────────────────┤
│ 已选 0 项 [登记体检] │
│ ───────────────────────────────────────────────── │
│ ☐ | 申请单号 | 患者 | 性别 | 年龄 | 渠道 | │
│ | 体检机构 | 预约时间 | 体检状态 | 费用状态 | │
│ | 实际费用 | 操作 │
│ │
│ [分页] │
└──────────────────────────────────────────────────┘
表格列定义:
| 列 | 字段 | 宽度 | 说明 |
|---|---|---|---|
| ☐选择 | — | 55 | type="selection",仅 checkupStatus=dtj 的行可勾选 |
| 申请单号 | applicationNo | 160 | |
| 患者姓名 | patientName | 100 | |
| 性别 | sex | 60 | el-tag:男/女 |
| 年龄 | age | 60 | |
| 渠道名称 | channelName | 140 | |
| 体检机构 | institutionName | 160 | 未登记时显示 "—" |
| 预约时间 | reserveTime | 160 | |
| 体检状态 | checkupStatus | 100 | el-tag:待体检(warning)/已体检(success) |
| 费用状态 | feeStatus | 100 | el-tag:未结算(info)/已结算(success) |
| 实际费用 | actualCheckupFee | 100 | formatMoneyDisplay |
| 操作 | — | 260 | fixed="right":查看、编辑项目、标记已体检、费用结算 |
操作按钮逻辑:
- 查看:所有状态可用 → 打开
CheckupDetailDialog(只读模式) - 编辑项目:
checkupStatus=dtj可用 → 打开CheckupDetailDialog(编辑模式) - 标记已体检:
checkupStatus=dtj可用 → 调用markPilotCheckupChecked - 费用结算:
checkupStatus=ytj且feeStatus=wjs可用 → 调用settlePilotCheckupFee - 登记体检(工具栏):选中 1+ 条
checkupStatus=dtj的记录 → 打开CheckupRegisterDialog
双数据源:
列表页有两组数据:
- 已登记的患者(有
biz_pilot_checkup记录):通过/pilot-checkup/list获取 - 未登记的患者(
need_checkup=1但无体检记录):通过/pilot-checkup/pending-patients获取
使用 Tab 切换:「全部」「待登记」「已登记」「待体检」「已体检」。
或者简化为:默认只展示已登记的(有 biz_pilot_checkup 记录),通过体检状态筛选。待登记的患者通过独立的"登记体检"按钮触发,弹窗中搜索并选择。
最终选择简化方案:列表只展示已有体检记录的患者。工具栏"登记体检"按钮打开弹窗,弹窗内搜索并勾选未登记的患者。
4.4 登记体检弹窗 (CheckupRegisterDialog.vue)
交互流程:
┌─ 登记体检 ─────────────────────────────────────┐
│ │
│ 搜索患者:[申请单号/患者姓名________] [搜索] │
│ │
│ ☐ | 申请单号 | 患者姓名 | 性别 | 年龄 | 渠道 | │
│ ☑ | ZSSQ...001 | 张三 | 男 | 45 | XX医院 | │
│ ☑ | ZSSQ...001 | 李四 | 女 | 52 | XX医院 | │
│ ☐ | ZSSQ...002 | 王五 | 男 | 38 | YY医院 | │
│ │
│ 已选 2 名患者 │
│ │
│ ── 体检信息 ─────────────────────────────────── │
│ │
│ 体检机构: [选择体检机构 ▼] │
│ 预约时间: [2026-05-28 10:00 🕐] │
│ │
│ ── 体检项目 ─────────────────────────────────── │
│ [从套餐添加] [添加单项] │
│ │
│ | 项目名称 | 项目费用 | 操作 | │
│ | 血常规 | ¥50.00 | [删除] | │
│ | 肝功能 | ¥120.00 | [删除] | │
│ │
│ 预约费用合计:¥170.00 │
│ │
│ [取消] [确定] │
└──────────────────────────────────────────────────┘
关键组件交互:
- 搜索患者:调用
/pilot-checkup/pending-patients接口,搜索need_checkup=1且无体检记录的患者 - 选择体检机构:下拉选择,数据来自
fetchCheckupInstitutionList(现有 API) - 从套餐添加:选择机构后,展示该机构的套餐列表,选择套餐后自动填充项目
- 添加单项:展示该机构的所有项目列表,多选后添加
- 保存:调用
/pilot-checkup/batch-save,为每个选中的患者创建体检记录 + 预约项目
项目选择器(复用 CheckupPackagePanel.vue 的模式):
- Tab 1:套餐选择 — 按机构筛选套餐 → 选择套餐 → 自动填充项目
- Tab 2:单项添加 — 按机构筛选项目 → 多选添加
4.5 详情弹窗 (CheckupDetailDialog.vue)
模板参考:ApplicationDetailDialog.vue 的体检 Tab 部分
布局:
┌─ 体检详情 ─ 张三 ──────────────────────────────┐
│ │
│ ── 基本信息 ────────────────────────────────── │
│ 申请单号:ZSSQ202605260001 │
│ 患者姓名:张三 性别:男 年龄:45 │
│ 渠道名称:XX医院 │
│ 体检机构:XX体检中心 │
│ 预约时间:2026-05-28 10:00 │
│ 体检状态:[待体检] 费用状态:[未结算] │
│ │
│ ── 预约项目 ────────────────────────────────── │
│ | 项目名称 | 项目费用 | │
│ | 血常规 | ¥50.00 | │
│ | 肝功能 | ¥120.00 | │
│ 合计:¥170.00 │
│ │
│ ── 实际项目 ───────────────── [添加单项] ────── │
│ | 项目名称 | 项目费用 | 操作 | │
│ | 血常规 | ¥50.00 | [删除] | │
│ | 肝功能 | ¥120.00 | [删除] | │
│ | 胸部CT | ¥300.00 | [删除] | │
│ 合计:¥470.00 │
│ │
│ ── 体检报告 ────────────────────────────────── │
│ [上传附件] [报告1.pdf] [报告2.jpg] │
│ │
│ ── 备注 ────────────────────────────────────── │
│ 患者体检一切正常... │
│ │
│ [标记已体检] [费用结算] [取消] │
└──────────────────────────────────────────────────┘
交互:
- 打开时加载
fetchPilotCheckupDetail+fetchPilotCheckupReserveItems+fetchPilotCheckupActualItems - 实际项目可增删,调用现有
savePilotCheckupActualItems/deletePilotCheckupActualItem - 附件上传使用卡片化上传模式(参考
ProtocolFormDialog.vue),调用fetchUploadAttachment - 标记已体检 / 费用结算复用现有 API
4.6 前端 API 新增
// web-admin/src/api/admin.ts
// 体检记录分页列表
export const fetchPilotCheckupList = (params: any) =>
request.get({ url: '/web/admin/pilot-checkup/list', params })
// 待登记体检的患者列表
export const fetchPilotCheckupPendingPatients = (params: any) =>
request.get({ url: '/web/admin/pilot-checkup/pending-patients', params })
// 批量创建体检记录
export const batchSavePilotCheckup = (data: any) =>
request.post({ url: '/web/admin/pilot-checkup/batch-save', params: data })
5. 改动范围总结
后端
| 文件 | 改动 |
|---|---|
app/models/models.py |
BizPilotCheckup 新增 pilot_patient_id 字段 |
app/crud/pilot_checkup.py |
_row_to_dict_checkup 返回新字段;新增 list_checkups_paginated、list_pending_patients、batch_create_checkups 函数 |
app/api/pilot_checkup.py |
新增 3 个 API 端点;CheckupSaveBody 新增 patient_id 字段 |
docs/sql/ |
新增迁移 SQL |
前端
| 文件 | 改动 |
|---|---|
src/views/pilot-service/checkup-management/index.vue |
新建 — 主列表页 |
src/views/pilot-service/checkup-management/CheckupRegisterDialog.vue |
新建 — 登记体检弹窗 |
src/views/pilot-service/checkup-management/CheckupDetailDialog.vue |
新建 — 详情弹窗 |
src/api/admin.ts |
新增 3 个 API 函数 |
src/router/modules/sqxp.ts |
新增路由 |
复用的现有代码
status-tag-registry.ts—checkupStatusLabel/checkupStatusTagType(现有)fetchCheckupInstitutionList— 体检机构列表 API(现有)fetchCheckupInstitutionProjects— 机构项目列表 API(现有)fetchCheckupInstitutionPackages— 机构套餐列表 API(现有)fetchUploadAttachment/fetchAttachmentDownloadUrl— 附件上传下载(现有)savePilotCheckupReserveItems/savePilotCheckupActualItems— 预约/实际项目保存(现有)markPilotCheckupChecked/settlePilotCheckupFee— 状态变更(现有)formatMoneyDisplay/AmountInput— 金额显示/输入(现有)
6. 不做的事
- 不修改现有申请单详情弹窗中的体检 Tab(保持向后兼容)
- 不新增数据库表(复用现有 3 张体检表 + 增加一个字段)
- 不做体检结果录入(如检查指标、异常标记等)——仅管理项目、费用和报告附件
- 不做自动费用计算——实际费用由运营手动录入