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; scrm_account_name?: string | null;
/** 项目名称(默认空值,传入项目名称过滤数据) */ /** 项目名称(默认空值,传入项目名称过滤数据) */
item_name?: string; item_name?: string;
/** 折扣率 */
discount_rate?: number | null;
} }
/** /**

View File

@@ -85,7 +85,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
} | null>(null); } | null>(null);
const [customSettlementLoading, setCustomSettlementLoading] = useState(false); const [customSettlementLoading, setCustomSettlementLoading] = useState(false);
const [customSettlementType, setCustomSettlementType] = useState<1 | 2>(1); // 1-按比例折扣 2-自定义结算价 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 [customFinalPrice, setCustomFinalPrice] = useState<number>(0); // 最终结算价
const [customApplyReason, setCustomApplyReason] = useState<string>(''); // 申请理由 const [customApplyReason, setCustomApplyReason] = useState<string>(''); // 申请理由
const [waitingSeconds, setWaitingSeconds] = useState<number>(0); // 等待审核的秒数 const [waitingSeconds, setWaitingSeconds] = useState<number>(0); // 等待审核的秒数
@@ -123,24 +123,15 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
try { try {
const res = await getAddItemCustomerInfo({ physical_exam_id }); const res = await getAddItemCustomerInfo({ physical_exam_id });
if (res.Status === 200) { if (res.Status === 200) {
// 保存客户信息 // 保存客户信息,渠道信息使用 listChannelDiscount 中的 channel_id / channel_name
if (res.Data?.customerInfo) { if (res.Data?.customerInfo) {
const channelInfo = res.Data.listChannelDiscount?.[0]; const firstChannel = 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;
setCustomerInfo({ setCustomerInfo({
patient_id: res.Data.customerInfo.patient_id, patient_id: res.Data.customerInfo.patient_id,
customer_name: res.Data.customerInfo.customer_name, customer_name: res.Data.customerInfo.customer_name,
phone: res.Data.customerInfo.phone, phone: res.Data.customerInfo.phone,
scrm_account_id, scrm_account_id: firstChannel?.channel_id ?? null,
scrm_account_name, scrm_account_name: firstChannel?.channel_name ?? null,
}); });
customerInfoLoadedRef.current = true; customerInfoLoadedRef.current = true;
// 设置挂账公司默认值 // 设置挂账公司默认值
@@ -237,11 +228,19 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
setAddonLoading(true); setAddonLoading(true);
setAddonError(null); setAddonError(null);
try { 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({ const res = await searchPhysicalExamAddItem({
physical_exam_id: Number(client.id), physical_exam_id: Number(client.id),
scrm_account_id: customerInfo?.scrm_account_id || null, scrm_account_id: customerInfo?.scrm_account_id || null,
scrm_account_name: customerInfo?.scrm_account_name || null, scrm_account_name: customerInfo?.scrm_account_name || null,
item_name: debouncedAddonSearch.trim() || "", item_name: debouncedAddonSearch.trim() || "",
discount_rate: discountRate,
}); });
if (res.Status === 200 && Array.isArray(res.Data)) { if (res.Status === 200 && Array.isArray(res.Data)) {
const list: AddonItem[] = res.Data.map((item) => ({ const list: AddonItem[] = res.Data.map((item) => ({
@@ -275,7 +274,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
} }
}; };
fetchList(); 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]); const allAddons = useMemo(() => addonList, [addonList]);
@@ -335,14 +334,19 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
return item.discount_name; return item.discount_name;
}; };
// 构建折扣选项列表 // 构建折扣选项列表带上渠道ID/名称,便于联动设置 scrm_account_id / scrm_account_name
const discountOptions = useMemo(() => { 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) => { channelDiscounts.forEach((item) => {
const rate = typeof item.discount_rate === 'number' && item.discount_rate > 0 ? item.discount_rate : 1; const rate = typeof item.discount_rate === 'number' && item.discount_rate > 0 ? item.discount_rate : 1;
const percent = Math.round(rate * 100); const percent = Math.round(rate * 100);
const label = item.discount_name || `${percent}%`; 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; return options;
}, [channelDiscounts]); }, [channelDiscounts]);
@@ -353,9 +357,21 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
return option?.label; return option?.label;
}, [discountRatio, discountOptions]); }, [discountRatio, discountOptions]);
// 处理折扣选择 // 处理折扣选择,同时更新 scrm_account_id / scrm_account_name
const handleDiscountSelect = (value: number) => { const handleDiscountSelect = (value: number) => {
setDiscountRatio(value); 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); setIsDiscountDropdownOpen(false);
}; };
@@ -503,7 +519,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
stopCustomSettlementPolling(); stopCustomSettlementPolling();
} }
}); });
}, 1000); }, 1500);
}, [fetchCustomSettlementStatus]); }, [fetchCustomSettlementStatus]);
// 停止轮询自定义结算审批状态 // 停止轮询自定义结算审批状态
@@ -559,6 +575,12 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
return; return;
} }
const discountRatioValue = customSettlementType === 1 ? (customDiscountRatio ?? 0) : 0;
if (customSettlementType === 1 && (discountRatioValue <= 0 || discountRatioValue > 100)) {
setPaymentMessage('请输入有效折扣比例');
return;
}
setCustomSettlementLoading(true); setCustomSettlementLoading(true);
setPaymentMessage(null); setPaymentMessage(null);
@@ -575,7 +597,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
if (customSettlementType === 1) { if (customSettlementType === 1) {
// 按比例折扣 // 按比例折扣
settlementPrice = originalPrice * (customDiscountRatio / 100); settlementPrice = originalPrice * (discountRatioValue / 100);
} else { } else {
// 自定义结算价 // 自定义结算价
settlementPrice = customFinalPrice / selectedItems.length; // 平均分配 settlementPrice = customFinalPrice / selectedItems.length; // 平均分配
@@ -595,7 +617,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
}, 0); }, 0);
const final_settlement_price = customSettlementType === 1 const final_settlement_price = customSettlementType === 1
? original_settlement_price * (customDiscountRatio / 100) ? original_settlement_price * (discountRatioValue / 100)
: customFinalPrice; : customFinalPrice;
const res = await customSettlementApply({ const res = await customSettlementApply({
@@ -603,7 +625,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
listAddItemDetail, listAddItemDetail,
original_settlement_price, original_settlement_price,
settlement_type: customSettlementType, settlement_type: customSettlementType,
discount_ratio: customSettlementType === 1 ? customDiscountRatio : undefined, discount_ratio: customSettlementType === 1 ? discountRatioValue : undefined,
final_settlement_price, final_settlement_price,
apply_reason: customApplyReason.trim(), apply_reason: customApplyReason.trim(),
}); });
@@ -647,6 +669,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
return; return;
} }
stopCustomSettlementPolling();
setCustomSettlementLoading(true); setCustomSettlementLoading(true);
setPaymentMessage(null); setPaymentMessage(null);
@@ -668,6 +691,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
if (res.Status === 200 && res.Data?.is_success === 1) { if (res.Status === 200 && res.Data?.is_success === 1) {
setPaymentMessage('取消申请成功'); setPaymentMessage('取消申请成功');
setCustomDiscountRatio(null);
// 停止轮询 // 停止轮询
stopCustomSettlementPolling(); 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-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'> <div className='flex items-center justify-between gap-2'>
<span></span> <span></span>
<span className={`text-[10px] px-2 rounded-full bg-[#EAFCF1] text-[#447955] whitespace-nowrap`}> <span className={`text-[10px] px-2 rounded-full bg-[#EAFCF1] text-[#447955] whitespace-nowrap`}>
{getDiscountText(item)} {getDiscountText(item)}
</span> </span>
</div> </div> */}
</div> </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='border-t pt-4 mt-6 flex items-center justify-between'>
<div className='space-y-1 text-sm'> <div className='flex items-center gap-2'>
<div className='text-gray-600'> <div className='space-y-1 text-sm'>
: <span className='text-gray-900'>¥{totalOriginal.toFixed(2)}</span> <div className='text-gray-600'>
</div> : <span className='text-gray-900'>¥{totalOriginal.toFixed(2)}</span>
<div className='text-gray-600'> </div>
: <span className='text-xl font-bold text-red-600'>¥{totalCurrent.toFixed(2)}</span> <div className='text-gray-600'>
{discount > 0 && ( : <span className='font-bold text-red-600'>¥{discount.toFixed(2)}</span>
<span className='text-gray-500 ml-1'> ¥{discount.toFixed(2)}</span> </div>
)} <div className='text-gray-600'>
</div> : <span className='font-bold text-red-600'>¥{(totalCurrent).toFixed(2)}</span>
{/* <div className='text-xs text-gray-500'>结算方式: 个人支付 (微信 / 支付宝)</div> */} </div>
</div> {/* <div className='text-xs text-gray-500'>结算方式: 个人支付 (微信 / 支付宝)</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'
)}
>
</Button>
</div> </div>
)}
{/* 添加自定义结算 */}
{localStorage.getItem('authCode')?.includes('HisTijianPad_Btn_Tongji') && selectedItems.length > 0 && (
<div className='flex flex-col items-end gap-2 relative'>
<Button
onClick={() => setShowCustomSettlementModal(true)}
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 && ( {paymentMessage && (
@@ -1264,7 +1274,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
)} )}
<Button <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} disabled={selectedCount === 0 || paymentLoading}
onClick={handlePayment} onClick={handlePayment}
> >
@@ -1273,223 +1283,234 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
</div> </div>
</div> </div>
{/* 二维码支付弹窗 */} {/* 二维码支付弹窗 */}
{showQrcodeModal && qrcodeUrl && ( {
<div className='fixed inset-0 z-[80] bg-black/80 flex items-center justify-center px-6'> showQrcodeModal && qrcodeUrl && (
<div className='bg-white rounded-2xl w-full max-w-md shadow-2xl p-6 flex flex-col gap-4'> <div className='fixed inset-0 z-[80] bg-black/80 flex items-center justify-center px-6'>
<div className='flex items-center justify-between'> <div className='bg-white rounded-2xl w-full max-w-md shadow-2xl p-6 flex flex-col gap-4'>
<div className='text-lg font-semibold text-gray-900'></div> <div className='flex items-center justify-between'>
<Button <div className='text-lg font-semibold text-gray-900'></div>
className='py-1 px-3' <Button
onClick={() => { className='py-1 px-3'
if (pollingTimerRef.current) { onClick={() => {
clearInterval(pollingTimerRef.current); if (pollingTimerRef.current) {
pollingTimerRef.current = null; clearInterval(pollingTimerRef.current);
} pollingTimerRef.current = null;
setShowQrcodeModal(false); }
setQrcodeUrl(null); setShowQrcodeModal(false);
}} setQrcodeUrl(null);
> }}
>
</Button>
</div> </Button>
<div className='flex flex-col items-center gap-4'>
<div className='bg-white p-4 rounded-lg border-2 border-gray-200'>
<img src={qrcodeUrl} alt='支付二维码' className='w-64 h-64 object-contain' />
</div> </div>
<div className='text-sm text-gray-600 text-center'> <div className='flex flex-col items-center gap-4'>
使 <div className='bg-white p-4 rounded-lg border-2 border-gray-200'>
</div> <img src={qrcodeUrl} alt='支付二维码' className='w-64 h-64 object-contain' />
{paymentMessage && (
<div className={`text-sm ${paymentMessage.includes('成功') ? 'text-green-600' : 'text-amber-600'}`}>
{paymentMessage}
</div> </div>
)} <div className='text-sm text-gray-600 text-center'>
使
</div>
{paymentMessage && (
<div className={`text-sm ${paymentMessage.includes('成功') ? 'text-green-600' : 'text-amber-600'}`}>
{paymentMessage}
</div>
)}
</div>
</div> </div>
</div> </div>
</div> )
)} }
{/* 自定义结算弹窗 */} {/* 自定义结算弹窗 */}
{showCustomSettlementModal && ( {
<div className='fixed inset-0 z-50 flex items-center justify-center bg-black/30' onClick={() => setShowCustomSettlementModal(false)}> showCustomSettlementModal && (
<div <div className='fixed inset-0 z-50 flex items-center justify-center bg-black/30' onClick={() => setShowCustomSettlementModal(false)}>
className='w-[500px] max-w-[95vw] bg-white rounded-2xl shadow-xl overflow-hidden' <div
onClick={(e) => e.stopPropagation()} className='w-[500px] max-w-[95vw] bg-white rounded-2xl shadow-xl overflow-hidden'
> onClick={(e) => e.stopPropagation()}
<div className='px-4 py-3 border-b flex items-center justify-between'> >
<div className='font-semibold'></div> <div className='px-4 py-3 border-b flex items-center justify-between'>
<button <div className='font-semibold'></div>
className='text-xs text-gray-500 hover:text-gray-700' <button
onClick={() => setShowCustomSettlementModal(false)} className='text-xs text-gray-500 hover:text-gray-700'
> onClick={() => setShowCustomSettlementModal(false)}
>
</button>
</div> </button>
<div className='px-4 py-6 space-y-4'>
{/* 选中项目信息 */}
<div className='text-sm text-gray-700'>
<div className='font-medium mb-2'> ({selectedItems.length}):</div>
<div className='max-h-32 overflow-y-auto space-y-1'>
{selectedItems.map((item, idx) => (
<div key={idx} className='text-xs text-gray-600 flex justify-between'>
<span>{item.name}</span>
<span>¥{parseFloat(item.currentPrice || item.originalPrice || '0').toFixed(2)}</span>
</div>
))}
</div>
<div className='mt-2 pt-2 border-t text-sm font-semibold flex justify-between'>
<span>:</span>
<span className='text-red-600'>¥{totalCurrent.toFixed(2)}</span>
</div>
</div> </div>
{/* 结算方式 */} <div className='px-4 py-6 space-y-4'>
<div className='space-y-2'> {/* 选中项目信息 */}
<label className='text-sm text-gray-700 font-medium'></label> <div className='text-sm text-gray-700'>
<div className='flex gap-3'> <div className='font-medium mb-2'> ({selectedItems.length}):</div>
<button <div className='max-h-32 overflow-y-auto space-y-1'>
type='button' {selectedItems.map((item, idx) => (
onClick={() => setCustomSettlementType(1)} <div key={idx} className='text-xs text-gray-600 flex justify-between'>
className={cls( <span>{item.name}</span>
'flex-1 px-4 py-2 rounded-lg border text-sm transition-colors', <span>¥{parseFloat(item.currentPrice || item.originalPrice || '0').toFixed(2)}</span>
customSettlementType === 1 </div>
? 'bg-blue-50 border-blue-500 text-blue-700 font-medium' ))}
: 'bg-white border-gray-300 text-gray-700 hover:border-gray-400'
)}
>
</button>
<button
type='button'
onClick={() => setCustomSettlementType(2)}
className={cls(
'flex-1 px-4 py-2 rounded-lg border text-sm transition-colors',
customSettlementType === 2
? 'bg-blue-50 border-blue-500 text-blue-700 font-medium'
: 'bg-white border-gray-300 text-gray-700 hover:border-gray-400'
)}
>
</button>
</div>
</div>
{/* 折扣比例或最终结算价 */}
{customSettlementType === 1 ? (
<div className='space-y-3'>
<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)}
</span>
</div> </div>
<div className='flex items-center gap-2'> <div className='mt-2 pt-2 border-t text-sm font-semibold flex justify-between'>
<span>:</span>
<span className='text-red-600'>¥{totalCurrent.toFixed(2)}</span>
</div>
</div>
{/* 结算方式 */}
<div className='space-y-2'>
<label className='text-sm text-gray-700 font-medium'></label>
<div className='flex gap-3'>
<button
type='button'
onClick={() => setCustomSettlementType(1)}
className={cls(
'flex-1 px-4 py-2 rounded-lg border text-sm transition-colors',
customSettlementType === 1
? 'bg-blue-50 border-blue-500 text-blue-700 font-medium'
: 'bg-white border-gray-300 text-gray-700 hover:border-gray-400'
)}
>
</button>
<button
type='button'
onClick={() => setCustomSettlementType(2)}
className={cls(
'flex-1 px-4 py-2 rounded-lg border text-sm transition-colors',
customSettlementType === 2
? 'bg-blue-50 border-blue-500 text-blue-700 font-medium'
: 'bg-white border-gray-300 text-gray-700 hover:border-gray-400'
)}
>
</button>
</div>
</div>
{/* 折扣比例或最终结算价 */}
{customSettlementType === 1 ? (
<div className='space-y-3'>
<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 ?? 0) / 10).toFixed(1)}
</span>
</div>
<div className='flex items-center gap-2'>
<Input
type='number'
min='0'
max='100'
step='1'
value={customDiscountRatio ?? ''}
onChange={(e) => {
const raw = e.target.value;
if (raw === '') {
setCustomDiscountRatio(null);
return;
}
const val = Number(raw);
if (Number.isNaN(val)) return;
if (val < 1) {
setCustomDiscountRatio(1);
} else if (val > 100) {
setCustomDiscountRatio(100);
} else {
setCustomDiscountRatio(val);
}
}}
className='w-28 text-sm'
/>
<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 ?? 0) / 100)).toFixed(2)}</span>
</div>
</div>
) : (
<div className='space-y-2'>
<label className='text-sm text-gray-700 font-medium'> (¥)</label>
<Input <Input
type='number' type='number'
min='10' min='0'
max='100' step='0.01'
step='1' value={customFinalPrice || ''}
value={customDiscountRatio}
onChange={(e) => { onChange={(e) => {
const val = Number(e.target.value); const val = Number(e.target.value);
if (Number.isNaN(val)) return; if (val >= 0) {
if (val < 10) { setCustomFinalPrice(val);
setCustomDiscountRatio(10);
} else if (val > 100) {
setCustomDiscountRatio(100);
} else {
setCustomDiscountRatio(val);
} }
}} }}
className='w-28 text-sm' placeholder='请输入最终结算价'
className='text-sm'
/> />
<span className='text-xs text-gray-500'>10-100100 </span>
</div> </div>
<div className='text-xs text-gray-500'> )}
: <span className='font-semibold text-red-600'>¥{(totalCurrent * (customDiscountRatio / 100)).toFixed(2)}</span>
</div> {/* 申请理由 */}
</div>
) : (
<div className='space-y-2'> <div className='space-y-2'>
<label className='text-sm text-gray-700 font-medium'> (¥)</label> <label className='text-sm text-gray-700 font-medium'> *</label>
<Input <textarea
type='number' value={customApplyReason}
min='0' onChange={(e) => setCustomApplyReason(e.target.value)}
step='0.01' placeholder='请输入申请理由'
value={customFinalPrice || ''} rows={3}
onChange={(e) => { className='w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 resize-none'
const val = Number(e.target.value);
if (val >= 0) {
setCustomFinalPrice(val);
}
}}
placeholder='请输入最终结算价'
className='text-sm'
/> />
</div> </div>
)}
{/* 申请理由 */} {/* 提交按钮 */}
<div className='space-y-2'> <div className='flex gap-3 pt-2'>
<label className='text-sm text-gray-700 font-medium'> *</label> <Button
<textarea onClick={() => setShowCustomSettlementModal(false)}
value={customApplyReason} className='flex-1 px-4 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700'
onChange={(e) => setCustomApplyReason(e.target.value)} >
placeholder='请输入申请理由'
rows={3} </Button>
className='w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 resize-none' <Button
/> onClick={handleSubmitCustomSettlement}
</div> disabled={customSettlementLoading || !customApplyReason.trim()}
className={cls(
{/* 提交按钮 */} 'flex-1 px-4 py-2 text-white font-medium',
<div className='flex gap-3 pt-2'> customSettlementLoading || !customApplyReason.trim()
<Button ? 'bg-gray-400 cursor-not-allowed'
onClick={() => setShowCustomSettlementModal(false)} : 'bg-blue-600 hover:bg-blue-700'
className='flex-1 px-4 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700' )}
> >
{customSettlementLoading ? '提交中...' : '提交申请'}
</Button> </Button>
<Button </div>
onClick={handleSubmitCustomSettlement}
disabled={customSettlementLoading || !customApplyReason.trim()}
className={cls(
'flex-1 px-4 py-2 text-white font-medium',
customSettlementLoading || !customApplyReason.trim()
? 'bg-gray-400 cursor-not-allowed'
: 'bg-blue-600 hover:bg-blue-700'
)}
>
{customSettlementLoading ? '提交中...' : '提交申请'}
</Button>
</div> </div>
</div> </div>
</div> </div>
</div> )
)} }
{/* 审核中全屏遮罩 */} {/* 审核中全屏遮罩 */}
{customSettlementStatus?.apply_status === 1 && ( {
<div className='fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50'> customSettlementStatus?.apply_status === 1 && (
<div className='bg-white rounded-2xl p-6 max-w-md w-full mx-4 shadow-xl'> <div className='fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50'>
<div className='text-center mb-6'> <div className='bg-white rounded-2xl p-6 max-w-md w-full mx-4 shadow-xl'>
<div className='text-lg font-semibold text-blue-600 mb-2'>...</div> <div className='text-center mb-6'>
<div className='text-sm text-gray-500 mb-1'></div> <div className='text-lg font-semibold text-blue-600 mb-2'>...</div>
<div className='text-xs text-gray-400'> {waitingSeconds} </div> <div className='text-sm text-gray-500 mb-1'></div>
</div> <div className='text-xs text-gray-400'> {waitingSeconds} </div>
<div className='flex justify-center'> </div>
<Button <div className='flex justify-center'>
onClick={handleCancelCustomSettlement} <Button
disabled={customSettlementLoading} onClick={handleCancelCustomSettlement}
className='px-6 py-2 bg-gray-500 hover:bg-gray-600 text-white disabled:opacity-50' disabled={customSettlementLoading}
> className='px-6 py-2 bg-gray-500 hover:bg-gray-600 text-white disabled:opacity-50'
{customSettlementLoading ? '取消中...' : '取消申请'} >
</Button> {customSettlementLoading ? '取消中...' : '取消申请'}
</Button>
</div>
</div> </div>
</div> </div>
</div> )
)} }
</div> </div >
); );
}; };