From 7205c13c7686f1faaea94839ff3e18398f5013a1 Mon Sep 17 00:00:00 2001 From: xianyi Date: Thu, 15 Jan 2026 15:24:34 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/his.ts | 14 ++ src/api/types.ts | 31 +++ src/components/exam/ExamSignPanel.tsx | 322 ++++++++++++++++++++++---- 3 files changed, 327 insertions(+), 40 deletions(-) diff --git a/src/api/his.ts b/src/api/his.ts index 2c07896..77a182e 100644 --- a/src/api/his.ts +++ b/src/api/his.ts @@ -74,6 +74,8 @@ import type { OptionalItemInfoListResponse, InputOptionalPackageRemove, OptionalPackageRemoveResponse, + InputTijianOptionItemRecordList, + TijianOptionItemRecordListResponse, } from './types'; /** @@ -577,3 +579,15 @@ export const removeOptionalPackage = ( data ).then(res => res.data); }; + +/** + * 获取体检客户可选项操作记录信息 + */ +export const getExamOptionRecordList = ( + data: InputTijianOptionItemRecordList +): Promise => { + return request.post( + `${MEDICAL_EXAM_BASE_PATH}/optional-record-list`, + data + ).then(res => res.data); +}; diff --git a/src/api/types.ts b/src/api/types.ts index 4bf8ae3..3c33478 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -1329,3 +1329,34 @@ export interface OutputOptionalPackageRemove { * 移除弃选体检套餐选项响应 */ export type OptionalPackageRemoveResponse = CommonActionResult; + +/** + * 获取体检客户可选项操作记录信息入参 + */ +export interface InputTijianOptionItemRecordList { + /** 体检ID */ + exam_id?: number; +} + +/** + * 体检可选项记录列表出参 + */ +export interface OutputTijianOptionItemRecordList { + /** 体检ID */ + exam_id?: number; + /** 组合代码 */ + combination_code?: string | null; + /** 组合名称 */ + combination_name?: string | null; + /** 套餐代码 */ + package_code?: string | null; + /** 是否弃选 默认0(1-弃选 0-选中) */ + is_abandon?: number; + /** 是否弃选名称 */ + is_abandon_name?: string | null; +} + +/** + * 获取体检客户可选项操作记录信息响应 + */ +export type TijianOptionItemRecordListResponse = CommonActionResult; diff --git a/src/components/exam/ExamSignPanel.tsx b/src/components/exam/ExamSignPanel.tsx index 795b3a2..219696f 100644 --- a/src/components/exam/ExamSignPanel.tsx +++ b/src/components/exam/ExamSignPanel.tsx @@ -3,7 +3,19 @@ import * as pdfjsLib from 'pdfjs-dist'; import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.min.mjs?url'; import type { OutputTongyishuFileInfo, OutputTijianPdfFileInfo, OutputPhysicalExamItemInfo } from '../../api'; -import { signInMedicalExamCenter, submitTongyishuSign, submitDaojiandanSign, editDaojiandanPrintStatus, submitAddItemBillSign, getTijianPdfFile, getTongyishuPdf, getDaojiandanPdf, getExamOptionalItemList, removeOptionalPackage } from '../../api'; +import { + signInMedicalExamCenter, + submitTongyishuSign, + submitDaojiandanSign, + editDaojiandanPrintStatus, + submitAddItemBillSign, + getTijianPdfFile, + getTongyishuPdf, + getDaojiandanPdf, + getExamOptionalItemList, + removeOptionalPackage, + getExamOptionRecordList, +} from '../../api'; import type { SignaturePadHandle } from '../ui'; import { Button, SignaturePad } from '../ui'; @@ -85,9 +97,20 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => { const [optionalItemList, setOptionalItemList] = useState([]); const [optionalItemLoading, setOptionalItemLoading] = useState(false); const [selectedOptionalItem, setSelectedOptionalItem] = useState(null); + // 是否已经确认过可选项目(如果后端已有记录,视为已确认) + const [optionalConfirmed, setOptionalConfirmed] = useState(false); + // 是否显示“请先确认体检项目,确认后不可修改”的提示 + const [showOptionalConfirmTip, setShowOptionalConfirmTip] = useState(false); const busy = - signLoading || submitLoading || consentLoading || pdfLoading || daojiandanSubmitLoading || addItemBillSubmitLoading || batchPrintLoading || optionalItemLoading; + signLoading || + submitLoading || + consentLoading || + pdfLoading || + daojiandanSubmitLoading || + addItemBillSubmitLoading || + batchPrintLoading || + optionalItemLoading; useEffect(() => { onBusyChange?.(busy); @@ -199,17 +222,67 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => { } }; - // 加载可选项目列表 + // 加载可选项目列表 & 操作记录(用于判断是否已选择) const loadOptionalItems = async () => { if (!examId) return; setOptionalItemLoading(true); try { - const res = await getExamOptionalItemList({ physical_exam_id: examId }); - if (res.Status === 200 && res.Data?.listOptionalItem) { - setOptionalItemList(res.Data.listOptionalItem); + const [listRes, recordRes] = await Promise.all([ + getExamOptionalItemList({ physical_exam_id: examId }), + getExamOptionRecordList({ exam_id: examId }), + ]); + + if (listRes.Status === 200 && listRes.Data?.listOptionalItem) { + const items = listRes.Data.listOptionalItem; + setOptionalItemList(items); + + // 根据操作记录判断是否已经选择过(is_abandon = 0) + let selectedFromRecord: number | null = null; + if (recordRes.Status === 200 && recordRes.Data) { + const selectedRecord = recordRes.Data.find( + (r) => r.is_abandon === 0 && r.combination_code + ); + if (selectedRecord?.combination_code) { + const codeNum = Number(selectedRecord.combination_code); + if (Number.isFinite(codeNum)) { + selectedFromRecord = codeNum; + } + } + } + + if (items.length > 0) { + if ( + selectedFromRecord != null && + items.some((i) => i.combination_code === selectedFromRecord) + ) { + // 后端已有已选记录 + setSelectedOptionalItem(selectedFromRecord); + setOptionalConfirmed(true); + setShowOptionalConfirmTip(false); + } else { + // 尚未选择,需要前端引导用户选择并确认 + setSelectedOptionalItem(null); + setOptionalConfirmed(false); + setShowOptionalConfirmTip(false); + } + } else { + // 没有可选项目,则视为无需确认 + setSelectedOptionalItem(null); + setOptionalConfirmed(true); + setShowOptionalConfirmTip(false); + } + } else { + setOptionalItemList([]); + setSelectedOptionalItem(null); + setOptionalConfirmed(true); + setShowOptionalConfirmTip(false); } } catch (err) { - console.error('获取可选项目列表失败', err); + console.error('获取可选项目/记录失败', err); + setOptionalItemList([]); + setSelectedOptionalItem(null); + setOptionalConfirmed(true); + setShowOptionalConfirmTip(false); } finally { setOptionalItemLoading(false); } @@ -222,6 +295,12 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => { }, [examId]); const handlePickFile = () => { + // 有可选项目但尚未确认时,禁止拍照并提示先确认项目 + if (optionalItemList.length > 0 && !optionalConfirmed) { + setMessage('请先确认体检项目'); + setShowOptionalConfirmTip(true); + return; + } fileInputRef.current?.click(); }; @@ -294,29 +373,11 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => { return; } - // 如果有可选项目,先移除未选中的项目 - if (optionalItemList.length > 0 && selectedOptionalItem != null) { - const unselectedCodes = optionalItemList - .map((item) => item.combination_code) - .filter((code): code is number => code != null && code !== selectedOptionalItem); - - if (unselectedCodes.length > 0) { - try { - const combinationCodeIds = unselectedCodes.join(','); - const removeRes = await removeOptionalPackage({ - physical_exam_id: examId, - combination_code_ids: combinationCodeIds, - }); - if (removeRes.Status !== 200 || removeRes.Data?.is_success !== 1) { - setMessage('移除未选项目失败:' + (removeRes.Message || '未知错误')); - return; - } - } catch (err) { - console.error('移除未选项目失败', err); - setMessage('移除未选项目失败,请稍后重试'); - return; - } - } + // 如果存在可选项目但尚未确认,则不允许直接签到 + if (optionalItemList.length > 0 && !optionalConfirmed) { + setMessage('请先确认体检项目'); + setShowOptionalConfirmTip(true); + return; } setSignLoading(true); @@ -337,7 +398,74 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => { } }; + // 确认当前选择的可选项目:移除未选择的项目 + const handleConfirmOptionalSelection = async () => { + if (!examId) { + setMessage('缺少体检ID'); + return; + } + if (optionalItemList.length === 0) { + setOptionalConfirmed(true); + setShowOptionalConfirmTip(false); + return; + } + if (selectedOptionalItem == null) { + setMessage('请选择一个可选项目'); + setShowOptionalConfirmTip(true); + return; + } + + const unselectedCodes = optionalItemList + .map((item) => item.combination_code) + .filter((code): code is number => code != null && code !== selectedOptionalItem); + + // 没有未选项目需要移除,直接标记为已确认 + if (unselectedCodes.length === 0) { + setOptionalConfirmed(true); + setShowOptionalConfirmTip(false); + setMessage('可选项目已确定'); + setTimeout(() => setMessage(null), 3000); + return; + } + + setOptionalItemLoading(true); + try { + const combinationCodeIds = unselectedCodes.join(','); + const res = await removeOptionalPackage({ + physical_exam_id: examId, + combination_code_ids: combinationCodeIds, + }); + if (res.Status === 200 && res.Data?.is_success === 1) { + setOptionalConfirmed(true); + setShowOptionalConfirmTip(false); + // 刷新剩余可选项目列表(理论上只剩一个) + const refreshRes = await getExamOptionalItemList({ physical_exam_id: examId }); + if (refreshRes.Status === 200 && refreshRes.Data?.listOptionalItem) { + setOptionalItemList(refreshRes.Data.listOptionalItem); + } + setMessage('可选项目已确定'); + setTimeout(() => setMessage(null), 3000); + } else { + setMessage('可选项目确定失败:' + (res.Message || '未知错误')); + setTimeout(() => setMessage(null), 3000); + } + } catch (err) { + console.error('可选项目确定失败', err); + setMessage('可选项目确定失败,请稍后重试'); + setTimeout(() => setMessage(null), 3000); + } finally { + setOptionalItemLoading(false); + } + }; + const handleSubmitSign = async () => { + // 有可选项目但尚未确认时,禁止签名并提示先确认项目 + if (optionalItemList.length > 0 && !optionalConfirmed) { + setSubmitMessage('请先确认体检项目'); + setShowOptionalConfirmTip(true); + return; + } + if (!examId || !previewPdf?.combination_code) { setSubmitMessage('缺少必要信息,无法提交签名'); return; @@ -1006,6 +1134,13 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => { // 导检单签名提交 const handleSubmitDaojiandanSign = async () => { + // 有可选项目但尚未确认时,禁止签名并提示先确认项目 + if (optionalItemList.length > 0 && !optionalConfirmed) { + setDaojiandanSubmitMessage('请先确认体检项目'); + setShowOptionalConfirmTip(true); + return; + } + if (!examId) { setDaojiandanSubmitMessage('缺少必要信息,无法提交签名'); return; @@ -1697,7 +1832,11 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => { 身份证预览 )} - @@ -1709,11 +1848,14 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
加载可选项目...
) : optionalItemList.length > 0 ? (
-
可选项目(必选其一):
+
+ 可选项目(必选其一){optionalConfirmed ? '(已确定)' : ''} +
{optionalItemList.map((item) => { const combinationCode = item.combination_code; - const isSelected = combinationCode != null && combinationCode === selectedOptionalItem; + const isSelected = + combinationCode != null && combinationCode === selectedOptionalItem; return (
+ {!optionalConfirmed && ( +
+ + {showOptionalConfirmTip && ( + + 请先确认体检项目,确认后不可修改 + + )} +
+ )}
) : null} @@ -1757,7 +1915,15 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => { {checkAllSigned() && ( -