From 4741a437a4d7c3ff77e09d49b16c241c3f06afc9 Mon Sep 17 00:00:00 2001 From: xianyi Date: Mon, 15 Dec 2025 15:29:51 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=86=E7=A6=BB=E4=BD=93=E6=A3=80=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E9=9D=A2=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/exam/ExamDetailPanel.tsx | 373 ++++++++++++++++++++++++ src/components/exam/ExamModal.tsx | 368 +---------------------- 2 files changed, 378 insertions(+), 363 deletions(-) create mode 100644 src/components/exam/ExamDetailPanel.tsx diff --git a/src/components/exam/ExamDetailPanel.tsx b/src/components/exam/ExamDetailPanel.tsx new file mode 100644 index 0000000..e84f47c --- /dev/null +++ b/src/components/exam/ExamDetailPanel.tsx @@ -0,0 +1,373 @@ +import { useMemo, useState } from 'react'; + +import type { + CustomerAppointmentInfo, + CustomerExamAddItem, + CustomerInfo, + PhysicalExamProgressItem, +} from '../../api'; +import { editCustomerDetail } from '../../api'; +import type { ExamClient } from '../../data/mockData'; +import { Button, Input } from '../ui'; + +interface ExamDetailPanelProps { + client: ExamClient; + customerInfo: CustomerInfo | null; + appointmentInfo: CustomerAppointmentInfo | null; + addItemInfoList: CustomerExamAddItem[] | null; + progressList: PhysicalExamProgressItem[] | null; + loading: boolean; +} + +const getMaritalCodeFromText = (text: string): number => { + if (text.includes('未婚') || text === '未婚') return 10; + if (text.includes('已婚') || text === '已婚') return 20; + return 20; +}; + +const getMaritalText = (code: number): string => (code === 10 ? '未婚' : '已婚'); + +export const ExamDetailPanel = ({ + client, + customerInfo, + appointmentInfo, + addItemInfoList, + progressList, + loading, +}: ExamDetailPanelProps) => { + const basePhone = customerInfo?.phone || (client['mobile' as keyof ExamClient] as string | undefined) || ''; + const baseMaritalText = + customerInfo?.patient_marital_status_name || + (client['maritalStatus' as keyof ExamClient] as string | undefined) || + '—'; + const baseMaritalCode = baseMaritalText === '—' ? 20 : getMaritalCodeFromText(baseMaritalText); + + const [phone, setPhone] = useState(basePhone || '—'); + const [maritalCode, setMaritalCode] = useState(baseMaritalCode); + const [phoneEditing, setPhoneEditing] = useState(false); + const [maritalEditing, setMaritalEditing] = useState(false); + const [editLoading, setEditLoading] = useState(false); + const [editMessage, setEditMessage] = useState(null); + + const customerChannel = client.customerType === '团客' ? '团体客户' : '散客客户'; + const familyDoctor = customerInfo?.family_doctor_name || (client['familyDoctor' as keyof ExamClient] as string | undefined) || '—'; + const groupTag = client['groupTag' as keyof ExamClient] || (client.customerType === '团客' ? '团检' : '—'); + const bookingTime = appointmentInfo?.appointment_time || (client['bookingTime' as keyof ExamClient] || '—'); + const signTime = appointmentInfo?.sign_in_time || (client['signTime' as keyof ExamClient] || '—'); + const addonSummary = + addItemInfoList && addItemInfoList.length > 0 + ? addItemInfoList.map((i) => `${i.dept_name ?? ''} ${i.combination_name ?? ''}`.trim()).join('、') + : client['addonSummary' as keyof ExamClient] || '—'; + + const handleSavePhone = async () => { + if (!phone || phone.trim() === '' || phone === '—') { + setEditMessage('请输入联系电话'); + return; + } + + setEditLoading(true); + setEditMessage(null); + + try { + const res = await editCustomerDetail({ + marital_status: maritalCode, + phone: phone.trim(), + }); + + if (res.Status === 200) { + setEditMessage('保存成功'); + setPhoneEditing(false); + setTimeout(() => setEditMessage(null), 2000); + } else { + setEditMessage(res.Message || '保存失败'); + } + } catch (err) { + console.error('保存客户信息失败', err); + setEditMessage('保存失败,请稍后重试'); + } finally { + setEditLoading(false); + } + }; + + const handleSaveMarital = async () => { + const phoneValue = phone === '—' ? '' : phone.trim(); + + if (!phoneValue) { + setEditMessage('联系电话不能为空'); + return; + } + + setEditLoading(true); + setEditMessage(null); + + try { + const res = await editCustomerDetail({ + marital_status: maritalCode, + phone: phoneValue, + }); + + if (res.Status === 200) { + setEditMessage('保存成功'); + setMaritalEditing(false); + setTimeout(() => setEditMessage(null), 2000); + } else { + setEditMessage(res.Message || '保存失败'); + } + } catch (err) { + console.error('保存客户信息失败', err); + setEditMessage('保存失败,请稍后重试'); + } finally { + setEditLoading(false); + } + }; + + const progressGroups = useMemo(() => { + const checked: string[] = []; + const abandoned: string[] = []; + const pending: string[] = []; + const deferred: string[] = []; + (progressList || []).forEach((p) => { + const name = p.project_name || p.department_name || '项目'; + switch (p.exam_status) { + case 1: + checked.push(name); + break; + case 2: + abandoned.push(name); + break; + case 4: + deferred.push(name); + break; + default: + pending.push(name); + } + }); + return { checked, abandoned, pending, deferred }; + }, [progressList]); + + return ( +
+
+
+
+ 头像 +
+
+
+ {loading ? '加载中…' : '基础信息:头像、姓名、证件号、手机号等(点击图标可进行编辑)'} +
+
+ {editMessage && ( +
+ {editMessage} +
+ )} +
+
基础信息
+
+
+ 姓名:{customerInfo?.customer_name || client.name} +
+
+ 证件号:{customerInfo?.id_no || '—'} +
+
+ 手机号: + {!phoneEditing ? ( + + {phone} + + + ) : ( + + setPhone(e.target.value)} + disabled={editLoading} + /> + + + + )} +
+
+ 性别/年龄: + + {customerInfo?.gender_name || client.gender} / {client.age} + +
+
+ 客户级别:{client.level} +
+
+ 所属渠道:{customerChannel} +
+
+ 婚姻状况: + {!maritalEditing ? ( + + {getMaritalText(maritalCode)} + + + ) : ( + + + + + + + )} +
+
+ 家医:{familyDoctor} +
+
+ 团标签:{groupTag as string} +
+
+
+ +
+
预约信息
+
+
+ 预约时间:{bookingTime as string} +
+
+ 签到时间:{signTime as string} +
+
+ 已消耗时长: + + {appointmentInfo?.physical_exam_complete_time && appointmentInfo?.sign_in_time + ? `${appointmentInfo.physical_exam_complete_time} - ${appointmentInfo.sign_in_time}` + : client.elapsed} + +
+
+ 体检套餐名称:{client.packageName} +
+
+ 加项内容:{addonSummary as string} +
+
+
+ +
+
+
已查项目 共 {progressGroups.checked.length} 项
+
+ {(progressGroups.checked.length ? progressGroups.checked : client.checkedItems).map((i) => ( + + {i} + + ))} +
+
+
+
弃检项目 共 {progressGroups.abandoned.length} 项
+
+ {progressGroups.abandoned.length ? ( + progressGroups.abandoned.map((i) => ( + + {i} + + )) + ) : ( + 暂无弃检项目 + )} +
+
+
+
未查项目 共 {progressGroups.pending.length} 项
+
+ {(progressGroups.pending.length ? progressGroups.pending : client.pendingItems).map((i) => ( + + {i} + + ))} +
+
+
+
延期项目 共 {progressGroups.deferred.length} 项
+
+ {progressGroups.deferred.length ? ( + progressGroups.deferred.map((i) => ( + + {i} + + )) + ) : ( + 暂无延期项目 + )} +
+
+
+
+ ); +}; + + diff --git a/src/components/exam/ExamModal.tsx b/src/components/exam/ExamModal.tsx index 9e7a373..f939366 100644 --- a/src/components/exam/ExamModal.tsx +++ b/src/components/exam/ExamModal.tsx @@ -1,16 +1,17 @@ -import { useEffect, useMemo, useRef, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import type { ExamClient, ExamModalTab } from '../../data/mockData'; import type { CustomerAppointmentInfo, CustomerExamAddItem, CustomerInfo, - OutputTongyishuFileInfo, PhysicalExamProgressItem, + OutputTongyishuFileInfo, } from '../../api'; -import { getCustomerDetail, getPhysicalExamProgressDetail, signInMedicalExamCenter, getTongyishuPdf, submitTongyishuSign, editCustomerDetail } from '../../api'; +import { getCustomerDetail, getPhysicalExamProgressDetail, signInMedicalExamCenter, getTongyishuPdf, submitTongyishuSign } from '../../api'; import type { SignaturePadHandle } from '../ui'; import { Button, Input, SignaturePad } from '../ui'; +import { ExamDetailPanel } from './ExamDetailPanel'; interface ExamModalProps { client: ExamClient; @@ -157,7 +158,7 @@ export const ExamModal = ({ client, tab, onTabChange, onClose }: ExamModalProps)
{tab === 'detail' && ( - { ); }; -const ExamDetailInfo = ({ - client, - customerInfo, - appointmentInfo, - addItemInfoList, - progressList, - loading, -}: { - client: ExamClient; - customerInfo: CustomerInfo | null; - appointmentInfo: CustomerAppointmentInfo | null; - addItemInfoList: CustomerExamAddItem[] | null; - progressList: PhysicalExamProgressItem[] | null; - loading: boolean; -}) => { - const basePhone = customerInfo?.phone || (client['mobile' as keyof ExamClient] as string | undefined) || ''; - const baseMaritalText = - customerInfo?.patient_marital_status_name || - (client['maritalStatus' as keyof ExamClient] as string | undefined) || - '—'; - // 将文本转换为数字:10-未婚,20-已婚 - const getMaritalCodeFromText = (text: string): number => { - if (text.includes('未婚') || text === '未婚') return 10; - if (text.includes('已婚') || text === '已婚') return 20; - return 20; // 默认已婚 - }; - const baseMaritalCode = baseMaritalText === '—' ? 20 : getMaritalCodeFromText(baseMaritalText); - - const [phone, setPhone] = useState(basePhone || '—'); - const [maritalCode, setMaritalCode] = useState(baseMaritalCode); - const [phoneEditing, setPhoneEditing] = useState(false); - const [maritalEditing, setMaritalEditing] = useState(false); - const [editLoading, setEditLoading] = useState(false); - const [editMessage, setEditMessage] = useState(null); - - const getMaritalText = (code: number): string => { - return code === 10 ? '未婚' : '已婚'; - }; - - const customerChannel = client.customerType === '团客' ? '团体客户' : '散客客户'; - const familyDoctor = customerInfo?.family_doctor_name || (client['familyDoctor' as keyof ExamClient] as string | undefined) || '—'; - const groupTag = client['groupTag' as keyof ExamClient] || (client.customerType === '团客' ? '团检' : '—'); - const bookingTime = appointmentInfo?.appointment_time || (client['bookingTime' as keyof ExamClient] || '—'); - const signTime = appointmentInfo?.sign_in_time || (client['signTime' as keyof ExamClient] || '—'); - const addonSummary = - addItemInfoList && addItemInfoList.length > 0 - ? addItemInfoList.map((i) => `${i.dept_name ?? ''} ${i.combination_name ?? ''}`.trim()).join('、') - : client['addonSummary' as keyof ExamClient] || '—'; - - const handleSavePhone = async () => { - if (!phone || phone.trim() === '' || phone === '—') { - setEditMessage('请输入联系电话'); - return; - } - - setEditLoading(true); - setEditMessage(null); - - try { - const res = await editCustomerDetail({ - marital_status: maritalCode, - phone: phone.trim(), - }); - - if (res.Status === 200) { - setEditMessage('保存成功'); - setPhoneEditing(false); - // 2秒后清除消息 - setTimeout(() => setEditMessage(null), 2000); - } else { - setEditMessage(res.Message || '保存失败'); - } - } catch (err) { - console.error('保存客户信息失败', err); - setEditMessage('保存失败,请稍后重试'); - } finally { - setEditLoading(false); - } - }; - - const handleSaveMarital = async () => { - const phoneValue = phone === '—' ? '' : phone.trim(); - - if (!phoneValue) { - setEditMessage('联系电话不能为空'); - return; - } - - setEditLoading(true); - setEditMessage(null); - - try { - const res = await editCustomerDetail({ - marital_status: maritalCode, - phone: phoneValue, - }); - - if (res.Status === 200) { - setEditMessage('保存成功'); - setMaritalEditing(false); - // 2秒后清除消息 - setTimeout(() => setEditMessage(null), 2000); - } else { - setEditMessage(res.Message || '保存失败'); - } - } catch (err) { - console.error('保存客户信息失败', err); - setEditMessage('保存失败,请稍后重试'); - } finally { - setEditLoading(false); - } - }; - - const progressGroups = useMemo(() => { - const checked: string[] = []; - const abandoned: string[] = []; - const pending: string[] = []; - const deferred: string[] = []; - (progressList || []).forEach((p) => { - const name = p.project_name || p.department_name || '项目'; - switch (p.exam_status) { - case 1: - checked.push(name); - break; - case 2: - abandoned.push(name); - break; - case 4: - deferred.push(name); - break; - default: - pending.push(name); - } - }); - return { checked, abandoned, pending, deferred }; - }, [progressList]); - - return ( -
-
-
-
- 头像 -
-
-
- {loading ? '加载中…' : '基础信息:头像、姓名、证件号、手机号等(点击图标可进行编辑)'} -
-
- {editMessage && ( -
- {editMessage} -
- )} -
-
基础信息
-
-
- 姓名:{customerInfo?.customer_name || client.name} -
-
- 证件号:{customerInfo?.id_no || '—'} -
-
- 手机号: - {!phoneEditing ? ( - - {phone} - - - ) : ( - - setPhone(e.target.value)} - disabled={editLoading} - /> - - - - )} -
-
- 性别/年龄: - - {customerInfo?.gender_name || client.gender} / {client.age} - -
-
- 客户级别:{client.level} -
-
- 所属渠道:{customerChannel} -
-
- 婚姻状况: - {!maritalEditing ? ( - - {getMaritalText(maritalCode)} - - - ) : ( - - - - - - - )} -
-
- 家医:{familyDoctor} -
-
- 团标签:{groupTag as string} -
-
-
- -
-
预约信息
-
-
- 预约时间:{bookingTime as string} -
-
- 签到时间:{signTime as string} -
-
- 已消耗时长: - - {appointmentInfo?.physical_exam_complete_time && appointmentInfo?.sign_in_time - ? `${appointmentInfo.physical_exam_complete_time} - ${appointmentInfo.sign_in_time}` - : client.elapsed} - -
-
- 体检套餐名称:{client.packageName} -
-
- 加项内容:{addonSummary as string} -
-
-
- -
-
-
已查项目 共 {progressGroups.checked.length} 项
-
- {(progressGroups.checked.length ? progressGroups.checked : client.checkedItems).map((i) => ( - - {i} - - ))} -
-
-
-
弃检项目 共 {progressGroups.abandoned.length} 项
-
- {progressGroups.abandoned.length ? ( - progressGroups.abandoned.map((i) => ( - - {i} - - )) - ) : ( - 暂无弃检项目 - )} -
-
-
-
未查项目 共 {progressGroups.pending.length} 项
-
- {(progressGroups.pending.length ? progressGroups.pending : client.pendingItems).map((i) => ( - - {i} - - ))} -
-
-
-
延期项目 共 {progressGroups.deferred.length} 项
-
- {progressGroups.deferred.length ? ( - progressGroups.deferred.map((i) => ( - - {i} - - )) - ) : ( - 暂无延期项目 - )} -
-
-
-
- ); -}; - const ExamDeliveryPanel = ({ client }: { client: ExamClient }) => (