完善打印

This commit is contained in:
xianyi
2026-01-06 16:42:56 +08:00
parent f17f9774ac
commit 973f5338af
3 changed files with 224 additions and 171 deletions

View File

@@ -356,17 +356,22 @@ export const ExamAddonPanel = ({ client }: ExamAddonPanelProps) => {
}; };
}, []); }, []);
// 获取加项PDF // 获取加项PDF(按本次支付的组合代码生成对应的加项单)
const fetchAddItemBillPdf = async (examId: number) => { const fetchAddItemBillPdf = async (examId: number, combinationItemCodes: string) => {
try { try {
// 调用接口获取加项PDF // 调用接口获取加项PDF
const res = await getAddItemBillPdf({ const res = await getAddItemBillPdf({
exam_id: examId, exam_id: examId,
CombinationCode: combinationItemCodes,
}); });
if (res.Status === 200 && res.Data?.pdf_url) { if (res.Status === 200 && res.Data?.pdf_url && res.Data?.pdf_sort !== undefined && res.Data?.pdf_sort !== null) {
// 保存加项PDF信息到localStorage未签名状态 // 保存加项PDF信息到localStorage未签名状态
setAddItemBillPdf(examId, { setAddItemBillPdf(examId, {
pdf_sort: res.Data.pdf_sort,
combinationCode: combinationItemCodes,
payment_status: res.Data.payment_status ?? null,
payment_status_name: res.Data.payment_status_name ?? null,
pdf_name: res.Data.pdf_name || '加项单', pdf_name: res.Data.pdf_name || '加项单',
pdf_url: res.Data.pdf_url, pdf_url: res.Data.pdf_url,
is_signed: false, is_signed: false,
@@ -392,7 +397,8 @@ export const ExamAddonPanel = ({ client }: ExamAddonPanelProps) => {
discount_rate: number; discount_rate: number;
}>, }>,
pay_type: number, pay_type: number,
company_id: number company_id: number,
combinationItemCodes: string
) => { ) => {
if (pollingTimerRef.current) { if (pollingTimerRef.current) {
clearInterval(pollingTimerRef.current); clearInterval(pollingTimerRef.current);
@@ -424,8 +430,8 @@ export const ExamAddonPanel = ({ client }: ExamAddonPanelProps) => {
setQrcodeUrl(null); setQrcodeUrl(null);
// 清空已选项目 // 清空已选项目
setSelectedIds(new Set()); setSelectedIds(new Set());
// 获取加项PDF // 获取本次支付对应的加项PDF
fetchAddItemBillPdf(physical_exam_id).then((success) => { fetchAddItemBillPdf(physical_exam_id, combinationItemCodes).then((success) => {
if (success) { if (success) {
setPaymentMessage('支付成功,加项单已生成'); setPaymentMessage('支付成功,加项单已生成');
} else { } else {
@@ -497,7 +503,7 @@ export const ExamAddonPanel = ({ client }: ExamAddonPanelProps) => {
return; return;
} }
// 获取组合项目代码(用于生成二维码接口,多个加项逗号分隔) // 获取组合项目代码(用于生成二维码接口 & 生成加项单,多个加项逗号分隔)
const combinationItemCodes = listAddItemCombination const combinationItemCodes = listAddItemCombination
.map((item) => item.combination_item_code) .map((item) => item.combination_item_code)
.join(','); .join(',');
@@ -534,7 +540,8 @@ export const ExamAddonPanel = ({ client }: ExamAddonPanelProps) => {
patient_id, patient_id,
listAddItemCombination, listAddItemCombination,
12, // 微信支付 12, // 微信支付
0 // 自费模式company_id 传 0 0, // 自费模式company_id 传 0
combinationItemCodes
); );
} else { } else {
setPaymentMessage(res.Message || '生成支付二维码失败'); setPaymentMessage(res.Message || '生成支付二维码失败');
@@ -564,8 +571,8 @@ export const ExamAddonPanel = ({ client }: ExamAddonPanelProps) => {
if (result === 'success' || result === '1' || result === 'SUCCESS') { if (result === 'success' || result === '1' || result === 'SUCCESS') {
setPaymentMessage('挂账成功,正在生成加项单...'); setPaymentMessage('挂账成功,正在生成加项单...');
setSelectedIds(new Set()); setSelectedIds(new Set());
// 获取加项PDF // 获取本次挂账对应的加项PDF
fetchAddItemBillPdf(physical_exam_id).then((success) => { fetchAddItemBillPdf(physical_exam_id, combinationItemCodes).then((success) => {
if (success) { if (success) {
setPaymentMessage('挂账成功,加项单已生成'); setPaymentMessage('挂账成功,加项单已生成');
} else { } else {

View File

@@ -13,6 +13,7 @@ import {
setAddItemBillPdf, setAddItemBillPdf,
getAddItemBillPdf as getAddItemBillPdfFromStorage, getAddItemBillPdf as getAddItemBillPdfFromStorage,
type TongyishuPdfInfo, type TongyishuPdfInfo,
type AddItemBillPdfInfo,
} from '../../utils/examActions'; } from '../../utils/examActions';
import type { SignaturePadHandle } from '../ui'; import type { SignaturePadHandle } from '../ui';
import { Button, SignaturePad } from '../ui'; import { Button, SignaturePad } from '../ui';
@@ -73,10 +74,9 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
const [daojiandanPdfReady, setDaojiandanPdfReady] = useState(false); const [daojiandanPdfReady, setDaojiandanPdfReady] = useState(false);
const [daojiandanPdfBlobUrl, setDaojiandanPdfBlobUrl] = useState<string | null>(null); const [daojiandanPdfBlobUrl, setDaojiandanPdfBlobUrl] = useState<string | null>(null);
const daojiandanCanvasContainerRef = useRef<HTMLDivElement>(null); const daojiandanCanvasContainerRef = useRef<HTMLDivElement>(null);
// 加项单相关状态 // 加项单相关状态(支持多个加项单)
const [addItemBillUrl, setAddItemBillUrl] = useState<string | null>(null); const [addItemBillList, setAddItemBillList] = useState<AddItemBillPdfInfo[]>([]);
const [addItemBillName, setAddItemBillName] = useState<string>('加项单'); const [currentAddItemBill, setCurrentAddItemBill] = useState<AddItemBillPdfInfo | null>(null);
const [isAddItemBillSigned, setIsAddItemBillSigned] = useState(false); // 加项单是否已签名
const [showAddItemBillPreview, setShowAddItemBillPreview] = useState(false); const [showAddItemBillPreview, setShowAddItemBillPreview] = useState(false);
const [showAddItemBillSignature, setShowAddItemBillSignature] = useState(false); const [showAddItemBillSignature, setShowAddItemBillSignature] = useState(false);
const addItemBillSignaturePadRef = useRef<SignaturePadHandle | null>(null); const addItemBillSignaturePadRef = useRef<SignaturePadHandle | null>(null);
@@ -362,31 +362,17 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
fetchDaojiandan(); fetchDaojiandan();
} }
// 检查加项单PDF逻辑和导检单一样 // 检查加项单PDF可能有多个
const storedAddItemBill = getAddItemBillPdfFromStorage(examId); const storedAddItemBillList = getAddItemBillPdfFromStorage(examId);
if (storedAddItemBill && storedAddItemBill.pdf_url) { if (storedAddItemBillList && storedAddItemBillList.length > 0) {
setAddItemBillUrl(storedAddItemBill.pdf_url); setAddItemBillList(storedAddItemBillList);
setAddItemBillName(storedAddItemBill.pdf_name || '加项单'); // 默认选中第一个未签名的加项单,如果都已签名则选中第一个
// 使用 localStorage 中保存的 is_signed 字段判断是否已签名 const unsigned = storedAddItemBillList.find((item) => item.is_signed !== true);
setIsAddItemBillSigned(storedAddItemBill.is_signed === true); setCurrentAddItemBill(unsigned || storedAddItemBillList[0]);
} else { } else {
// 如果 localStorage 中没有加项单,调用接口获取未签名的加项单用于查看和签名 // 没有加项单:不再主动调用接口获取,因为新接口需要 CombinationCode仅支付时知道
const fetchAddItemBill = async () => { setAddItemBillList([]);
try { setCurrentAddItemBill(null);
const res = await getAddItemBillPdfApi({ exam_id: examId });
if (res.Status === 200 && res.Data?.pdf_url) {
const pdfUrlValue = res.Data.pdf_url;
const pdfNameValue = res.Data.pdf_name || '加项单';
// 不保存到 localStorage只用于显示和签名
setAddItemBillUrl(pdfUrlValue);
setAddItemBillName(pdfNameValue);
setIsAddItemBillSigned(false); // 接口获取的是未签名的
}
} catch (err) {
console.error('获取加项单失败', err);
}
};
fetchAddItemBill();
} }
}, [examId]); }, [examId]);
@@ -578,9 +564,9 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
renderAllPages(); renderAllPages();
}, [daojiandanPdfData]); }, [daojiandanPdfData]);
// 加载加项单 PDF 数据 // 加载加项单 PDF 数据(当前选中的加项单)
useEffect(() => { useEffect(() => {
if (!showAddItemBillPreview || !addItemBillUrl) { if (!showAddItemBillPreview || !currentAddItemBill?.pdf_url) {
setAddItemBillPdfData(null); setAddItemBillPdfData(null);
setAddItemBillPdfBlobUrl(null); setAddItemBillPdfBlobUrl(null);
setAddItemBillPdfReady(false); setAddItemBillPdfReady(false);
@@ -592,7 +578,7 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
setAddItemBillPdfLoading(true); setAddItemBillPdfLoading(true);
setAddItemBillPdfData(null); setAddItemBillPdfData(null);
fetch(addItemBillUrl) fetch(currentAddItemBill.pdf_url)
.then((resp) => { .then((resp) => {
if (!resp.ok) throw new Error('获取PDF文件失败'); if (!resp.ok) throw new Error('获取PDF文件失败');
return resp.blob(); return resp.blob();
@@ -616,7 +602,7 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
URL.revokeObjectURL(objectUrl); URL.revokeObjectURL(objectUrl);
} }
}; };
}, [showAddItemBillPreview, addItemBillUrl]); }, [showAddItemBillPreview, currentAddItemBill?.pdf_url]);
// 渲染加项单 PDF // 渲染加项单 PDF
useEffect(() => { useEffect(() => {
@@ -736,29 +722,19 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
}; };
}, [showDaojiandanPreview, daojiandanUrl]); }, [showDaojiandanPreview, daojiandanUrl]);
// 加项单预览:使用 pdfjs 渲染到 canvas // 加项单预览:使用 pdfjs 渲染到 canvas(依赖加载好的 addItemBillPdfData
useEffect(() => { useEffect(() => {
if (!showAddItemBillPreview || !addItemBillUrl || !addItemBillCanvasContainerRef.current) return; if (!showAddItemBillPreview || !addItemBillPdfData || !addItemBillCanvasContainerRef.current) return;
const container = addItemBillCanvasContainerRef.current; const container = addItemBillCanvasContainerRef.current;
let cancelled = false; container.innerHTML = '';
const renderAddItemBill = async () => { const renderAddItemBill = async () => {
try { try {
setAddItemBillPdfLoading(true); const pdf = await pdfjsLib.getDocument({ data: addItemBillPdfData }).promise;
container.innerHTML = '';
const resp = await fetch(addItemBillUrl);
if (!resp.ok) throw new Error('获取PDF文件失败');
const blob = await resp.blob();
const arrayBuffer = await blob.arrayBuffer();
const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
const scale = 3.0; const scale = 3.0;
for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) { for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
if (cancelled) return;
const page = await pdf.getPage(pageNum); const page = await pdf.getPage(pageNum);
const viewport = page.getViewport({ scale }); const viewport = page.getViewport({ scale });
@@ -785,20 +761,15 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
} }
} catch (err) { } catch (err) {
console.error('加项单PDF 渲染失败', err); console.error('加项单PDF 渲染失败', err);
} finally {
if (!cancelled) {
setAddItemBillPdfLoading(false);
}
} }
}; };
renderAddItemBill(); renderAddItemBill();
return () => { return () => {
cancelled = true;
container.innerHTML = ''; container.innerHTML = '';
}; };
}, [showAddItemBillPreview, addItemBillUrl]); }, [showAddItemBillPreview, addItemBillPdfData]);
const handlePrint = () => { const handlePrint = () => {
if (!pdfBlobUrl || !pdfReady || !canvasContainerRef.current || !previewPdf) return; if (!pdfBlobUrl || !pdfReady || !canvasContainerRef.current || !previewPdf) return;
@@ -1104,13 +1075,18 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
} }
}; };
// 加项单签名提交 // 加项单签名提交(针对当前选中的加项单)
const handleSubmitAddItemBillSign = async () => { const handleSubmitAddItemBillSign = async () => {
if (!examId) { if (!examId) {
setAddItemBillSubmitMessage('缺少必要信息,无法提交签名'); setAddItemBillSubmitMessage('缺少必要信息,无法提交签名');
return; return;
} }
if (!currentAddItemBill) {
setAddItemBillSubmitMessage('没有可签名的加项单');
return;
}
const dataUrl = addItemBillSignaturePadRef.current?.toDataURL('image/png'); const dataUrl = addItemBillSignaturePadRef.current?.toDataURL('image/png');
if (!dataUrl) { if (!dataUrl) {
setAddItemBillSubmitMessage('请先完成签名'); setAddItemBillSubmitMessage('请先完成签名');
@@ -1124,6 +1100,8 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
const res = await submitAddItemBillSign({ const res = await submitAddItemBillSign({
exam_id: examId, exam_id: examId,
pdf_sort: currentAddItemBill.pdf_sort,
combinationCode: currentAddItemBill.combinationCode,
sign_file: blob, sign_file: blob,
}); });
@@ -1131,17 +1109,40 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
setAddItemBillSubmitMessage('签名提交成功'); setAddItemBillSubmitMessage('签名提交成功');
const pdfUrlValue = res.Data.pdf_url; const pdfUrlValue = res.Data.pdf_url;
const pdfNameValue = res.Data.pdf_name || '加项单'; const pdfNameValue = res.Data.pdf_name || '加项单';
setAddItemBillUrl(pdfUrlValue); // 更新当前加项单为已签名,并更新本地列表与 localStorage
setAddItemBillName(pdfNameValue); setAddItemBillList((prev) => {
setIsAddItemBillSigned(true); // 签名成功后标记为已签名 const next = prev.map((item) => {
if (item.pdf_sort === currentAddItemBill.pdf_sort) {
// 保存加项单PDF信息到localStorage标记为已签名 const updated: AddItemBillPdfInfo = {
setAddItemBillPdf(examId, { ...item,
pdf_name: pdfNameValue, pdf_name: pdfNameValue,
pdf_url: pdfUrlValue, pdf_url: pdfUrlValue,
is_signed: true, payment_status: res.Data?.payment_status ?? item.payment_status ?? null,
payment_status_name: res.Data?.payment_status_name ?? item.payment_status_name ?? null,
is_signed: true,
};
// 同步写入 localStorage
setAddItemBillPdf(examId, updated);
return updated;
}
return item;
});
return next;
}); });
setCurrentAddItemBill((prev) =>
prev && prev.pdf_sort === currentAddItemBill.pdf_sort
? {
...prev,
pdf_name: pdfNameValue,
pdf_url: pdfUrlValue,
payment_status: res.Data?.payment_status ?? prev.payment_status ?? null,
payment_status_name: res.Data?.payment_status_name ?? prev.payment_status_name ?? null,
is_signed: true,
}
: prev
);
setTimeout(() => { setTimeout(() => {
setShowAddItemBillSignature(false); setShowAddItemBillSignature(false);
setAddItemBillSubmitMessage(null); setAddItemBillSubmitMessage(null);
@@ -1160,11 +1161,11 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
}; };
// 加项单直接打印 // 加项单直接打印
const handleAddItemBillDirectPrint = async () => { const handleAddItemBillDirectPrint = async (target: AddItemBillPdfInfo | null) => {
if (!addItemBillUrl) return; if (!target?.pdf_url) return;
try { try {
const response = await fetch(addItemBillUrl); const response = await fetch(target.pdf_url);
if (!response.ok) throw new Error('获取PDF文件失败'); if (!response.ok) throw new Error('获取PDF文件失败');
const blob = await response.blob(); const blob = await response.blob();
const arrayBuffer = await blob.arrayBuffer(); const arrayBuffer = await blob.arrayBuffer();
@@ -1200,7 +1201,7 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>加项单打印</title> <title>${target.pdf_name || '加项单打印'}</title>
<style> <style>
@media print { @media print {
body { body {
@@ -1544,33 +1545,36 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
} }
// 处理加项单(如果有且已签名) // 处理加项单(如果有且已签名)
if (addItemBillUrl && isAddItemBillSigned) { if (addItemBillList.length > 0) {
try { try {
const response = await fetch(addItemBillUrl); for (const bill of addItemBillList) {
if (response.ok) { if (!bill.pdf_url || bill.is_signed !== true) continue;
const blob = await response.blob(); const response = await fetch(bill.pdf_url);
const arrayBuffer = await blob.arrayBuffer(); if (response.ok) {
const blob = await response.blob();
const arrayBuffer = await blob.arrayBuffer();
const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise; const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) { for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
const page = await pdf.getPage(pageNum); const page = await pdf.getPage(pageNum);
const viewport = page.getViewport({ scale }); const viewport = page.getViewport({ scale });
const canvas = document.createElement('canvas'); const canvas = document.createElement('canvas');
const context = canvas.getContext('2d'); const context = canvas.getContext('2d');
if (!context) continue; if (!context) continue;
canvas.height = viewport.height; canvas.height = viewport.height;
canvas.width = viewport.width; canvas.width = viewport.width;
const renderContext = { const renderContext = {
canvasContext: context, canvasContext: context,
viewport: viewport, viewport: viewport,
} as any; } as any;
await page.render(renderContext).promise; await page.render(renderContext).promise;
allImages.push(canvas.toDataURL('image/png')); allImages.push(canvas.toDataURL('image/png'));
}
} }
} }
} catch (err) { } catch (err) {
@@ -1913,73 +1917,75 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
)} )}
</div> </div>
</div> </div>
{/* 加项单 */} {/* 加项单列表(可能有多个) */}
{addItemBillUrl && ( {addItemBillList.length > 0 &&
<div className='flex items-center justify-between gap-3 p-2 rounded-xl border bg-white shadow-sm'> addItemBillList.map((bill) => {
<div className='text-sm text-gray-800 truncate flex items-center gap-2 relative pr-20'> const isSigned = bill.is_signed === true;
<span className='truncate'>{addItemBillName.length > 12 ? addItemBillName.slice(0, 12) + "..." : addItemBillName}</span> const isCurrent = currentAddItemBill && currentAddItemBill.pdf_sort === bill.pdf_sort;
{isAddItemBillSigned && ( const displayName =
<img bill.pdf_name && bill.pdf_name.length > 12 ? bill.pdf_name.slice(0, 12) + '...' : bill.pdf_name || '加项单';
src='/sign.png' return (
alt='已签名' <div
className='w-16 h-16 absolute right-2 top-1/2 -translate-y-1/2 pointer-events-none select-none' key={bill.pdf_sort}
loading='lazy' className='flex items-center justify-between gap-3 p-2 rounded-xl border bg-white shadow-sm'
/> >
)} <div className='text-sm text-gray-800 truncate flex items-center gap-2 relative pr-20'>
</div> <span className='truncate'>
<div className='flex items-center gap-2'> {displayName}
{isAddItemBillSigned ? ( {typeof bill.pdf_sort === 'number' ? `#${bill.pdf_sort}` : ''}
// 已签名:显示打印和查看按钮 </span>
<> {isSigned && (
<Button <img
className='py-1.5 px-3 bg-blue-600 hover:bg-blue-700 text-white' src='/sign.png'
onClick={() => { alt='已签名'
if (busy) return; className='w-16 h-16 absolute right-2 top-1/2 -translate-y-1/2 pointer-events-none select-none'
handleAddItemBillDirectPrint(); loading='lazy'
}} />
disabled={busy} )}
> </div>
<div className='flex items-center gap-2'>
</Button> {isSigned && bill.pdf_url && (
<Button <Button
className='py-1.5 px-3' className='py-1.5 px-3 bg-blue-600 hover:bg-blue-700 text-white'
onClick={() => { onClick={() => {
if (busy) return; if (busy) return;
setShowAddItemBillPreview(true); handleAddItemBillDirectPrint(bill);
}} }}
disabled={busy} disabled={busy}
> >
</Button> </Button>
</> )}
) : ( {bill.pdf_url && (
// 未签名:显示查看和签名按钮 <Button
<> className='py-1.5 px-3'
<Button onClick={() => {
className='py-1.5 px-3' if (busy) return;
onClick={() => { setCurrentAddItemBill(bill);
if (busy) return; setShowAddItemBillPreview(true);
setShowAddItemBillPreview(true); }}
}} disabled={busy}
disabled={busy} >
>
</Button>
</Button> )}
<Button {!isSigned && (
className='py-1.5 px-3' <Button
onClick={() => { className='py-1.5 px-3'
if (busy) return; onClick={() => {
setShowAddItemBillSignature(true); if (busy) return;
}} setCurrentAddItemBill(bill);
disabled={busy} setShowAddItemBillSignature(true);
> }}
disabled={busy}
</Button> >
</>
)} </Button>
</div> )}
</div> </div>
)} </div>
);
})}
</div> </div>
)} )}
</div> </div>
@@ -2213,21 +2219,21 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
) )
} }
{ {
showAddItemBillPreview && addItemBillUrl && ( showAddItemBillPreview && currentAddItemBill && currentAddItemBill.pdf_url && (
<div className='fixed inset-0 z-[60] bg-black/75 flex flex-col'> <div className='fixed inset-0 z-[60] bg-black/75 flex flex-col'>
<div className='flex items-center justify-between p-4 text-white bg-gray-900/80'> <div className='flex items-center justify-between p-4 text-white bg-gray-900/80'>
<div className='text-sm font-medium truncate pr-3'>{addItemBillName}</div> <div className='text-sm font-medium truncate pr-3'>{currentAddItemBill.pdf_name || '加项单'}</div>
<div className='flex items-center gap-2'> <div className='flex items-center gap-2'>
{isAddItemBillSigned && ( {currentAddItemBill.is_signed && (
<Button <Button
className='py-1 px-3 bg-blue-600 hover:bg-blue-700 text-white disabled:opacity-50 disabled:cursor-not-allowed' className='py-1 px-3 bg-blue-600 hover:bg-blue-700 text-white disabled:opacity-50 disabled:cursor-not-allowed'
onClick={handleAddItemBillDirectPrint} onClick={() => handleAddItemBillDirectPrint(currentAddItemBill)}
disabled={busy} disabled={busy}
> >
</Button> </Button>
)} )}
{!isAddItemBillSigned && ( {!currentAddItemBill.is_signed && (
<Button <Button
className='py-1 px-3 bg-blue-600 hover:bg-blue-700 text-white' className='py-1 px-3 bg-blue-600 hover:bg-blue-700 text-white'
onClick={() => { onClick={() => {

View File

@@ -196,6 +196,14 @@ export const getDaojiandanPdf = (examId: string | number): DaojiandanPdfInfo | n
* 加项单PDF信息 * 加项单PDF信息
*/ */
export interface AddItemBillPdfInfo { export interface AddItemBillPdfInfo {
/** 加项单编号(接口返回的 pdf_sort */
pdf_sort: number;
/** 加项组合代码多个加项项目用逗号拼接例如123,456 */
combinationCode: string;
/** 支付状态 (1-未支付 2-已支付) */
payment_status?: string | null;
/** 支付状态名称 (1-未支付 2-已支付) */
payment_status_name?: string | null;
/** PDF文件名称 */ /** PDF文件名称 */
pdf_name: string; pdf_name: string;
/** PDF文件地址 */ /** PDF文件地址 */
@@ -222,13 +230,38 @@ export const setAddItemBillPdf = (
if (typeof window === 'undefined') return; if (typeof window === 'undefined') return;
const key = getAddItemBillPdfKey(examId); const key = getAddItemBillPdfKey(examId);
localStorage.setItem(key, JSON.stringify(pdfInfo)); const raw = localStorage.getItem(key);
let list: AddItemBillPdfInfo[] = [];
if (raw) {
try {
const parsed = JSON.parse(raw);
if (Array.isArray(parsed)) {
list = parsed as AddItemBillPdfInfo[];
} else if (parsed && typeof parsed === 'object') {
// 兼容旧版本(单个对象)
list = [parsed as AddItemBillPdfInfo];
}
} catch (err) {
console.warn('加项单PDF信息解析失败将重置为新列表', err);
}
}
// 根据 pdf_sort 去重:同一个 pdf_sort 覆盖旧数据
const index = list.findIndex((item) => item.pdf_sort === pdfInfo.pdf_sort);
if (index >= 0) {
list[index] = pdfInfo;
} else {
list.push(pdfInfo);
}
localStorage.setItem(key, JSON.stringify(list));
}; };
/** /**
* 获取加项单PDF信息 * 获取加项单PDF信息
*/ */
export const getAddItemBillPdf = (examId: string | number): AddItemBillPdfInfo | null => { export const getAddItemBillPdf = (examId: string | number): AddItemBillPdfInfo[] | null => {
if (typeof window === 'undefined') return null; if (typeof window === 'undefined') return null;
const key = getAddItemBillPdfKey(examId); const key = getAddItemBillPdfKey(examId);
@@ -237,7 +270,14 @@ export const getAddItemBillPdf = (examId: string | number): AddItemBillPdfInfo |
try { try {
const parsed = JSON.parse(raw); const parsed = JSON.parse(raw);
return parsed as AddItemBillPdfInfo; if (Array.isArray(parsed)) {
return parsed as AddItemBillPdfInfo[];
}
if (parsed && typeof parsed === 'object') {
// 兼容旧版本(单个对象)
return [parsed as AddItemBillPdfInfo];
}
return null;
} catch (err) { } catch (err) {
console.warn('加项单PDF信息解析失败', err); console.warn('加项单PDF信息解析失败', err);
return null; return null;