For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 新增独立的体检管理页面,以患者为维度管理中试服务体检全流程(登记、上传报告、费用结算)。
Architecture: 在现有 biz_pilot_checkup 表新增 pilot_patient_id 字段实现患者维度关联。后端新增 3 个 API(分页列表、待登记患者、批量创建),前端新增 3 个 Vue 文件(列表页 + 登记弹窗 + 详情弹窗)。
Tech Stack: FastAPI + SQLAlchemy(后端)、Vue 3 + Element Plus + TypeScript(前端)
Design Spec: docs/superpowers/specs/2026-05-26-checkup-management-page-design.md
| 文件 | 操作 | 职责 |
|---|---|---|
docs/sql/2026-05-26-add-pilot-patient-id-to-checkup.sql |
新建 | ALTER TABLE 迁移 SQL |
backend/app/models/models.py:2913-2938 |
修改 | BizPilotCheckup 新增 pilot_patient_id 字段 |
backend/app/crud/pilot_checkup.py |
修改 | 新增 3 个函数 + 更新现有 _row_to_dict_checkup/create_checkup |
backend/app/api/pilot_checkup.py |
修改 | 新增 3 个端点 + 更新 CheckupSaveBody |
| 文件 | 操作 | 职责 |
|---|---|---|
web-admin/src/api/admin.ts |
修改 | 新增 3 个 API 函数 |
web-admin/src/router/modules/sqxp.ts |
修改 | 新增路由 |
web-admin/src/views/pilot-service/checkup-management/index.vue |
新建 | 主列表页 |
web-admin/src/views/pilot-service/checkup-management/CheckupRegisterDialog.vue |
新建 | 批量登记体检弹窗 |
web-admin/src/views/pilot-service/checkup-management/CheckupDetailDialog.vue |
新建 | 详情弹窗(报告上传 + 实际项目) |
Files:
Create: docs/sql/2026-05-26-add-pilot-patient-id-to-checkup.sql
[ ] Step 1: 创建迁移 SQL 文件
-- 2026-05-26: biz_pilot_checkup 新增 pilot_patient_id 字段
-- 用于体检管理页面按患者维度管理体检记录
ALTER TABLE biz_pilot_checkup
ADD COLUMN pilot_patient_id VARCHAR(64) DEFAULT NULL COMMENT '中试患者ID'
AFTER pilot_application_id;
-- 为已有数据补充 pilot_patient_id(通过申请单关联患者)
-- 注意:一个申请单可能对应多个患者,这里仅标记第一个需要体检的患者
-- 新数据将通过 API 正确设置
CREATE INDEX idx_pilot_checkup_patient_id ON biz_pilot_checkup(pilot_patient_id);
cd backend && source venv/bin/activate
python -c "
from app.core.database import engine
from sqlalchemy import text
with engine.connect() as conn:
conn.execute(text('ALTER TABLE biz_pilot_checkup ADD COLUMN pilot_patient_id VARCHAR(64) DEFAULT NULL COMMENT \"中试患者ID\" AFTER pilot_application_id'))
conn.execute(text('CREATE INDEX idx_pilot_checkup_patient_id ON biz_pilot_checkup(pilot_patient_id)'))
conn.commit()
print('Migration done')
"
git add docs/sql/2026-05-26-add-pilot-patient-id-to-checkup.sql
git commit -m "feat(checkup): add pilot_patient_id column to biz_pilot_checkup table"
Files:
Modify: backend/app/models/models.py (line ~2913, BizPilotCheckup class)
[ ] Step 1: 在 BizPilotCheckup 类中新增字段
在 pilot_application_id 字段之后(约 line 2919 后)添加:
pilot_patient_id = Column(String(64), nullable=True, comment="中试患者ID")
完整上下文:找到 BizPilotCheckup 类中的 pilot_application_id 行,在其下一行添加 pilot_patient_id。
cd backend && source venv/bin/activate
python -c "from app.models.models import BizPilotCheckup; print([c.name for c in BizPilotCheckup.__table__.columns])"
预期输出包含 pilot_patient_id。
git add backend/app/models/models.py
git commit -m "feat(checkup): add pilot_patient_id to BizPilotCheckup model"
Files:
Modify: backend/app/crud/pilot_checkup.py
[ ] Step 1: 更新 _row_to_dict_checkup 函数 — 添加 pilotPatientId 字段
在 _row_to_dict_checkup 函数的字典构建中,"pilotApplicationId" 行之后添加:
"pilotPatientId": row.pilot_patient_id or "",
同时,在 if db and tenant_no: 块内(institutionName 查找之后),添加患者信息关联查询:
# patientName / sex / age / phoneNo — from BizPilotPatient
from app.models.models import BizPilotPatient, BizPilotApplication
if row.pilot_patient_id:
patient = (
db.query(BizPilotPatient)
.filter(
BizPilotPatient.id == row.pilot_patient_id,
BizPilotPatient.del_flag == 0,
)
.first()
)
if patient:
d["patientName"] = patient.patient_name or ""
d["sex"] = patient.sex or ""
d["age"] = patient.age
d["phoneNo"] = patient.phone_no or ""
else:
d["patientName"] = ""
d["sex"] = ""
d["age"] = None
d["phoneNo"] = ""
else:
d["patientName"] = ""
d["sex"] = ""
d["age"] = None
d["phoneNo"] = ""
# applicationNo / channelName — from BizPilotApplication
if row.pilot_application_id:
app = (
db.query(BizPilotApplication)
.filter(
BizPilotApplication.id == row.pilot_application_id,
BizPilotApplication.del_flag == 0,
)
.first()
)
if app:
d["applicationNo"] = app.application_no or ""
d["channelName"] = app.channel_name or ""
else:
d["applicationNo"] = ""
d["channelName"] = ""
else:
d["applicationNo"] = ""
d["channelName"] = ""
注意:from app.models.models import BizPilotPatient, BizPilotApplication 已在文件顶部 import 区域(如果不存在需添加)。当前文件顶部已有:
from app.models.models import (
BizCheckupInstitution,
BizPilotCheckup,
BizPilotCheckupActualItem,
BizPilotCheckupReserveItem,
)
需要改为:
from app.models.models import (
BizCheckupInstitution,
BizPilotApplication,
BizPilotCheckup,
BizPilotCheckupActualItem,
BizPilotCheckupReserveItem,
BizPilotPatient,
)
create_checkup 函数 — 支持 pilot_patient_id在 create_checkup 函数中,BizPilotCheckup(...) 构造参数里添加:
pilot_patient_id=data.get("pilotPatientId") or None,
list_checkups_paginated 函数在文件末尾(delete_actual_item 函数之后)添加:
# ────────────────────────────────────────────
# Checkup List (Paginated)
# ────────────────────────────────────────────
def list_checkups_paginated(
db: Session,
tenant_no: str,
page: int = 1,
page_size: int = 10,
checkup_status: Optional[str] = None,
fee_status: Optional[str] = None,
checkup_institution_id: Optional[str] = None,
application_no: Optional[str] = None,
patient_name: Optional[str] = None,
channel_id: Optional[str] = None,
) -> Dict[str, Any]:
q = db.query(BizPilotCheckup).filter(
BizPilotCheckup.tenant_no == tenant_no,
BizPilotCheckup.del_flag == 0,
)
if checkup_status:
statuses = [s.strip() for s in checkup_status.split(",") if s.strip()]
if statuses:
q = q.filter(BizPilotCheckup.checkup_status.in_(statuses))
if fee_status:
statuses = [s.strip() for s in fee_status.split(",") if s.strip()]
if statuses:
q = q.filter(BizPilotCheckup.fee_status.in_(statuses))
if checkup_institution_id:
q = q.filter(BizPilotCheckup.checkup_institution_id == checkup_institution_id)
if application_no:
app_subq = (
db.query(BizPilotApplication.id)
.filter(
BizPilotApplication.application_no.like(f"%{application_no}%"),
BizPilotApplication.del_flag == 0,
)
.subquery()
)
q = q.filter(BizPilotCheckup.pilot_application_id.in_(app_subq))
if patient_name:
patient_subq = (
db.query(BizPilotPatient.id)
.filter(
BizPilotPatient.patient_name.like(f"%{patient_name}%"),
BizPilotPatient.del_flag == 0,
)
.subquery()
)
q = q.filter(BizPilotCheckup.pilot_patient_id.in_(patient_subq))
if channel_id:
app_subq = (
db.query(BizPilotApplication.id)
.filter(
BizPilotApplication.channel_id == channel_id,
BizPilotApplication.del_flag == 0,
)
.subquery()
)
q = q.filter(BizPilotCheckup.pilot_application_id.in_(app_subq))
total = q.order_by(None).with_entities(func.count(BizPilotCheckup.id)).scalar() or 0
page = max(1, page)
page_size = min(max(1, page_size), 100)
rows = (
q.order_by(BizPilotCheckup.create_time.desc())
.offset((page - 1) * page_size)
.limit(page_size)
.all()
)
items = [_row_to_dict_checkup(r, db, tenant_no) for r in rows]
return {
"list": items,
"pagination": {"page": page, "pageSize": page_size, "total": total},
}
list_pending_patients 函数紧接上面函数之后添加:
def list_pending_patients(
db: Session,
tenant_no: str,
page: int = 1,
page_size: int = 10,
application_no: Optional[str] = None,
patient_name: Optional[str] = None,
channel_id: Optional[str] = None,
) -> Dict[str, Any]:
"""查询 need_checkup=1 但尚未有 biz_pilot_checkup 记录的患者"""
# 已有 checkup 的 patient_id 集合
checked_patient_ids = (
db.query(BizPilotCheckup.pilot_patient_id)
.filter(
BizPilotCheckup.tenant_no == tenant_no,
BizPilotCheckup.del_flag == 0,
BizPilotCheckup.pilot_patient_id.isnot(None),
BizPilotCheckup.pilot_patient_id != "",
)
.subquery()
)
q = db.query(BizPilotPatient).filter(
BizPilotPatient.need_checkup == 1,
BizPilotPatient.tenant_no == tenant_no,
BizPilotPatient.del_flag == 0,
~BizPilotPatient.id.in_(checked_patient_ids),
)
if patient_name:
q = q.filter(BizPilotPatient.patient_name.like(f"%{patient_name}%"))
if application_no or channel_id:
app_q = db.query(BizPilotApplication.id).filter(
BizPilotApplication.tenant_no == tenant_no,
BizPilotApplication.del_flag == 0,
)
if application_no:
app_q = app_q.filter(
BizPilotApplication.application_no.like(f"%{application_no}%")
)
if channel_id:
app_q = app_q.filter(BizPilotApplication.channel_id == channel_id)
app_subq = app_q.subquery()
q = q.filter(BizPilotPatient.pilot_application_id.in_(app_subq))
total = q.order_by(None).with_entities(func.count(BizPilotPatient.id)).scalar() or 0
page = max(1, page)
page_size = min(max(1, page_size), 100)
rows = (
q.order_by(BizPilotPatient.create_time.desc())
.offset((page - 1) * page_size)
.limit(page_size)
.all()
)
items = []
for r in rows:
d = {
"id": r.id,
"pilotApplicationId": r.pilot_application_id or "",
"patientName": r.patient_name or "",
"sex": r.sex or "",
"age": r.age,
"phoneNo": r.phone_no or "",
}
# 关联申请单信息
app = (
db.query(BizPilotApplication)
.filter(
BizPilotApplication.id == r.pilot_application_id,
BizPilotApplication.del_flag == 0,
)
.first()
)
if app:
d["applicationNo"] = app.application_no or ""
d["channelName"] = app.channel_name or ""
d["channelId"] = app.channel_id or ""
else:
d["applicationNo"] = ""
d["channelName"] = ""
d["channelId"] = ""
items.append(d)
return {
"list": items,
"pagination": {"page": page, "pageSize": page_size, "total": total},
}
batch_create_checkups 函数紧接上面函数之后添加:
def batch_create_checkups(
db: Session,
patient_ids: list,
checkup_institution_id: str,
reserve_time: Optional[str],
reserve_items: list,
tenant_no: str,
user_id: str,
) -> list:
"""为多个患者批量创建体检记录 + 预约项目"""
results = []
now = datetime.now()
for patient_id in patient_ids:
# 查找患者信息以获取 application_id
patient = (
db.query(BizPilotPatient)
.filter(
BizPilotPatient.id == patient_id,
BizPilotPatient.tenant_no == tenant_no,
BizPilotPatient.del_flag == 0,
)
.first()
)
if not patient:
continue
# 创建 checkup 主记录
checkup_row = BizPilotCheckup(
id=uuid.uuid4().hex,
pilot_application_id=patient.pilot_application_id,
pilot_patient_id=patient_id,
checkup_institution_id=checkup_institution_id or "",
reserve_time=reserve_time or None,
reserve_fee=sum(
float(item.get("projectFee") or 0) for item in reserve_items
) if reserve_items else None,
actual_checkup_time=None,
actual_checkup_fee=None,
fee_settled=0,
checkup_status="dtj",
fee_status="wjs",
checkup_remark="",
report_attachments="",
tenant_no=tenant_no,
order_num=0,
remark="",
create_by=user_id,
create_dept_id="",
create_time=now,
update_by=user_id,
update_time=now,
del_flag=0,
)
db.add(checkup_row)
db.flush() # 确保 checkup_row.id 可用
# 创建预约项目
for idx, item in enumerate(reserve_items):
ri = BizPilotCheckupReserveItem(
id=uuid.uuid4().hex,
pilot_checkup_id=checkup_row.id,
project_name=item.get("projectName") or item.get("project_name") or "",
project_fee=item.get("projectFee") if item.get("projectFee") is not None else (
item.get("project_fee") if item.get("project_fee") is not None else None
),
tenant_no=tenant_no,
order_num=idx + 1,
remark="",
create_by=user_id,
create_dept_id="",
create_time=now,
update_by=user_id,
update_time=now,
del_flag=0,
)
db.add(ri)
results.append(_row_to_dict_checkup(checkup_row, db, tenant_no))
db.commit()
return results
cd backend && source venv/bin/activate
python -c "from app.crud import pilot_checkup; print([f for f in dir(pilot_checkup) if not f.startswith('_')])"
预期输出包含 list_checkups_paginated、list_pending_patients、batch_create_checkups。
git add backend/app/crud/pilot_checkup.py
git commit -m "feat(checkup): add paginated list, pending patients, and batch create CRUD functions"
Files:
Modify: backend/app/api/pilot_checkup.py
[ ] Step 1: 更新 CheckupSaveBody — 新增 patient_id 字段
在 CheckupSaveBody 类中添加:
patient_id: Optional[str] = None
api_save_checkup — 传递 patient_id在 api_save_checkup 函数中,data 字典构建添加:
"pilotPatientId": body.patient_id,
BatchCheckupSaveBody Schema在 Pydantic Schemas 区域(CheckupItemsSaveBody 之后)添加:
class BatchCheckupSaveBody(BaseModel):
patientIds: List[str]
checkupInstitutionId: Optional[str] = None
reserveTime: Optional[str] = None
reserveItems: Optional[List[CheckupItemBody]] = None
api_list_checkups 端点在"1. 体检信息 CRUD"区域末尾(api_settle_fee 之后)添加:
@router.get("/pilot-checkup/list")
async def api_list_checkups(
page: int = Query(1, ge=1),
pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
checkupStatus: Optional[str] = Query(None, alias="checkupStatus"),
feeStatus: Optional[str] = Query(None, alias="feeStatus"),
checkupInstitutionId: Optional[str] = Query(None, alias="checkupInstitutionId"),
applicationNo: Optional[str] = Query(None, alias="applicationNo"),
patientName: Optional[str] = Query(None, alias="patientName"),
channelId: Optional[str] = Query(None, alias="channelId"),
user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
tenant_no = _get_tenant_no(db, user)
result = pilot_checkup.list_checkups_paginated(
db,
tenant_no=tenant_no,
page=page,
page_size=pageSize,
checkup_status=checkupStatus,
fee_status=feeStatus,
checkup_institution_id=checkupInstitutionId,
application_no=applicationNo,
patient_name=patientName,
channel_id=channelId,
)
return success_response(result)
api_list_pending_patients 端点@router.get("/pilot-checkup/pending-patients")
async def api_list_pending_patients(
page: int = Query(1, ge=1),
pageSize: int = Query(10, ge=1, le=100, alias="pageSize"),
applicationNo: Optional[str] = Query(None, alias="applicationNo"),
patientName: Optional[str] = Query(None, alias="patientName"),
channelId: Optional[str] = Query(None, alias="channelId"),
user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
tenant_no = _get_tenant_no(db, user)
result = pilot_checkup.list_pending_patients(
db,
tenant_no=tenant_no,
page=page,
page_size=pageSize,
application_no=applicationNo,
patient_name=patientName,
channel_id=channelId,
)
return success_response(result)
api_batch_save_checkup 端点@router.post("/pilot-checkup/batch-save")
async def api_batch_save_checkup(
body: BatchCheckupSaveBody,
user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
try:
tenant_no = _get_tenant_no(db, user)
items_json = [item.dict() for item in body.reserveItems] if body.reserveItems else []
result = pilot_checkup.batch_create_checkups(
db,
patient_ids=body.patientIds,
checkup_institution_id=body.checkupInstitutionId or "",
reserve_time=body.reserveTime or None,
reserve_items=items_json,
tenant_no=tenant_no,
user_id=user.get("user_id"),
)
return success_response(result, message=f"成功为 {len(result)} 名患者创建体检记录")
except ValueError as e:
return error_response(400, str(e))
cd backend && source venv/bin/activate
python -c "from app.api.pilot_checkup import router; routes = [r.path for r in router.routes]; print(routes)"
预期输出包含 /web/admin/pilot-checkup/list、/web/admin/pilot-checkup/pending-patients、/web/admin/pilot-checkup/batch-save。
git add backend/app/api/pilot_checkup.py
git commit -m "feat(checkup): add list, pending-patients, and batch-save API endpoints"
Files:
Modify: web-admin/src/api/admin.ts
[ ] Step 1: 在"中试服务 - 体检管理"区域末尾(deletePilotCheckupActualItem 之后)添加 3 个新函数
// 体检记录分页列表
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 })
cd web-admin && npx tsc --noEmit src/api/admin.ts 2>&1 | head -5
git add web-admin/src/api/admin.ts
git commit -m "feat(checkup): add frontend API functions for checkup management page"
Files:
Modify: web-admin/src/router/modules/sqxp.ts
[ ] Step 1: 在 PilotService 的 children 数组末尾(PilotApplication 路由之后,即约 line 710 的 } 之前)添加新路由
{
id: '80000000000000000000000000000011',
name: 'CheckupManagement',
path: 'checkup-management',
component: '/pilot-service/checkup-management/index',
meta: {
title: '体检管理',
keepAlive: true,
menuCode: 'pilot-service:checkup-management:list'
}
}
git add web-admin/src/router/modules/sqxp.ts
git commit -m "feat(checkup): add checkup management route"
index.vueFiles:
web-admin/src/views/pilot-service/checkup-management/index.vue参考模板: pilot-service/channel-service-order/index.vue(复杂列表)+ checkup-institution/index.vue(同模块风格)
mkdir -p web-admin/src/views/pilot-service/checkup-management
index.vue页面结构要求(遵循 AGENTS.md UI 规范):
page-container > page-head > h1 标题"体检管理"panel > panel-top > h2 "体检记录"filter-block > filter-grid(2行4列筛选项)list-toolbar:已选 N 项 + 「登记体检」按钮el-table 带 type="selection" 列el-paginationfixed="right" min-width ≥ 260筛选项:
el-select:全部/待体检/已体检)el-select:全部/未结算/已结算)el-select,调用 fetchCheckupInstitutionList 加载选项)el-input)el-input)表格列(min-width,无 width):
| 列 | prop | 说明 |
|---|---|---|
| ☐选择 | — | type="selection" |
| 序号 | — | type="index" |
| 申请单号 | applicationNo | |
| 患者姓名 | patientName | |
| 性别 | sex | 显示"男"/"女" |
| 年龄 | age | |
| 渠道名称 | channelName | |
| 体检机构 | institutionName | 未登记显示"—" |
| 预约时间 | reserveTime | |
| 体检状态 | checkupStatus | el-tag:checkupStatusTagType |
| 费用状态 | feeStatus | el-tag:待结算 info / 已结算 success |
| 实际费用 | actualCheckupFee | formatMoneyDisplay |
| 操作 | — | 查看 / 标记已体检 / 费用结算 |
操作按钮条件:
CheckupDetailDialogcheckupStatus === 'dtj' 可用checkupStatus === 'ytj' && feeStatus === 'wjs' 可用工具栏「登记体检」按钮:selectedRows.length > 0 且所有选中行 checkupStatus === 'dtj' 时可用,点击打开 CheckupRegisterDialog。
API 调用:
fetchPilotCheckupListfetchCheckupInstitutionListmarkPilotCheckupCheckedsettlePilotCheckupFee复用工具:
checkupStatusLabel / checkupStatusTagType from @/utils/status-tag-registryformatMoneyDisplay from @/utils/format
[ ] Step 3: 验证页面加载
启动 dev server,访问 /#/pilot-service/checkup-management,确认页面正常渲染。
git add web-admin/src/views/pilot-service/checkup-management/index.vue
git commit -m "feat(checkup): create checkup management list page"
CheckupRegisterDialog.vueFiles:
web-admin/src/views/pilot-service/checkup-management/CheckupRegisterDialog.vue参考模式: CheckupPackagePanel.vue(项目选择器模式)
组件结构:
el-dialog title="登记体检" width="1380px" class="dialog-form-layout" destroy-on-close患者选择区:
el-table 带 type="selection":展示 fetchPilotCheckupPendingPatients 数据体检信息区(form-grid-2):
el-select 调用 fetchCheckupInstitutionList,选择后加载该机构的项目和套餐el-date-picker type="datetime"项目选择区:
el-tabs 两个 Tab:「从套餐添加」和「添加单项」el-select 选套餐 → 点击"加载套餐项目" → 自动填充到项目列表el-button "添加项目" → 打开项目选择器(el-table 带 selection 展示该机构所有项目)el-table 显示项目名称 + 费用,可删除保存逻辑:
batchSavePilotCheckup 传入 { patientIds, checkupInstitutionId, reserveTime, reserveItems }emit('success') 并关闭弹窗API 调用:
fetchPilotCheckupPendingPatients — 搜索患者fetchCheckupInstitutionList — 机构下拉fetchCheckupInstitutionProjects(institutionId) — 机构项目列表fetchCheckupInstitutionPackages(institutionId) — 机构套餐列表fetchCheckupInstitutionPackageDetail(packageId) — 套餐详情(含项目)batchSavePilotCheckup — 批量保存
[ ] Step 2: 在 index.vue 中引入并使用
在 index.vue 中 import 并注册 CheckupRegisterDialog,添加 v-model 绑定和 @success 回调(回调刷新列表)。
git add web-admin/src/views/pilot-service/checkup-management/
git commit -m "feat(checkup): create batch checkup register dialog"
CheckupDetailDialog.vueFiles:
web-admin/src/views/pilot-service/checkup-management/CheckupDetailDialog.vue参考模式: ApplicationDetailDialog.vue 体检 Tab 部分(约 line 2900-3500)
组件结构:
el-dialog title="体检详情 — {patientName}" width="1200px" class="dialog-form-layout" destroy-on-close基本信息区(只读展示,form-grid-2):
el-tag、费用状态 el-tag预约项目区(只读):
el-table 展示预约项目列表(项目名称 + 费用)实际项目区(可编辑):
el-button "添加单项" → 内联输入行(项目名称 input + 费用 input + 确认按钮)el-table 展示实际项目列表(项目名称 + 费用 + 删除操作)体检报告区(卡片化上传,参考 ProtocolFormDialog.vue):
el-upload 触发器为虚线卡片(140×96px, dashed border #a7f3d0)fetchUploadAttachment,URL 以逗号分隔存储到 reportAttachments备注区:
el-input type="textarea" v-model 绑定 checkupRemark底部操作按钮(根据状态显示):
checkupStatus === 'dtj' 时显示checkupStatus === 'ytj' && feeStatus === 'wjs' 时显示API 调用:
fetchPilotCheckupDetail({ application_id }) + fetchPilotCheckupReserveItems + fetchPilotCheckupActualItemssavePilotCheckupActualItemssavePilotCheckupmarkPilotCheckupCheckedsettlePilotCheckupFeefetchUploadAttachment下载附件:fetchAttachmentDownloadUrl
[ ] Step 2: 在 index.vue 中引入并使用
在 index.vue 中 import 并注册 CheckupDetailDialog,点击"查看"或行双击时打开。
git add web-admin/src/views/pilot-service/checkup-management/
git commit -m "feat(checkup): create checkup detail dialog with report upload"
cd backend && source venv/bin/activate && ./start-local.sh
cd web-admin && pnpm dev
/#/pilot-service/checkup-management,列表页正常加载# 后端
cd backend && python -c "from app.api.pilot_checkup import router; print('OK')"
# 前端 — 在 IDE 中检查 index.vue 无报错
git add -A
git commit -m "feat(checkup): complete checkup management page with register, detail, and report upload"