import { useEffect, useMemo, useState, useRef, useCallback } from 'react'; import type { ExamClient } from '../../data/mockData'; import { searchPhysicalExamAddItem, getAddItemCustomerInfo, getChannelCompanyList, createNativePaymentQrcode, checkNativePaymentStatus, getAddItemBillPdf, getCustomSettlementApproveStatus, customSettlementApply, customSettlementApplyCancel } from '../../api'; import type { InputAddItemCombinationInfo, OutputCustomSettlementApplyApproveItem } from '../../api/types'; import { Button, Input } from '../ui'; import { cls } from '../../utils/cls'; import nozImage from '../../assets/image/noz.png'; interface AddonTag { title: string; type: 1 | 2 | 3 | 4; // 1: 热门(红), 2: 普通(灰), 3: 医生推荐(蓝), 4: 折扣信息 } interface AddonItem { id?: string; name: string; paid?: boolean; tags?: AddonTag[]; originalPrice?: string; currentPrice?: string; combinationItemCode?: number | null; isEnjoyDiscount?: number | null; discount_name?: string | null; discount_ratio?: number | null; } interface ExamAddonPanelProps { client: ExamClient; onGoToSign?: () => void; } export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => { const [addonList, setAddonList] = useState([]); const allAddons = useMemo(() => addonList, [addonList]); const [selectedIds, setSelectedIds] = useState>(new Set()); const currentAddItemId = useMemo(() => { const currentSelectedItems = allAddons.filter(item => selectedIds.has(item.id || item.name)); return currentSelectedItems .map(item => item.combinationItemCode) .filter((code): code is number => code !== null && code !== undefined) .join(','); }, [allAddons, selectedIds]); // 防抖:内部输入值(用于显示) const [addonSearchInput, setAddonSearchInput] = useState(''); // 防抖:实际用于 API 调用的值(延迟更新) const [debouncedAddonSearch, setDebouncedAddonSearch] = useState(''); const debounceTimerRef = useRef(null); const [addonLoading, setAddonLoading] = useState(false); const [addonError, setAddonError] = useState(null); // 折扣比例(1 = 100%) const [discountRatio, setDiscountRatio] = useState(1); // 渠道折扣列表 const [channelDiscounts, setChannelDiscounts] = useState< { channel_id?: string | null; channel_name?: string | null; discount_rate?: number | null; discount_name?: string | null }[] >([]); // 下拉框状态 const [isDiscountDropdownOpen, setIsDiscountDropdownOpen] = useState(false); const discountDropdownRef = useRef(null); // 结算方式:'self' 自费, 'account' 挂账 const [paymentMethod, setPaymentMethod] = useState<'self' | 'account'>('self'); const [isPaymentMethodDropdownOpen, setIsPaymentMethodDropdownOpen] = useState(false); const paymentMethodDropdownRef = useRef(null); // 挂账公司 const [accountCompany, setAccountCompany] = useState(''); const [isAccountCompanyDropdownOpen, setIsAccountCompanyDropdownOpen] = useState(false); const [accountCompanySearch, setAccountCompanySearch] = useState(''); const accountCompanyDropdownRef = useRef(null); // 挂账公司列表 const [accountCompanyList, setAccountCompanyList] = useState< Array<{ company_id: number; company_name?: string | null; pinyin?: string | null }> >([]); // 客户信息 const [customerInfo, setCustomerInfo] = useState<{ customer_name?: string | null; phone?: string | null; patient_id?: number | null; scrm_account_id?: string | null; scrm_account_name?: string | null; } | null>(null); // 支付相关状态 const [showQrcodeModal, setShowQrcodeModal] = useState(false); const [qrcodeUrl, setQrcodeUrl] = useState(null); const [paymentLoading, setPaymentLoading] = useState(false); const [paymentMessage, setPaymentMessage] = useState(null); const pollingTimerRef = useRef(null); // 跟踪客户信息是否已加载(用于避免重复请求加项列表) const customerInfoLoadedRef = useRef(false); // 自定义结算相关状态 const customSettlementPollingTimerRef = useRef(null); const lastFetchStatusTimeRef = useRef(0); // 上次获取审批状态的时间戳 const [showCustomSettlementModal, setShowCustomSettlementModal] = useState(false); const [customSettlementStatus, setCustomSettlementStatus] = useState<{ apply_status?: number; apply_status_name?: string | null; final_settlement_price?: number | null; apply_reason?: string | null; settlement_type?: number | null; discount_ratio?: number | null; add_item_id?: string | null; listAddItemCombination?: OutputCustomSettlementApplyApproveItem[] | null; } | null>(null); const [customSettlementLoading, setCustomSettlementLoading] = useState(false); const [customSettlementType, setCustomSettlementType] = useState<1 | 2>(1); // 1-按比例折扣 2-自定义结算价 const [customDiscountRatio, setCustomDiscountRatio] = useState(null); // 折扣比例(如100代表10折,即原价) const [customFinalPrice, setCustomFinalPrice] = useState(null); // 最终结算价 const [customApplyReason, setCustomApplyReason] = useState(''); // 申请理由 const [waitingSeconds, setWaitingSeconds] = useState(0); // 等待审核的秒数 const waitingTimerRef = useRef(null); // 等待计时器 useEffect(() => { if (showCustomSettlementModal && customSettlementStatus) { // 已通过(3) 或 已拒绝(4) 且 add_item_id 匹配才需要回显 if ((customSettlementStatus.apply_status === 3 || customSettlementStatus.apply_status === 4) && customSettlementStatus.add_item_id === currentAddItemId) { // 如果 discount_ratio 为 0,说明是自定义结算价模式 if (customSettlementStatus.discount_ratio === 0) { setCustomSettlementType(2); } else if (customSettlementStatus.settlement_type === 1 || customSettlementStatus.settlement_type === 2) { setCustomSettlementType(customSettlementStatus.settlement_type as 1 | 2); } if (typeof customSettlementStatus.discount_ratio === 'number') { setCustomDiscountRatio(customSettlementStatus.discount_ratio || null); } if (typeof customSettlementStatus.final_settlement_price === 'number') { setCustomFinalPrice(customSettlementStatus.final_settlement_price); } if (customSettlementStatus.apply_reason) { setCustomApplyReason(customSettlementStatus.apply_reason); } } else { // 其他状态(如取消后再点开)重置表单为默认值 setCustomSettlementType(1); setCustomDiscountRatio(null); setCustomFinalPrice(null); setCustomApplyReason(''); } } }, [showCustomSettlementModal, customSettlementStatus, currentAddItemId]); // 点击外部关闭下拉框 useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (discountDropdownRef.current && !discountDropdownRef.current.contains(event.target as Node)) { setIsDiscountDropdownOpen(false); } if (paymentMethodDropdownRef.current && !paymentMethodDropdownRef.current.contains(event.target as Node)) { setIsPaymentMethodDropdownOpen(false); } if (accountCompanyDropdownRef.current && !accountCompanyDropdownRef.current.contains(event.target as Node)) { setIsAccountCompanyDropdownOpen(false); } }; if (isDiscountDropdownOpen || isPaymentMethodDropdownOpen || isAccountCompanyDropdownOpen) { document.addEventListener('mousedown', handleClickOutside); } return () => { document.removeEventListener('mousedown', handleClickOutside); }; }, [isDiscountDropdownOpen, isPaymentMethodDropdownOpen, isAccountCompanyDropdownOpen]); // 获取体检加项客户信息(用于拿到渠道折扣等信息) useEffect(() => { const physical_exam_id = Number(client.id); if (!physical_exam_id) return; const fetchCustomerInfo = async () => { try { const res = await getAddItemCustomerInfo({ physical_exam_id }); if (res.Status === 200) { // 保存客户信息,渠道信息使用 listChannelDiscount 中的 channel_id / channel_name if (res.Data?.customerInfo) { 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: firstChannel?.channel_id ?? null, scrm_account_name: firstChannel?.channel_name ?? null, }); customerInfoLoadedRef.current = true; // 设置挂账公司默认值 const companyName = res.Data.customerInfo.company_name; if (companyName && companyName.trim() !== '') { setAccountCompany(companyName); setAccountCompanySearch(companyName); } else { setAccountCompany(''); setAccountCompanySearch(''); } } // 保存渠道折扣信息 if (res.Data?.listChannelDiscount && res.Data.listChannelDiscount.length > 0) { setChannelDiscounts(res.Data.listChannelDiscount); const rate = res.Data.listChannelDiscount[0]?.discount_rate; if (typeof rate === 'number' && rate > 0) { setDiscountRatio(rate); } } else { setChannelDiscounts([]); } } else { setChannelDiscounts([]); } } catch (err) { console.error('获取加项客户信息失败', err); } finally { // 无论成功或失败,都标记为已尝试加载 customerInfoLoadedRef.current = true; } }; // 当 client.id 变化时,重置加载状态 customerInfoLoadedRef.current = false; fetchCustomerInfo(); }, [client.id]); // 获取挂账公司列表 useEffect(() => { const fetchCompanyList = async () => { try { const res = await getChannelCompanyList({}); if (res.Status === 200 && Array.isArray(res.Data) && res.Data.length > 0) { setAccountCompanyList(res.Data); } else { setAccountCompanyList([]); } } catch (err) { console.error('获取挂账公司列表失败', err); setAccountCompanyList([]); } }; fetchCompanyList(); }, []); // 当挂账公司列表更新时,如果当前选中的公司不在列表中,清空选择 useEffect(() => { if (accountCompany && accountCompanyList.length > 0) { const companyNames = accountCompanyList.map((c) => c.company_name).filter(Boolean) as string[]; if (!companyNames.includes(accountCompany)) { // 如果当前选中的公司不在列表中,清空选择 setAccountCompany(''); setAccountCompanySearch(''); } } }, [accountCompanyList, accountCompany]); // 防抖:当输入值变化时,延迟 0.5 秒后更新 debouncedAddonSearch(用于 API 调用) useEffect(() => { if (debounceTimerRef.current) { window.clearTimeout(debounceTimerRef.current); } debounceTimerRef.current = window.setTimeout(() => { setDebouncedAddonSearch(addonSearchInput); }, 500); return () => { if (debounceTimerRef.current) { window.clearTimeout(debounceTimerRef.current); } }; }, [addonSearchInput]); // 拉取加项列表 useEffect(() => { if (!customerInfoLoadedRef.current && customerInfo === null) { return; } const fetchList = async () => { 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) => ({ id: item.item_id ? String(item.item_id) : `addon_${item.item_name}`, name: item.item_name || '', originalPrice: item.original_price !== undefined && item.original_price !== null ? Number(item.original_price).toFixed(2) : '0.00', currentPrice: item.actual_received_amount !== undefined && item.actual_received_amount !== null ? Number(item.actual_received_amount).toFixed(2) : '0.00', combinationItemCode: item.combination_item_code ?? null, isEnjoyDiscount: item.is_enjoy_discount ?? null, discount_name: item.discount_rate ?? null, discount_ratio: item.discount_ratio ?? null, tags: [], paid: false, })); setAddonList(list); } else { setAddonError(res.Message || '获取加项列表失败'); setAddonList([]); } } catch (err) { console.error('获取加项列表失败', err); setAddonError('获取加项列表失败,请稍后重试'); setAddonList([]); } finally { setAddonLoading(false); } }; fetchList(); }, [debouncedAddonSearch, customerInfo?.scrm_account_id, customerInfo?.scrm_account_name, client.id, customerInfo, channelDiscounts, discountRatio]); const maxSelect = 15; const selectedCount = selectedIds.size; const filteredAddons = allAddons; const toggleSelect = (id: string) => { if (selectedIds.has(id)) { setSelectedIds(prev => { const next = new Set(prev); next.delete(id); return next; }); } else { if (selectedCount < maxSelect) { setSelectedIds(prev => new Set(prev).add(id)); } } }; // 计算价格汇总 const selectedItems = allAddons.filter(item => selectedIds.has(item.id || item.name)); const totalOriginal = selectedItems.reduce((sum, item) => { return sum + parseFloat(item.originalPrice || item.currentPrice || '0'); }, 0); const totalCurrentBase = selectedItems.reduce((sum, item) => { return sum + parseFloat(item.currentPrice || item.originalPrice || '0'); }, 0); // 如果自定义结算审核通过,使用审核后的金额 const totalCurrent = (customSettlementStatus?.apply_status === 3 && typeof customSettlementStatus.final_settlement_price === 'number') ? customSettlementStatus.final_settlement_price : totalCurrentBase; const discount = totalOriginal - totalCurrent; // 获取标签样式 const getTagStyle = (tag: AddonTag) => { switch (tag.type) { case 1: // 热门 return 'bg-[#FDF0F0] text-[#BC4845]'; case 3: // 医生推荐 return 'bg-[#ECF0FF] text-[#6A6AE5]'; case 4: // 折扣信息 return 'bg-[#4C5460] text-[#F1F2F5]'; default: // 2: 普通 return 'bg-[#F1F2F5] text-[#464E5B]'; } }; // 获取折扣信息文字(从 tags 中提取 type 4 的标签,或计算折扣) const getDiscountText = (item: AddonItem) => { return item.discount_name; }; // 构建折扣选项列表(带上渠道ID/名称,便于联动设置 scrm_account_id / scrm_account_name) const discountOptions = useMemo(() => { 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, channel_id: item.channel_id ?? null, channel_name: item.channel_name ?? null, }); }); return options; }, [channelDiscounts]); // 获取当前选中的标签 const currentDiscountLabel = useMemo(() => { const option = discountOptions.find(opt => opt.value === discountRatio); 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); }; // 处理结算方式选择 const handlePaymentMethodSelect = (value: 'self' | 'account') => { setPaymentMethod(value); setIsPaymentMethodDropdownOpen(false); }; // 处理挂账公司选择 const handleAccountCompanySelect = (value: string, label: string) => { setAccountCompany(value); setAccountCompanySearch(label); setIsAccountCompanyDropdownOpen(false); }; // 清空挂账公司 const handleClearAccountCompany = () => { setAccountCompany(''); setAccountCompanySearch(''); setIsAccountCompanyDropdownOpen(false); }; // 结算方式选项 const paymentMethodOptions: Array<{ value: 'self' | 'account'; label: string }> = [ { value: 'self', label: '自费' }, { value: 'account', label: '挂账' }, ]; // 挂账公司选项(从接口获取) const accountCompanyOptions = useMemo(() => { if (accountCompanyList.length === 0) { // 如果没有数据,返回默认的"圆和" return [{ value: '圆和', label: '圆和' }]; } return accountCompanyList.map((company) => ({ value: company.company_name || `公司${company.company_id}`, label: company.company_name || `公司${company.company_id}`, })); }, [accountCompanyList]); // 挂账公司模糊过滤 const filteredAccountCompanyOptions = useMemo(() => { const kw = accountCompanySearch.trim().toLowerCase(); if (!kw) return accountCompanyOptions; return accountCompanyOptions.filter((opt) => opt.label.toLowerCase().includes(kw)); }, [accountCompanyOptions, accountCompanySearch]); // 清理轮询定时器 useEffect(() => { return () => { if (pollingTimerRef.current) { clearInterval(pollingTimerRef.current); pollingTimerRef.current = null; } }; }, []); // 获取自定义结算审批状态(带节流,最多每秒一次) const fetchCustomSettlementStatus = useCallback(async () => { const physical_exam_id = Number(client.id); if (!physical_exam_id || !currentAddItemId) { setCustomSettlementStatus(null); return false; } // 节流:如果距离上次调用不足0.8秒,则跳过 const now = Date.now(); const timeSinceLastCall = now - lastFetchStatusTimeRef.current; if (timeSinceLastCall < 800) { // 从 ref 获取当前状态判断是否需要轮询 return customSettlementStatus?.apply_status === 1; } lastFetchStatusTimeRef.current = now; try { const res = await getCustomSettlementApproveStatus({ physical_exam_id, add_item_id: currentAddItemId, }); if (res.Status === 200 && res.Data) { const status = { apply_status: res.Data.apply_status, apply_status_name: res.Data.apply_status_name, final_settlement_price: res.Data.final_settlement_price, apply_reason: (res.Data as any).apply_reason, settlement_type: (res.Data as any).settlement_type, discount_ratio: (res.Data as any).discount_ratio, add_item_id: currentAddItemId, listAddItemCombination: res.Data.listAddItemCombination, }; // 获取旧状态用于判断是否刚进入审核中 setCustomSettlementStatus(prev => { if (status.apply_status === 1 && prev?.apply_status !== 1) { setWaitingSeconds(0); } return status; }); // 返回是否需要继续轮询(1-审核中 需要轮询) return res.Data.apply_status === 1; } else { setCustomSettlementStatus(null); return false; } } catch (err) { console.error('获取自定义结算审批状态失败', err); setCustomSettlementStatus(null); return false; } }, [client.id, currentAddItemId]); // 当选中加项变化时,获取审批状态 useEffect(() => { // 切换选中项时,先清零状态,避免错误回显旧数据 setCustomSettlementStatus(null); setCustomSettlementType(1); setCustomDiscountRatio(null); setCustomFinalPrice(null); setCustomApplyReason(''); lastFetchStatusTimeRef.current = 0; // 强制立即发起新请求 if (currentAddItemId) { fetchCustomSettlementStatus().then((shouldPoll) => { // 如果需要轮询(审核中),开始轮询 if (shouldPoll) { startCustomSettlementPolling(); } else { stopCustomSettlementPolling(); } }); } else { stopCustomSettlementPolling(); } }, [currentAddItemId, client.id]); // 移除 fetchCustomSettlementStatus 依赖,避免由于状态更新导致的死循环 // 开始轮询自定义结算审批状态 const startCustomSettlementPolling = useCallback(() => { // 清除之前的定时器 stopCustomSettlementPolling(); // 每1秒轮询一次 customSettlementPollingTimerRef.current = window.setInterval(() => { fetchCustomSettlementStatus().then((shouldPoll) => { // 如果不再需要轮询(审核完成),停止轮询 if (!shouldPoll) { stopCustomSettlementPolling(); } }); }, 1500); }, [fetchCustomSettlementStatus]); // 停止轮询自定义结算审批状态 const stopCustomSettlementPolling = useCallback(() => { if (customSettlementPollingTimerRef.current) { clearInterval(customSettlementPollingTimerRef.current); customSettlementPollingTimerRef.current = null; } }, []); // 清理轮询定时器 useEffect(() => { return () => { stopCustomSettlementPolling(); }; }, [stopCustomSettlementPolling]); // 等待计时器:当审核中时开始计时 useEffect(() => { if (customSettlementStatus?.apply_status === 1) { // 开始计时 setWaitingSeconds(0); waitingTimerRef.current = window.setInterval(() => { setWaitingSeconds((prev) => prev + 1); }, 1000); } else { // 停止计时并重置 if (waitingTimerRef.current) { clearInterval(waitingTimerRef.current); waitingTimerRef.current = null; } setWaitingSeconds(0); } return () => { if (waitingTimerRef.current) { clearInterval(waitingTimerRef.current); waitingTimerRef.current = null; } }; }, [customSettlementStatus?.apply_status]); // 提交自定义结算申请 const handleSubmitCustomSettlement = async () => { const physical_exam_id = Number(client.id); if (!physical_exam_id || selectedItems.length === 0) { setPaymentMessage('请先选择加项项目'); return; } if (!customApplyReason.trim()) { setPaymentMessage('请输入申请理由'); return; } const discountRatioValue = customSettlementType === 1 ? (customDiscountRatio ?? 0) : 0; if (customSettlementType === 1 && (discountRatioValue <= 0 || discountRatioValue > 100)) { setPaymentMessage('请输入有效折扣比例'); return; } setCustomSettlementLoading(true); setPaymentMessage(null); try { // 构建加项项目明细列表 const listAddItemDetail = selectedItems .map(item => { const combinationItemCode = item.combinationItemCode; if (combinationItemCode === null || combinationItemCode === undefined) { return null; } const originalPrice = parseFloat(item.originalPrice || '0'); // if (customSettlementType === 1) { // // 按比例折扣 // settlementPrice = originalPrice * (discountRatioValue / 100); // } else { // // 自定义结算价 // settlementPrice = (customFinalPrice ?? 0) / selectedItems.length; // 平均分配 // } const discount_ratio = (() => { const r = item.discount_ratio; if (r == null || typeof r !== 'number') return 0; return r > 1 ? r : Math.round(r * 100); })(); return { combination_item_code: String(combinationItemCode), combination_item_name: item.name, original_price: originalPrice, settlement_price: originalPrice * (discount_ratio / 100), discount_ratio: discount_ratio, }; }) .filter((item): item is { combination_item_code: string; combination_item_name: string; original_price: number; settlement_price: number; discount_ratio: number } => item !== null); const original_settlement_price = listAddItemDetail.reduce((sum, item) => sum + item.original_price, 0); const final_settlement_price = customSettlementType === 1 ? totalCurrent * ((customDiscountRatio ?? 0) / 100) : (customFinalPrice ?? 0); const apply_user = localStorage.getItem('operatorName'); if (!apply_user) { alert('请先登录'); window.location.href = '/home'; return; } const res = await customSettlementApply({ physical_exam_id, listAddItemDetail, original_settlement_price, settlement_type: customSettlementType, discount_ratio: customSettlementType === 1 ? discountRatioValue : undefined, final_settlement_price: final_settlement_price ?? undefined, apply_reason: customApplyReason.trim(), apply_user, }); if (res.Status === 200) { setPaymentMessage('自定义结算申请提交成功'); setShowCustomSettlementModal(false); // 重置表单 setCustomSettlementType(1); setCustomDiscountRatio(100); setCustomFinalPrice(null); setCustomApplyReason(''); // 重新获取审批状态并开始轮询 setTimeout(() => { fetchCustomSettlementStatus().then((shouldPoll) => { if (shouldPoll) { startCustomSettlementPolling(); } }); }, 500); } else { setPaymentMessage(res.Message || '提交失败,请稍后重试'); } } catch (err) { console.error('提交自定义结算申请失败', err); setPaymentMessage('提交失败,请稍后重试'); } finally { setCustomSettlementLoading(false); } }; // 取消自定义结算申请 const handleCancelCustomSettlement = async () => { const physical_exam_id = Number(client.id); if (!physical_exam_id || selectedItems.length === 0) { setPaymentMessage('请先选择加项项目'); return; } if (!window.confirm('确定要取消自定义结算申请吗?')) { return; } stopCustomSettlementPolling(); setCustomSettlementLoading(true); setPaymentMessage(null); try { const add_item_id = selectedItems .map(item => item.combinationItemCode) .filter((code): code is number => code !== null && code !== undefined) .join(','); if (!add_item_id) { setPaymentMessage('无法获取加项项目ID'); return; } const cancel_user = localStorage.getItem('operatorName'); if (!cancel_user) { setPaymentMessage('请先登录'); return; } const res = await customSettlementApplyCancel({ physical_exam_id, add_item_id, cancel_user, }); if (res.Status === 200 && res.Data?.is_success === 1) { setPaymentMessage('取消申请成功'); setCustomDiscountRatio(null); // 停止轮询 stopCustomSettlementPolling(); // 重新获取审批状态 setTimeout(() => { fetchCustomSettlementStatus(); }, 500); } else { setPaymentMessage(res.Message || '取消申请失败,请稍后重试'); } } catch (err) { console.error('取消自定义结算申请失败', err); setPaymentMessage('取消申请失败,请稍后重试'); } finally { setCustomSettlementLoading(false); } }; // 获取加项PDF(按本次支付的组合代码生成对应的加项单) const fetchAddItemBillPdf = async (examId: number, combinationItemCodes: string) => { try { // 调用接口获取加项PDF const res = await getAddItemBillPdf({ exam_id: examId, CombinationCode: combinationItemCodes, }); if (res.Status === 200) { return true; } else { console.error('获取加项PDF失败', res.Message); return false; } } catch (err) { console.error('获取加项PDF失败', err); return false; } }; // 轮询查询支付结果 const startPaymentPolling = ( physical_exam_id: number, patient_id: number, listAddItemCombination: InputAddItemCombinationInfo[], pay_type: number, company_id: number, combinationItemCodes: string, orderAmount: number ) => { if (pollingTimerRef.current) { clearInterval(pollingTimerRef.current); } pollingTimerRef.current = window.setInterval(async () => { try { const res = await checkNativePaymentStatus({ physical_exam_id, patient_id, listAddItemCombination, pay_type, company_id, orderAmount, }); if (res.Status === 200) { const result = res.Data; console.log(result); // 支付成功:返回 "true" if (result === 'true') { // 支付成功,停止轮询 if (pollingTimerRef.current) { clearInterval(pollingTimerRef.current); pollingTimerRef.current = null; } setPaymentMessage('支付成功,正在生成加项单...'); setShowQrcodeModal(false); setQrcodeUrl(null); // 清空已选项目 setSelectedIds(new Set()); // 获取本次支付对应的加项PDF fetchAddItemBillPdf(physical_exam_id, combinationItemCodes).then((success) => { if (success) { setPaymentMessage('支付成功,加项单已生成,正在跳转签署...'); // 跳转到签署页面 onGoToSign?.(); } else { setPaymentMessage('支付成功,但加项单生成失败'); } setTimeout(() => { setPaymentMessage(null); }, 3000); }); } else if (result === 'false') { // 支付失败,停止轮询 if (pollingTimerRef.current) { clearInterval(pollingTimerRef.current); pollingTimerRef.current = null; } setPaymentMessage('支付失败'); setTimeout(() => { setPaymentMessage(null); }, 3000); } // 其他状态(如 "pending"、"processing" 等)继续轮询 } } catch (err) { console.error('查询支付状态失败', err); } }, 2000); // 每2秒轮询一次 }; // 处理支付 const handlePayment = async () => { if (selectedCount === 0) return; const physical_exam_id = Number(client.id); if (!physical_exam_id) { setPaymentMessage('缺少体检ID'); return; } if (!customerInfo?.customer_name || !customerInfo?.phone) { setPaymentMessage('缺少客户信息,请稍后重试'); return; } setPaymentLoading(true); setPaymentMessage(null); try { const selectedItems = allAddons.filter((item) => selectedIds.has(item.id || item.name)); // 构建加项组合项目信息列表 let listAddItemCombination: InputAddItemCombinationInfo[] = []; // 如果自定义结算审核通过,直接使用接口返回的项目列表 if (customSettlementStatus?.apply_status === 3 && customSettlementStatus.listAddItemCombination) { listAddItemCombination = customSettlementStatus.listAddItemCombination.map((item: OutputCustomSettlementApplyApproveItem) => ({ combination_item_code: item.combination_item_code, combination_item_price: item.combination_item_price, discount_rate: item.discount_rate })); } else { listAddItemCombination = selectedItems .map((item) => { const combinationItemCode = item.combinationItemCode; if (combinationItemCode === null || combinationItemCode === undefined) { return null; } const combinationItemPrice = parseFloat(item.currentPrice || item.originalPrice || '0'); return { combination_item_code: String(combinationItemCode), combination_item_price: combinationItemPrice, discount_rate: discountRatio, } as InputAddItemCombinationInfo; }) .filter((item): item is InputAddItemCombinationInfo => item !== null); } if (listAddItemCombination.length === 0) { setPaymentMessage('缺少加项信息,请稍后重试'); setPaymentLoading(false); return; } // 获取组合项目代码(用于生成二维码接口 & 生成加项单,多个加项逗号分隔) const combinationItemCodes = listAddItemCombination .map((item) => item.combination_item_code) .filter(Boolean) .join(','); // 获取 patient_id(必须从接口返回的客户信息中获取) if (!customerInfo?.patient_id) { setPaymentMessage('缺少患者ID,请稍后重试'); setPaymentLoading(false); return; } const patient_id = customerInfo.patient_id; if (paymentMethod === 'self') { // 自费模式:如果是 0 元支付,直接查询状态,不生成二维码 if (totalCurrent === 0) { startPaymentPolling( physical_exam_id, patient_id, listAddItemCombination, 12, // 微信支付 0, // 自费模式,company_id 传 0 combinationItemCodes, 0 ); return; } // 自费模式:生成二维码 const res = await createNativePaymentQrcode({ physical_exam_id, userName: customerInfo.customer_name!, userPhone: customerInfo.phone!, combinationItemCodes, orderAmount: totalCurrent, }); if (res.Status === 200 && res.Data) { // res.Data 是微信支付 URL(如:weixin://wxpay/bizpayurl?pr=xxx) // 需要将 URL 转换为二维码图片 const paymentUrl = res.Data; // 使用在线二维码生成 API 生成二维码图片 const qrcodeImageUrl = `https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=${encodeURIComponent(paymentUrl)}`; setQrcodeUrl(qrcodeImageUrl); setShowQrcodeModal(true); // 开始轮询(自费模式:pay_type=12, company_id=0) startPaymentPolling( physical_exam_id, patient_id, listAddItemCombination, 12, // 微信支付 0, // 自费模式,company_id 传 0 combinationItemCodes, totalCurrent ); } else { setPaymentMessage(res.Message || '生成支付二维码失败'); } } else { // 挂账模式:直接查询 // 验证是否选择了挂账公司 console.log('accountCompany', accountCompany); if (!accountCompany || accountCompany.trim() === '') { setPaymentMessage('请选择挂账公司'); setPaymentLoading(false); return; } const selectedCompany = accountCompanyList.find( (c) => c.company_name === accountCompany ); if (!selectedCompany) { setPaymentMessage('请选择挂账公司'); setPaymentLoading(false); return; } // 挂账模式:直接查询(pay_type=13, company_id=选中的公司ID) const res = await checkNativePaymentStatus({ physical_exam_id, patient_id, listAddItemCombination, pay_type: 13, // 挂账公司 company_id: selectedCompany.company_id, orderAmount: totalCurrent, }); if (res.Status === 200) { const result = res.Data; if (result === 'true') { setPaymentMessage('挂账成功,正在生成加项单...'); setSelectedIds(new Set()); // 获取本次挂账对应的加项PDF fetchAddItemBillPdf(physical_exam_id, combinationItemCodes).then((success) => { if (success) { setPaymentMessage('挂账成功,加项单已生成,正在跳转签署...'); onGoToSign?.(); } else { setPaymentMessage('挂账成功,但加项单生成失败'); } setTimeout(() => { setPaymentMessage(null); }, 3000); }); } else { setPaymentMessage('挂账失败'); setTimeout(() => { setPaymentMessage(null); }, 3000); } } else { setPaymentMessage(res.Message || '挂账失败'); } } } catch (err) { console.error('支付处理失败', err); setPaymentMessage('支付处理失败,请稍后重试'); } finally { setPaymentLoading(false); } }; const isApprovedOrRejected = (customSettlementStatus?.apply_status === 3 || customSettlementStatus?.apply_status === 4) && customSettlementStatus?.add_item_id === currentAddItemId; return (
{/* 标题和说明 */}

体检套餐加项选择

{/* 折扣比例 */}
{isDiscountDropdownOpen && (
{discountOptions.map((option) => ( ))}
)}
setAddonSearchInput(e.target.value)} className='text-sm flex-1' /> {addonSearchInput && ( )}
{/*
最多可选 {maxSelect} 项 · 一排 5 个
已勾选 {selectedCount} 项,自费加项费用按渠道折扣价结算。
*/}
{/* 加项网格 */}
{addonError && (
{addonError}
)} {addonLoading && (
加载中...
)} {!addonLoading && !addonError && filteredAddons.length === 0 && (
暂无数据
)} {filteredAddons.map((item) => { const id = item.id || item.name; const isSelected = selectedIds.has(id); const origPrice = parseFloat(item.originalPrice || '0'); const currPrice = parseFloat(item.currentPrice || '0'); return (
toggleSelect(id)} > {/* 无折扣标签图片 - 浮动在右上角(当 is_enjoy_discount 为 0 或 null 时显示) */} {(!item.isEnjoyDiscount || item.isEnjoyDiscount === 0) && ( 无折扣 )} {/* 第一行:复选框 + 名称 */}
toggleSelect(id)} onClick={(e) => e.stopPropagation()} className='mt-0.5 w-4 h-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500' />
{item.name}
{/* 第二行:标签 */} {item.tags && item.tags.length >= 0 && (
{item.tags .filter(t => t.type !== 4) // 折扣信息单独显示 .map((tag, idx) => ( {tag.title} ))}
)} {/* 第三行:价格信息(固定在卡片底部) */}
{origPrice >= 0 && origPrice >= currPrice && ( 原价:¥{origPrice.toFixed(2)} )} {getDiscountText(item)} 折后价:¥{currPrice.toFixed(2)} {/*
{getDiscountText(item)}
*/}
); })}
{/* 底部汇总和支付 */}
原价: ¥{totalOriginal.toFixed(2)}
优惠: ¥{discount.toFixed(2)}
结算价: ¥{(totalCurrent).toFixed(2)}
{/*
结算方式: 个人支付 (微信 / 支付宝)
*/}
{/* 添加自定义结算 */} {localStorage.getItem('authCode')?.includes('HisTijian_Btn_JiesuanShenqing') && selectedItems.length > 0 && (
)}
{paymentMessage && (
)}
{/* 结算方式 */}
结算方式
{isPaymentMethodDropdownOpen && (
{paymentMethodOptions.map((option) => ( ))}
)}
{/* 挂账公司 */} {paymentMethod === 'account' && (
挂账公司
setIsAccountCompanyDropdownOpen(true)} > { setAccountCompanySearch(e.target.value); setIsAccountCompanyDropdownOpen(true); }} placeholder={'请输入公司名称'} className='w-full outline-none text-xs bg-transparent' onClick={(e) => e.stopPropagation()} />
{accountCompanySearch && ( )}
{isAccountCompanyDropdownOpen && (
{filteredAccountCompanyOptions.length === 0 && (
无匹配结果
)} {filteredAccountCompanyOptions.map((option, idx) => ( ))}
)}
)}
{/* 二维码支付弹窗 */} { showQrcodeModal && qrcodeUrl && (
扫码支付
支付二维码
请使用微信扫描上方二维码完成支付
{paymentMessage && (
{paymentMessage}
)}
) } {/* 自定义结算弹窗 */} { showCustomSettlementModal && (
setShowCustomSettlementModal(false)}>
e.stopPropagation()} >
自定义结算申请
{/* 选中项目信息 */}
选中项目 ({selectedItems.length}项):
{selectedItems.map((item, idx) => (
{item.name} ¥{parseFloat(item.currentPrice || item.originalPrice || '0').toFixed(2)}
))}
渠道折扣价合计: ¥{totalCurrent.toFixed(2)}
{/* 结算方式 */}
{/* 折扣比例或最终结算价 */} {customSettlementType === 1 ? (
{((customDiscountRatio ?? 0) / 10).toFixed(1)}折
{ 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' /> 1-100,100 表示不打折
最终结算价: ¥{(totalCurrent * ((customDiscountRatio ?? 0) / 100)).toFixed(2)}
) : (
{ const rawValue = e.target.value; if (rawValue === '') { setCustomFinalPrice(null); return; } const val = Number(rawValue); if (val >= 0) { setCustomFinalPrice(val); } }} placeholder='请输入最终结算价' className='text-sm' />
)} {/* 申请理由 */}