This commit is contained in:
xianyi
2026-01-28 11:41:34 +08:00
parent f75e19cf85
commit d09bfc4ec6
2 changed files with 285 additions and 262 deletions

View File

@@ -548,6 +548,8 @@ export interface InputPhysicalExamAddItem {
scrm_account_name?: string | null;
/** 项目名称(默认空值,传入项目名称过滤数据) */
item_name?: string;
/** 折扣率 */
discount_rate?: number | null;
}
/**

View File

@@ -85,7 +85,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
} | null>(null);
const [customSettlementLoading, setCustomSettlementLoading] = useState(false);
const [customSettlementType, setCustomSettlementType] = useState<1 | 2>(1); // 1-按比例折扣 2-自定义结算价
const [customDiscountRatio, setCustomDiscountRatio] = useState<number>(100); // 折扣比例如100代表10折即原价
const [customDiscountRatio, setCustomDiscountRatio] = useState<number | null>(null); // 折扣比例如100代表10折即原价
const [customFinalPrice, setCustomFinalPrice] = useState<number>(0); // 最终结算价
const [customApplyReason, setCustomApplyReason] = useState<string>(''); // 申请理由
const [waitingSeconds, setWaitingSeconds] = useState<number>(0); // 等待审核的秒数
@@ -123,24 +123,15 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
try {
const res = await getAddItemCustomerInfo({ physical_exam_id });
if (res.Status === 200) {
// 保存客户信息
// 保存客户信息,渠道信息使用 listChannelDiscount 中的 channel_id / channel_name
if (res.Data?.customerInfo) {
const channelInfo = res.Data.listChannelDiscount?.[0];
const scrm_account_id =
res.Data.customerInfo.scrm_account_id ??
channelInfo?.channel_id ??
null;
const scrm_account_name =
res.Data.customerInfo.scrm_account_name ??
channelInfo?.channel_name ??
null;
const firstChannel = res.Data.listChannelDiscount?.[0];
setCustomerInfo({
patient_id: res.Data.customerInfo.patient_id,
customer_name: res.Data.customerInfo.customer_name,
phone: res.Data.customerInfo.phone,
scrm_account_id,
scrm_account_name,
scrm_account_id: firstChannel?.channel_id ?? null,
scrm_account_name: firstChannel?.channel_name ?? null,
});
customerInfoLoadedRef.current = true;
// 设置挂账公司默认值
@@ -237,11 +228,19 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
setAddonLoading(true);
setAddonError(null);
try {
console.log("请求数据 1", customerInfo, customerInfo?.scrm_account_id);
const selectedChannel = channelDiscounts.find(
(item) => item.discount_rate === discountRatio
);
const discountRate = selectedChannel?.discount_rate ?? null;
const res = await searchPhysicalExamAddItem({
physical_exam_id: Number(client.id),
scrm_account_id: customerInfo?.scrm_account_id || null,
scrm_account_name: customerInfo?.scrm_account_name || null,
item_name: debouncedAddonSearch.trim() || "",
discount_rate: discountRate,
});
if (res.Status === 200 && Array.isArray(res.Data)) {
const list: AddonItem[] = res.Data.map((item) => ({
@@ -275,7 +274,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
}
};
fetchList();
}, [debouncedAddonSearch, customerInfo?.scrm_account_id, customerInfo?.scrm_account_name, client.id, customerInfo]);
}, [debouncedAddonSearch, customerInfo?.scrm_account_id, customerInfo?.scrm_account_name, client.id, customerInfo, channelDiscounts, discountRatio]);
const allAddons = useMemo(() => addonList, [addonList]);
@@ -335,14 +334,19 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
return item.discount_name;
};
// 构建折扣选项列表
// 构建折扣选项列表带上渠道ID/名称,便于联动设置 scrm_account_id / scrm_account_name
const discountOptions = useMemo(() => {
const options: Array<{ value: number; label: string }> = [];
const options: Array<{ value: number; label: string; channel_id?: string | null; channel_name?: string | null }> = [];
channelDiscounts.forEach((item) => {
const rate = typeof item.discount_rate === 'number' && item.discount_rate > 0 ? item.discount_rate : 1;
const percent = Math.round(rate * 100);
const label = item.discount_name || `${percent}%`;
options.push({ value: rate, label });
options.push({
value: rate,
label,
channel_id: item.channel_id ?? null,
channel_name: item.channel_name ?? null,
});
});
return options;
}, [channelDiscounts]);
@@ -353,9 +357,21 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
return option?.label;
}, [discountRatio, discountOptions]);
// 处理折扣选择
// 处理折扣选择,同时更新 scrm_account_id / scrm_account_name
const handleDiscountSelect = (value: number) => {
setDiscountRatio(value);
const matched = discountOptions.find(opt => opt.value === value);
if (matched && (matched.channel_id || matched.channel_name)) {
setCustomerInfo((prev) =>
prev
? {
...prev,
scrm_account_id: matched.channel_id ?? null,
scrm_account_name: matched.channel_name ?? null,
}
: prev
);
}
setIsDiscountDropdownOpen(false);
};
@@ -503,7 +519,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
stopCustomSettlementPolling();
}
});
}, 1000);
}, 1500);
}, [fetchCustomSettlementStatus]);
// 停止轮询自定义结算审批状态
@@ -559,6 +575,12 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
return;
}
const discountRatioValue = customSettlementType === 1 ? (customDiscountRatio ?? 0) : 0;
if (customSettlementType === 1 && (discountRatioValue <= 0 || discountRatioValue > 100)) {
setPaymentMessage('请输入有效折扣比例');
return;
}
setCustomSettlementLoading(true);
setPaymentMessage(null);
@@ -575,7 +597,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
if (customSettlementType === 1) {
// 按比例折扣
settlementPrice = originalPrice * (customDiscountRatio / 100);
settlementPrice = originalPrice * (discountRatioValue / 100);
} else {
// 自定义结算价
settlementPrice = customFinalPrice / selectedItems.length; // 平均分配
@@ -595,7 +617,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
}, 0);
const final_settlement_price = customSettlementType === 1
? original_settlement_price * (customDiscountRatio / 100)
? original_settlement_price * (discountRatioValue / 100)
: customFinalPrice;
const res = await customSettlementApply({
@@ -603,7 +625,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
listAddItemDetail,
original_settlement_price,
settlement_type: customSettlementType,
discount_ratio: customSettlementType === 1 ? customDiscountRatio : undefined,
discount_ratio: customSettlementType === 1 ? discountRatioValue : undefined,
final_settlement_price,
apply_reason: customApplyReason.trim(),
});
@@ -647,6 +669,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
return;
}
stopCustomSettlementPolling();
setCustomSettlementLoading(true);
setPaymentMessage(null);
@@ -668,6 +691,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
if (res.Status === 200 && res.Data?.is_success === 1) {
setPaymentMessage('取消申请成功');
setCustomDiscountRatio(null);
// 停止轮询
stopCustomSettlementPolling();
// 重新获取审批状态
@@ -1078,14 +1102,16 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
<span className='text-xs text-gray-400 line-through'>¥{origPrice.toFixed(2)}</span>
)}
<span className='text-[13px] font-bold text-red-600'>¥{currPrice.toFixed(2)}</span>
<span className='text-[13px] font-bold text-[#447955]'>{getDiscountText(item)}</span>
<span className='text-[13px] font-bold text-red-600'>¥{currPrice.toFixed(2)}</span>
{/*
<div className='flex items-center justify-between gap-2'>
<span></span>
<span className={`text-[10px] px-2 rounded-full bg-[#EAFCF1] text-[#447955] whitespace-nowrap`}>
{getDiscountText(item)}
</span>
</div>
</div> */}
</div>
</div>
</div>
@@ -1096,55 +1122,39 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
{/* 底部汇总和支付 */}
<div className='border-t pt-4 mt-6 flex items-center justify-between'>
<div className='flex items-center gap-2'>
<div className='space-y-1 text-sm'>
<div className='text-gray-600'>
: <span className='text-gray-900'>¥{totalOriginal.toFixed(2)}</span>
: <span className='text-gray-900'>¥{totalOriginal.toFixed(2)}</span>
</div>
<div className='text-gray-600'>
: <span className='text-xl font-bold text-red-600'>¥{totalCurrent.toFixed(2)}</span>
{discount > 0 && (
<span className='text-gray-500 ml-1'> ¥{discount.toFixed(2)}</span>
)}
: <span className='font-bold text-red-600'>¥{discount.toFixed(2)}</span>
</div>
<div className='text-gray-600'>
: <span className='font-bold text-red-600'>¥{(totalCurrent).toFixed(2)}</span>
</div>
{/* <div className='text-xs text-gray-500'>结算方式: 个人支付 (微信 / 支付宝)</div> */}
</div>
{/* 添加自定义结算 */}
{localStorage.getItem('authCode')?.includes('HisTijianPad_Btn_Tongji') && selectedItems.length > 0 && (
<div className='flex flex-col items-end gap-2 relative'>
{customSettlementStatus && (
<div className='text-xs text-gray-600'>
: <span className={cls(
'font-semibold',
customSettlementStatus.apply_status === 1 && 'text-blue-600', // 审核中
customSettlementStatus.apply_status === 3 && 'text-green-600', // 审核通过
customSettlementStatus.apply_status === 4 && 'text-red-600', // 审核不通过
customSettlementStatus.apply_status === 2 && 'text-gray-600' // 取消申请
)}>
{customSettlementStatus.apply_status_name || '未知'}
{customSettlementStatus.apply_status === 1 && ' (审核中...)'}
</span>
{customSettlementStatus.final_settlement_price !== null && customSettlementStatus.final_settlement_price !== undefined && (
<span className='ml-2'>
: <span className='font-semibold text-red-600'>¥{customSettlementStatus.final_settlement_price.toFixed(2)}</span>
</span>
)}
</div>
)}
<Button
onClick={() => setShowCustomSettlementModal(true)}
disabled={customSettlementStatus?.apply_status === 1}
className={cls(
'px-3 py-1.5 text-xs text-white',
customSettlementStatus?.apply_status === 1
? 'bg-gray-400 cursor-not-allowed'
: 'bg-blue-600 hover:bg-blue-700'
)}
className=
'px-3 py-1.5 text-xs text-white bg-blue-600'
>
{!customSettlementStatus || !customSettlementStatus.apply_status || customSettlementStatus.apply_status === 1
? '申请自定义结算'
: customSettlementStatus.apply_status === 3
? '自定义结算(已通过)'
: '自定义结算(未通过)'}
</Button>
</div>
)}
</div>
{paymentMessage && (
@@ -1264,7 +1274,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
)}
<Button
className='bg-[#269745] hover:bg-[#269745]/80 rounded-3xl text-white px-6 py-3 text-base font-medium'
className='bg-blue-600 rounded-3xl text-white px-6 py-3 text-base font-medium'
disabled={selectedCount === 0 || paymentLoading}
onClick={handlePayment}
>
@@ -1273,7 +1283,8 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
</div>
</div>
{/* 二维码支付弹窗 */}
{showQrcodeModal && qrcodeUrl && (
{
showQrcodeModal && qrcodeUrl && (
<div className='fixed inset-0 z-[80] bg-black/80 flex items-center justify-center px-6'>
<div className='bg-white rounded-2xl w-full max-w-md shadow-2xl p-6 flex flex-col gap-4'>
<div className='flex items-center justify-between'>
@@ -1307,10 +1318,12 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
</div>
</div>
</div>
)}
)
}
{/* 自定义结算弹窗 */}
{showCustomSettlementModal && (
{
showCustomSettlementModal && (
<div className='fixed inset-0 z-50 flex items-center justify-center bg-black/30' onClick={() => setShowCustomSettlementModal(false)}>
<div
className='w-[500px] max-w-[95vw] bg-white rounded-2xl shadow-xl overflow-hidden'
@@ -1381,21 +1394,26 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
<div className='flex items-center justify-between'>
<label className='text-sm text-gray-700 font-medium'> (%)</label>
<span className='text-sm font-semibold text-blue-600'>
{(customDiscountRatio / 10).toFixed(1)}
{((customDiscountRatio ?? 0) / 10).toFixed(1)}
</span>
</div>
<div className='flex items-center gap-2'>
<Input
type='number'
min='10'
min='0'
max='100'
step='1'
value={customDiscountRatio}
value={customDiscountRatio ?? ''}
onChange={(e) => {
const val = Number(e.target.value);
const raw = e.target.value;
if (raw === '') {
setCustomDiscountRatio(null);
return;
}
const val = Number(raw);
if (Number.isNaN(val)) return;
if (val < 10) {
setCustomDiscountRatio(10);
if (val < 1) {
setCustomDiscountRatio(1);
} else if (val > 100) {
setCustomDiscountRatio(100);
} else {
@@ -1404,10 +1422,10 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
}}
className='w-28 text-sm'
/>
<span className='text-xs text-gray-500'>10-100100 </span>
<span className='text-xs text-gray-500'>1-100100 </span>
</div>
<div className='text-xs text-gray-500'>
: <span className='font-semibold text-red-600'>¥{(totalCurrent * (customDiscountRatio / 100)).toFixed(2)}</span>
: <span className='font-semibold text-red-600'>¥{(totalCurrent * ((customDiscountRatio ?? 0) / 100)).toFixed(2)}</span>
</div>
</div>
) : (
@@ -1466,10 +1484,12 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
</div>
</div>
</div>
)}
)
}
{/* 审核中全屏遮罩 */}
{customSettlementStatus?.apply_status === 1 && (
{
customSettlementStatus?.apply_status === 1 && (
<div className='fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50'>
<div className='bg-white rounded-2xl p-6 max-w-md w-full mx-4 shadow-xl'>
<div className='text-center mb-6'>
@@ -1488,7 +1508,8 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
</div>
</div>
</div>
)}
)
}
</div >
);
};