完善客户信息编辑
This commit is contained in:
@@ -18,6 +18,8 @@ import type {
|
|||||||
TongyishuGetResponse,
|
TongyishuGetResponse,
|
||||||
InputTongyishuSignSubmit,
|
InputTongyishuSignSubmit,
|
||||||
TongyishuSignSubmitResponse,
|
TongyishuSignSubmitResponse,
|
||||||
|
InputCustomerDetailEdit,
|
||||||
|
CustomerDetailEditResponse,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -156,3 +158,15 @@ export const submitTongyishuSign = (
|
|||||||
).then(res => res.data);
|
).then(res => res.data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户信息编辑
|
||||||
|
*/
|
||||||
|
export const editCustomerDetail = (
|
||||||
|
data: InputCustomerDetailEdit
|
||||||
|
): Promise<CustomerDetailEditResponse> => {
|
||||||
|
return request.post<CustomerDetailEditResponse>(
|
||||||
|
`${MEDICAL_EXAM_BASE_PATH}/customer-detail-edit`,
|
||||||
|
data
|
||||||
|
).then(res => res.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -336,3 +336,25 @@ export interface OutputTongyishuSignInfo {
|
|||||||
*/
|
*/
|
||||||
export type TongyishuSignSubmitResponse = CommonActionResult<OutputTongyishuSignInfo>;
|
export type TongyishuSignSubmitResponse = CommonActionResult<OutputTongyishuSignInfo>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户信息编辑入参
|
||||||
|
*/
|
||||||
|
export interface InputCustomerDetailEdit {
|
||||||
|
/** 婚姻状况(10-未婚 20-已婚) */
|
||||||
|
marital_status: number;
|
||||||
|
/** 联系电话 */
|
||||||
|
phone: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户信息编辑出参
|
||||||
|
*/
|
||||||
|
export interface OutputCustomerDetailEdit {
|
||||||
|
// 空对象,无属性
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户信息编辑响应
|
||||||
|
*/
|
||||||
|
export type CustomerDetailEditResponse = CommonActionResult<OutputCustomerDetailEdit>;
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import type {
|
|||||||
OutputTongyishuFileInfo,
|
OutputTongyishuFileInfo,
|
||||||
PhysicalExamProgressItem,
|
PhysicalExamProgressItem,
|
||||||
} from '../../api';
|
} from '../../api';
|
||||||
import { getCustomerDetail, getPhysicalExamProgressDetail, signInMedicalExamCenter, getTongyishuPdf, submitTongyishuSign } from '../../api';
|
import { getCustomerDetail, getPhysicalExamProgressDetail, signInMedicalExamCenter, getTongyishuPdf, submitTongyishuSign, editCustomerDetail } from '../../api';
|
||||||
import type { SignaturePadHandle } from '../ui';
|
import type { SignaturePadHandle } from '../ui';
|
||||||
import { Button, Input, SignaturePad } from '../ui';
|
import { Button, Input, SignaturePad } from '../ui';
|
||||||
|
|
||||||
@@ -788,14 +788,28 @@ const ExamDetailInfo = ({
|
|||||||
loading: boolean;
|
loading: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const basePhone = customerInfo?.phone || (client['mobile' as keyof ExamClient] as string | undefined) || '';
|
const basePhone = customerInfo?.phone || (client['mobile' as keyof ExamClient] as string | undefined) || '';
|
||||||
const baseMarital =
|
const baseMaritalText =
|
||||||
customerInfo?.patient_marital_status_name ||
|
customerInfo?.patient_marital_status_name ||
|
||||||
(client['maritalStatus' as keyof ExamClient] as string | undefined) ||
|
(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 [phone, setPhone] = useState(basePhone || '—');
|
||||||
const [marital, setMarital] = useState(baseMarital);
|
const [maritalCode, setMaritalCode] = useState(baseMaritalCode);
|
||||||
const [phoneEditing, setPhoneEditing] = useState(false);
|
const [phoneEditing, setPhoneEditing] = useState(false);
|
||||||
const [maritalEditing, setMaritalEditing] = useState(false);
|
const [maritalEditing, setMaritalEditing] = useState(false);
|
||||||
|
const [editLoading, setEditLoading] = useState(false);
|
||||||
|
const [editMessage, setEditMessage] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const getMaritalText = (code: number): string => {
|
||||||
|
return code === 10 ? '未婚' : '已婚';
|
||||||
|
};
|
||||||
|
|
||||||
const customerChannel = client.customerType === '团客' ? '团体客户' : '散客客户';
|
const customerChannel = client.customerType === '团客' ? '团体客户' : '散客客户';
|
||||||
const familyDoctor = customerInfo?.family_doctor_name || (client['familyDoctor' as keyof ExamClient] as string | undefined) || '—';
|
const familyDoctor = customerInfo?.family_doctor_name || (client['familyDoctor' as keyof ExamClient] as string | undefined) || '—';
|
||||||
@@ -807,6 +821,70 @@ const ExamDetailInfo = ({
|
|||||||
? addItemInfoList.map((i) => `${i.dept_name ?? ''} ${i.combination_name ?? ''}`.trim()).join('、')
|
? addItemInfoList.map((i) => `${i.dept_name ?? ''} ${i.combination_name ?? ''}`.trim()).join('、')
|
||||||
: client['addonSummary' as keyof ExamClient] || '—';
|
: 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 progressGroups = useMemo(() => {
|
||||||
const checked: string[] = [];
|
const checked: string[] = [];
|
||||||
const abandoned: string[] = [];
|
const abandoned: string[] = [];
|
||||||
@@ -843,7 +921,12 @@ const ExamDetailInfo = ({
|
|||||||
{loading ? '加载中…' : '基础信息:头像、姓名、证件号、手机号等(点击图标可进行编辑)'}
|
{loading ? '加载中…' : '基础信息:头像、姓名、证件号、手机号等(点击图标可进行编辑)'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{editMessage && (
|
||||||
|
<div className={`text-xs px-3 py-2 rounded-lg ${editMessage.includes('成功') ? 'bg-green-50 text-green-600' : 'bg-amber-50 text-amber-600'
|
||||||
|
}`}>
|
||||||
|
{editMessage}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className='space-y-2 text-xs text-gray-700'>
|
<div className='space-y-2 text-xs text-gray-700'>
|
||||||
<div className='font-medium text-gray-900'>基础信息</div>
|
<div className='font-medium text-gray-900'>基础信息</div>
|
||||||
<div className='grid grid-cols-2 gap-x-8 gap-y-1'>
|
<div className='grid grid-cols-2 gap-x-8 gap-y-1'>
|
||||||
@@ -868,9 +951,25 @@ const ExamDetailInfo = ({
|
|||||||
className='w-28 rounded-xl border px-2 py-0.5 text-[11px] outline-none'
|
className='w-28 rounded-xl border px-2 py-0.5 text-[11px] outline-none'
|
||||||
value={phone}
|
value={phone}
|
||||||
onChange={(e) => setPhone(e.target.value)}
|
onChange={(e) => setPhone(e.target.value)}
|
||||||
|
disabled={editLoading}
|
||||||
/>
|
/>
|
||||||
<button className='px-2 py-0.5 rounded-xl border text-[11px]' onClick={() => setPhoneEditing(false)}>
|
<button
|
||||||
保存
|
className='px-2 py-0.5 rounded-xl border text-[11px] disabled:opacity-50'
|
||||||
|
onClick={handleSavePhone}
|
||||||
|
disabled={editLoading}
|
||||||
|
>
|
||||||
|
{editLoading ? '保存中...' : '保存'}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='px-2 py-0.5 rounded-xl border text-[11px]'
|
||||||
|
onClick={() => {
|
||||||
|
setPhoneEditing(false);
|
||||||
|
setPhone(basePhone || '—');
|
||||||
|
setEditMessage(null);
|
||||||
|
}}
|
||||||
|
disabled={editLoading}
|
||||||
|
>
|
||||||
|
取消
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
@@ -891,20 +990,54 @@ const ExamDetailInfo = ({
|
|||||||
<span>婚姻状况:</span>
|
<span>婚姻状况:</span>
|
||||||
{!maritalEditing ? (
|
{!maritalEditing ? (
|
||||||
<span className='text-gray-900 flex items-center'>
|
<span className='text-gray-900 flex items-center'>
|
||||||
{marital}
|
{getMaritalText(maritalCode)}
|
||||||
<button className='ml-1 text-blue-500 text-[11px] hover:underline' onClick={() => setMaritalEditing(true)}>
|
<button className='ml-1 text-blue-500 text-[11px] hover:underline' onClick={() => setMaritalEditing(true)}>
|
||||||
✏️ 编辑
|
✏️ 编辑
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span className='flex items-center gap-1'>
|
<span className='flex items-center gap-2'>
|
||||||
<input
|
<label className='flex items-center gap-1 cursor-pointer'>
|
||||||
className='w-20 rounded-xl border px-2 py-0.5 text-[11px] outline-none'
|
<input
|
||||||
value={marital}
|
type='radio'
|
||||||
onChange={(e) => setMarital(e.target.value)}
|
name='marital'
|
||||||
/>
|
value='10'
|
||||||
<button className='px-2 py-0.5 rounded-xl border text-[11px]' onClick={() => setMaritalEditing(false)}>
|
checked={maritalCode === 10}
|
||||||
保存
|
onChange={(e) => setMaritalCode(Number(e.target.value))}
|
||||||
|
disabled={editLoading}
|
||||||
|
className='w-3 h-3'
|
||||||
|
/>
|
||||||
|
<span className='text-[11px]'>未婚</span>
|
||||||
|
</label>
|
||||||
|
<label className='flex items-center gap-1 cursor-pointer'>
|
||||||
|
<input
|
||||||
|
type='radio'
|
||||||
|
name='marital'
|
||||||
|
value='20'
|
||||||
|
checked={maritalCode === 20}
|
||||||
|
onChange={(e) => setMaritalCode(Number(e.target.value))}
|
||||||
|
disabled={editLoading}
|
||||||
|
className='w-3 h-3'
|
||||||
|
/>
|
||||||
|
<span className='text-[11px]'>已婚</span>
|
||||||
|
</label>
|
||||||
|
<button
|
||||||
|
className='px-2 py-0.5 rounded-xl border text-[11px] disabled:opacity-50'
|
||||||
|
onClick={handleSaveMarital}
|
||||||
|
disabled={editLoading}
|
||||||
|
>
|
||||||
|
{editLoading ? '保存中...' : '保存'}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='px-2 py-0.5 rounded-xl border text-[11px]'
|
||||||
|
onClick={() => {
|
||||||
|
setMaritalEditing(false);
|
||||||
|
setMaritalCode(baseMaritalCode);
|
||||||
|
setEditMessage(null);
|
||||||
|
}}
|
||||||
|
disabled={editLoading}
|
||||||
|
>
|
||||||
|
取消
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user