更新新签到
This commit is contained in:
@@ -4,7 +4,6 @@ import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.min.mjs?url';
|
|||||||
|
|
||||||
import { getDaojiandanPdf as getDaojiandanPdfApi, submitDaojiandanSign, editDaojiandanPrintStatus } from '../../api';
|
import { getDaojiandanPdf as getDaojiandanPdfApi, submitDaojiandanSign, editDaojiandanPrintStatus } from '../../api';
|
||||||
import type { ExamClient } from '../../data/mockData';
|
import type { ExamClient } from '../../data/mockData';
|
||||||
import { setExamActionRecord, setDaojiandanPdf, getDaojiandanPdf } from '../../utils/examActions';
|
|
||||||
import type { SignaturePadHandle } from '../ui';
|
import type { SignaturePadHandle } from '../ui';
|
||||||
import { Button, SignaturePad } from '../ui';
|
import { Button, SignaturePad } from '../ui';
|
||||||
|
|
||||||
@@ -69,17 +68,7 @@ export const ExamPrintPanel = ({ client }: { client: ExamClient }) => {
|
|||||||
setSubmitMessage('签名提交成功,正在加载导检单...');
|
setSubmitMessage('签名提交成功,正在加载导检单...');
|
||||||
setSignatureSubmitted(true);
|
setSignatureSubmitted(true);
|
||||||
const pdfUrlValue = res.Data.pdf_url;
|
const pdfUrlValue = res.Data.pdf_url;
|
||||||
const pdfNameValue = res.Data.pdf_name || '导检单';
|
|
||||||
setPdfUrl(pdfUrlValue);
|
setPdfUrl(pdfUrlValue);
|
||||||
// 保存导检单PDF信息到localStorage
|
|
||||||
setDaojiandanPdf(examId, {
|
|
||||||
pdf_name: pdfNameValue,
|
|
||||||
pdf_url: pdfUrlValue,
|
|
||||||
is_signed: true,
|
|
||||||
});
|
|
||||||
// 记录打印导检单是否签名操作
|
|
||||||
setExamActionRecord(examId, 'printSign', true);
|
|
||||||
|
|
||||||
// 更新导检单打印状态
|
// 更新导检单打印状态
|
||||||
try {
|
try {
|
||||||
await editDaojiandanPrintStatus({ exam_id: examId });
|
await editDaojiandanPrintStatus({ exam_id: examId });
|
||||||
@@ -102,12 +91,6 @@ export const ExamPrintPanel = ({ client }: { client: ExamClient }) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const examId = Number(client.id);
|
const examId = Number(client.id);
|
||||||
if (!examId) return;
|
if (!examId) return;
|
||||||
|
|
||||||
const storedPdf = getDaojiandanPdf(examId);
|
|
||||||
if (storedPdf && storedPdf.pdf_url) {
|
|
||||||
setPdfUrl(storedPdf.pdf_url);
|
|
||||||
setShowPreview(true);
|
|
||||||
}
|
|
||||||
}, [client.id]);
|
}, [client.id]);
|
||||||
|
|
||||||
// 获取导检单PDF(优先使用localStorage,没有则调用接口)
|
// 获取导检单PDF(优先使用localStorage,没有则调用接口)
|
||||||
@@ -122,16 +105,6 @@ export const ExamPrintPanel = ({ client }: { client: ExamClient }) => {
|
|||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 先尝试从localStorage获取
|
|
||||||
const storedPdf = getDaojiandanPdf(examId);
|
|
||||||
if (storedPdf && storedPdf.pdf_url) {
|
|
||||||
setPdfUrl(storedPdf.pdf_url);
|
|
||||||
setShowPreview(true);
|
|
||||||
setFetchLoading(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果没有存储的,则调用接口获取
|
|
||||||
const res = await getDaojiandanPdfApi({ exam_id: examId });
|
const res = await getDaojiandanPdfApi({ exam_id: examId });
|
||||||
if (res.Status === 200 && res.Data?.pdf_url) {
|
if (res.Status === 200 && res.Data?.pdf_url) {
|
||||||
const pdfUrlValue = res.Data.pdf_url;
|
const pdfUrlValue = res.Data.pdf_url;
|
||||||
|
|||||||
@@ -2,19 +2,8 @@ import { useEffect, useRef, useState } from 'react';
|
|||||||
import * as pdfjsLib from 'pdfjs-dist';
|
import * as pdfjsLib from 'pdfjs-dist';
|
||||||
import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.min.mjs?url';
|
import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.min.mjs?url';
|
||||||
|
|
||||||
import type { OutputTongyishuFileInfo } from '../../api';
|
import type { OutputTongyishuFileInfo, OutputTijianPdfFileInfo } from '../../api';
|
||||||
import { getTongyishuPdf, signInMedicalExamCenter, submitTongyishuSign, submitDaojiandanSign, editDaojiandanPrintStatus, getDaojiandanPdf as getDaojiandanPdfApi, submitAddItemBillSign } from '../../api';
|
import { signInMedicalExamCenter, submitTongyishuSign, submitDaojiandanSign, editDaojiandanPrintStatus, submitAddItemBillSign, getTijianPdfFile } from '../../api';
|
||||||
import {
|
|
||||||
setExamActionRecord,
|
|
||||||
setTongyishuPdfList,
|
|
||||||
getTongyishuPdfList,
|
|
||||||
setDaojiandanPdf,
|
|
||||||
getDaojiandanPdf as getDaojiandanPdfFromStorage,
|
|
||||||
setAddItemBillPdf,
|
|
||||||
getAddItemBillPdf as getAddItemBillPdfFromStorage,
|
|
||||||
type TongyishuPdfInfo,
|
|
||||||
type AddItemBillPdfInfo,
|
|
||||||
} from '../../utils/examActions';
|
|
||||||
import type { SignaturePadHandle } from '../ui';
|
import type { SignaturePadHandle } from '../ui';
|
||||||
import { Button, SignaturePad } from '../ui';
|
import { Button, SignaturePad } from '../ui';
|
||||||
|
|
||||||
@@ -74,14 +63,22 @@ 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 [addItemBillList, setAddItemBillList] = useState<AddItemBillPdfInfo[]>([]);
|
|
||||||
const [currentAddItemBill, setCurrentAddItemBill] = useState<AddItemBillPdfInfo | null>(null);
|
|
||||||
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);
|
||||||
const [addItemBillSubmitLoading, setAddItemBillSubmitLoading] = useState(false);
|
const [addItemBillSubmitLoading, setAddItemBillSubmitLoading] = useState(false);
|
||||||
const [addItemBillSubmitMessage, setAddItemBillSubmitMessage] = useState<string | null>(null);
|
const [addItemBillSubmitMessage, setAddItemBillSubmitMessage] = useState<string | null>(null);
|
||||||
|
type AddItemBillItem = {
|
||||||
|
pdf_sort: number;
|
||||||
|
combinationCode: string;
|
||||||
|
payment_status?: string | null;
|
||||||
|
payment_status_name?: string | null;
|
||||||
|
pdf_name: string;
|
||||||
|
pdf_url: string;
|
||||||
|
is_signed: boolean;
|
||||||
|
};
|
||||||
|
const [addItemBillList, setAddItemBillList] = useState<AddItemBillItem[]>([]);
|
||||||
|
const [currentAddItemBill, setCurrentAddItemBill] = useState<AddItemBillItem | null>(null);
|
||||||
const [addItemBillPdfData, setAddItemBillPdfData] = useState<ArrayBuffer | null>(null);
|
const [addItemBillPdfData, setAddItemBillPdfData] = useState<ArrayBuffer | null>(null);
|
||||||
const [addItemBillPdfLoading, setAddItemBillPdfLoading] = useState(false);
|
const [addItemBillPdfLoading, setAddItemBillPdfLoading] = useState(false);
|
||||||
const [addItemBillPdfReady, setAddItemBillPdfReady] = useState(false);
|
const [addItemBillPdfReady, setAddItemBillPdfReady] = useState(false);
|
||||||
@@ -95,6 +92,86 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
|
|||||||
return () => onBusyChange?.(false);
|
return () => onBusyChange?.(false);
|
||||||
}, [busy, onBusyChange]);
|
}, [busy, onBusyChange]);
|
||||||
|
|
||||||
|
const refreshTijianPdfs = async (examIdValue: number) => {
|
||||||
|
setConsentLoading(true);
|
||||||
|
setConsentMessage(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await getTijianPdfFile({ exam_id: examIdValue });
|
||||||
|
const list: OutputTijianPdfFileInfo[] = res.Data || [];
|
||||||
|
|
||||||
|
// 知情同意书列表(pdf_type = 2)
|
||||||
|
const consentItems = list.filter((item) => item.pdf_type === 2);
|
||||||
|
const mappedConsent: OutputTongyishuFileInfo[] = consentItems.map((item) => ({
|
||||||
|
pdf_name: item.pdf_name || '',
|
||||||
|
pdf_url: item.sign_pdf_url || item.pdf_url || '',
|
||||||
|
combination_code: item.combination_code ?? null,
|
||||||
|
is_signed: item.is_sign === 1,
|
||||||
|
}));
|
||||||
|
setConsentList(mappedConsent);
|
||||||
|
|
||||||
|
// 已签名的组合码,用于一键打印等逻辑
|
||||||
|
const signedCodes =
|
||||||
|
consentItems
|
||||||
|
.filter((item) => item.is_sign === 1 && item.combination_code !== null && item.combination_code !== undefined)
|
||||||
|
.map((item) => Number(item.combination_code))
|
||||||
|
.filter((n) => Number.isFinite(n)) || [];
|
||||||
|
setSignedCombinationCodes(signedCodes);
|
||||||
|
|
||||||
|
if (!mappedConsent.length) {
|
||||||
|
setConsentMessage(res.Message || '暂无知情同意书');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导检单(pdf_type = 1,取第一条)
|
||||||
|
const daojiandan = list.find((item) => item.pdf_type === 1);
|
||||||
|
if (daojiandan) {
|
||||||
|
const url = daojiandan.sign_pdf_url || daojiandan.pdf_url || null;
|
||||||
|
setDaojiandanUrl(url);
|
||||||
|
setIsDaojiandanSigned(daojiandan.is_sign === 1);
|
||||||
|
} else {
|
||||||
|
setDaojiandanUrl(null);
|
||||||
|
setIsDaojiandanSigned(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加项单(pdf_type = 3):完全由新接口提供列表和签名状态
|
||||||
|
const addItemFromApi = list.filter((item) => item.pdf_type === 3);
|
||||||
|
if (addItemFromApi.length > 0) {
|
||||||
|
const addItemList: AddItemBillItem[] = addItemFromApi.map((item) => ({
|
||||||
|
pdf_sort: item.combination_code ?? 0,
|
||||||
|
combinationCode: String(item.combination_code ?? ''),
|
||||||
|
payment_status: item.is_pay != null ? String(item.is_pay) : null,
|
||||||
|
payment_status_name: item.is_pay_name ?? null,
|
||||||
|
pdf_name: item.pdf_name || '加项单',
|
||||||
|
pdf_url: item.sign_pdf_url || item.pdf_url || '',
|
||||||
|
is_signed: item.is_sign === 1,
|
||||||
|
}));
|
||||||
|
setAddItemBillList(addItemList);
|
||||||
|
const unsigned = addItemList.find((bill) => bill.is_signed !== true);
|
||||||
|
setCurrentAddItemBill(unsigned || addItemList[0] || null);
|
||||||
|
} else {
|
||||||
|
setAddItemBillList([]);
|
||||||
|
setCurrentAddItemBill(null);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('获取体检PDF列表失败', err);
|
||||||
|
setConsentMessage('知情同意书加载失败,请稍后重试');
|
||||||
|
} finally {
|
||||||
|
setConsentLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化加载体检 PDF 列表(导检单、知情同意书、加项单)
|
||||||
|
useEffect(() => {
|
||||||
|
if (!examId) {
|
||||||
|
setConsentList([]);
|
||||||
|
setConsentMessage('缺少体检ID,无法获取知情同意书');
|
||||||
|
setDaojiandanUrl(null);
|
||||||
|
setIsDaojiandanSigned(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
refreshTijianPdfs(examId);
|
||||||
|
}, [examId]);
|
||||||
|
|
||||||
const handlePickFile = () => {
|
const handlePickFile = () => {
|
||||||
fileInputRef.current?.click();
|
fileInputRef.current?.click();
|
||||||
};
|
};
|
||||||
@@ -170,10 +247,6 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
|
|||||||
const ok = res.Status === 200 && res.Data?.is_success === 0;
|
const ok = res.Status === 200 && res.Data?.is_success === 0;
|
||||||
if (ok) {
|
if (ok) {
|
||||||
setMessage('签到成功');
|
setMessage('签到成功');
|
||||||
// 记录身份证拍照与签到操作
|
|
||||||
if (examId) {
|
|
||||||
setExamActionRecord(examId, 'idCardSignIn', true);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
setMessage(res.Message || '签到失败');
|
setMessage(res.Message || '签到失败');
|
||||||
}
|
}
|
||||||
@@ -210,20 +283,7 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
|
|||||||
|
|
||||||
if (res.Status === 200) {
|
if (res.Status === 200) {
|
||||||
setSubmitMessage('签名提交成功');
|
setSubmitMessage('签名提交成功');
|
||||||
// 记录体检知情同意书的签字操作
|
// 更新本地已签名组合码(立刻刷新按钮状态)
|
||||||
if (examId) {
|
|
||||||
setExamActionRecord(examId, 'consentSign', true);
|
|
||||||
}
|
|
||||||
// 存储返回的PDF列表(标记为已签名)
|
|
||||||
if (res.Data?.list_pdf_url && Array.isArray(res.Data.list_pdf_url) && examId) {
|
|
||||||
const pdfList: TongyishuPdfInfo[] = res.Data.list_pdf_url.map((item) => ({
|
|
||||||
pdf_name: item.pdf_name || '',
|
|
||||||
pdf_url: item.pdf_url || '',
|
|
||||||
combination_code: item.combination_code ?? null,
|
|
||||||
is_signed: true,
|
|
||||||
}));
|
|
||||||
setTongyishuPdfList(examId, pdfList);
|
|
||||||
}
|
|
||||||
setSignedCombinationCodes((prev) => {
|
setSignedCombinationCodes((prev) => {
|
||||||
const code = Number(previewPdf.combination_code);
|
const code = Number(previewPdf.combination_code);
|
||||||
if (!Number.isFinite(code)) return prev || [];
|
if (!Number.isFinite(code)) return prev || [];
|
||||||
@@ -234,6 +294,11 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
|
|||||||
setPreviewPdf(null);
|
setPreviewPdf(null);
|
||||||
setSubmitMessage(null);
|
setSubmitMessage(null);
|
||||||
signaturePadRef.current?.clear();
|
signaturePadRef.current?.clear();
|
||||||
|
|
||||||
|
// 更新签名状态(刷新统一 PDF 列表)
|
||||||
|
if (examId) {
|
||||||
|
refreshTijianPdfs(examId);
|
||||||
|
}
|
||||||
}, 2000);
|
}, 2000);
|
||||||
} else {
|
} else {
|
||||||
setSubmitMessage(res.Message || '签名提交失败');
|
setSubmitMessage(res.Message || '签名提交失败');
|
||||||
@@ -246,121 +311,6 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!examId) {
|
|
||||||
setConsentList([]);
|
|
||||||
setConsentMessage('缺少体检ID,无法获取知情同意书');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setConsentLoading(true);
|
|
||||||
setConsentMessage(null);
|
|
||||||
|
|
||||||
// 先检查 localStorage 中是否有已签名的知情同意书
|
|
||||||
const storedList = getTongyishuPdfList(examId);
|
|
||||||
const signedFromStored =
|
|
||||||
storedList
|
|
||||||
?.filter((pdf) => pdf.is_signed === true && pdf.combination_code !== null && pdf.combination_code !== undefined)
|
|
||||||
.map((pdf) => Number(pdf.combination_code))
|
|
||||||
.filter((n) => Number.isFinite(n)) || [];
|
|
||||||
setSignedCombinationCodes(signedFromStored);
|
|
||||||
const allSigned = storedList && storedList.length > 0 && storedList.every((pdf) => pdf.is_signed === true);
|
|
||||||
|
|
||||||
// 如果所有知情同意书都已签名,直接使用 localStorage 中的数据,不请求接口
|
|
||||||
if (allSigned && storedList) {
|
|
||||||
const mergedList = storedList.map((pdf) => ({
|
|
||||||
pdf_name: pdf.pdf_name,
|
|
||||||
pdf_url: pdf.pdf_url,
|
|
||||||
combination_code: pdf.combination_code ?? null,
|
|
||||||
}));
|
|
||||||
setConsentList(mergedList);
|
|
||||||
setConsentLoading(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果有未签名的,请求接口获取最新列表
|
|
||||||
getTongyishuPdf({ exam_id: examId })
|
|
||||||
.then((res) => {
|
|
||||||
const list = res.Data?.list_pdf_url || [];
|
|
||||||
|
|
||||||
// 合并接口返回的数据和本地已保存的已签名 PDF
|
|
||||||
let mergedList = list;
|
|
||||||
if (storedList && storedList.length > 0) {
|
|
||||||
mergedList = list.map((item) => {
|
|
||||||
if (item.combination_code === undefined || item.combination_code === null) return item;
|
|
||||||
const code = Number(item.combination_code);
|
|
||||||
if (!Number.isFinite(code)) return item;
|
|
||||||
|
|
||||||
const matched = storedList.find(
|
|
||||||
(pdf) => pdf.combination_code !== null && Number(pdf.combination_code) === code && pdf.is_signed === true,
|
|
||||||
);
|
|
||||||
|
|
||||||
// 如果本地有已签名的版本,优先使用
|
|
||||||
if (matched && matched.pdf_url && matched.is_signed === true) {
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
pdf_url: matched.pdf_url,
|
|
||||||
pdf_name: matched.pdf_name || item.pdf_name,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setConsentList(mergedList);
|
|
||||||
if (!list.length) {
|
|
||||||
setConsentMessage(res.Data?.message || '暂无知情同意书');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error('获取知情同意书失败', err);
|
|
||||||
setConsentMessage('知情同意书加载失败,请稍后重试');
|
|
||||||
})
|
|
||||||
.finally(() => setConsentLoading(false));
|
|
||||||
}, [examId]);
|
|
||||||
|
|
||||||
// 组件加载时检查导检单localStorage,如果没有则调用接口获取未签名的导检单
|
|
||||||
useEffect(() => {
|
|
||||||
if (!examId) return;
|
|
||||||
|
|
||||||
// 先检查 localStorage 中的导检单
|
|
||||||
const storedPdf = getDaojiandanPdfFromStorage(examId);
|
|
||||||
if (storedPdf && storedPdf.pdf_url) {
|
|
||||||
setDaojiandanUrl(storedPdf.pdf_url);
|
|
||||||
// 使用 localStorage 中保存的 is_signed 字段判断是否已签名
|
|
||||||
setIsDaojiandanSigned(storedPdf.is_signed === true);
|
|
||||||
} else {
|
|
||||||
// 如果 localStorage 中没有导检单,调用接口获取未签名的导检单用于查看和签名
|
|
||||||
const fetchDaojiandan = async () => {
|
|
||||||
try {
|
|
||||||
const res = await getDaojiandanPdfApi({ exam_id: examId });
|
|
||||||
if (res.Status === 200 && res.Data?.pdf_url) {
|
|
||||||
const pdfUrlValue = res.Data.pdf_url;
|
|
||||||
// 不保存到 localStorage,只用于显示和签名
|
|
||||||
setDaojiandanUrl(pdfUrlValue);
|
|
||||||
setIsDaojiandanSigned(false); // 接口获取的是未签名的
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error('获取导检单失败', err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
fetchDaojiandan();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查加项单PDF(可能有多个)
|
|
||||||
const storedAddItemBillList = getAddItemBillPdfFromStorage(examId);
|
|
||||||
if (storedAddItemBillList && storedAddItemBillList.length > 0) {
|
|
||||||
setAddItemBillList(storedAddItemBillList);
|
|
||||||
// 默认选中第一个未签名的加项单,如果都已签名则选中第一个
|
|
||||||
const unsigned = storedAddItemBillList.find((item) => item.is_signed !== true);
|
|
||||||
setCurrentAddItemBill(unsigned || storedAddItemBillList[0]);
|
|
||||||
} else {
|
|
||||||
// 没有加项单:不再主动调用接口获取,因为新接口需要 CombinationCode(仅支付时知道)
|
|
||||||
setAddItemBillList([]);
|
|
||||||
setCurrentAddItemBill(null);
|
|
||||||
}
|
|
||||||
}, [examId]);
|
|
||||||
|
|
||||||
// 加载 PDF 数据
|
// 加载 PDF 数据
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!previewPdf?.pdf_url) {
|
if (!previewPdf?.pdf_url) {
|
||||||
@@ -1026,16 +976,6 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
|
|||||||
setDaojiandanUrl(pdfUrlValue);
|
setDaojiandanUrl(pdfUrlValue);
|
||||||
setIsDaojiandanSigned(true); // 签名成功后标记为已签名
|
setIsDaojiandanSigned(true); // 签名成功后标记为已签名
|
||||||
|
|
||||||
// 保存导检单PDF信息到localStorage(标记为已签名)
|
|
||||||
setDaojiandanPdf(examId, {
|
|
||||||
pdf_name: pdfNameValue,
|
|
||||||
pdf_url: pdfUrlValue,
|
|
||||||
is_signed: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
// 记录打印导检单是否签名操作
|
|
||||||
setExamActionRecord(examId, 'printSign', true);
|
|
||||||
|
|
||||||
// 更新导检单打印状态
|
// 更新导检单打印状态
|
||||||
try {
|
try {
|
||||||
await editDaojiandanPrintStatus({ exam_id: examId });
|
await editDaojiandanPrintStatus({ exam_id: examId });
|
||||||
@@ -1090,43 +1030,36 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
|
|||||||
sign_file: blob,
|
sign_file: blob,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.Status === 200 && res.Data?.pdf_url) {
|
if (res.Status === 200) {
|
||||||
setAddItemBillSubmitMessage('签名提交成功');
|
setAddItemBillSubmitMessage('签名提交成功');
|
||||||
const pdfUrlValue = res.Data.pdf_url;
|
|
||||||
const pdfNameValue = res.Data.pdf_name || '加项单';
|
|
||||||
// 更新当前加项单为已签名,并更新本地列表与 localStorage
|
|
||||||
setAddItemBillList((prev) => {
|
|
||||||
const next = prev.map((item) => {
|
|
||||||
if (item.pdf_sort === currentAddItemBill.pdf_sort) {
|
|
||||||
const updated: AddItemBillPdfInfo = {
|
|
||||||
...item,
|
|
||||||
pdf_name: pdfNameValue,
|
|
||||||
pdf_url: pdfUrlValue,
|
|
||||||
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) =>
|
// 签名成功后刷新统一 PDF 列表,获取最新的加项单签名状态和地址
|
||||||
prev && prev.pdf_sort === currentAddItemBill.pdf_sort
|
if (examId) {
|
||||||
? {
|
try {
|
||||||
...prev,
|
const refreshRes = await getTijianPdfFile({ exam_id: examId as number });
|
||||||
pdf_name: pdfNameValue,
|
const list: OutputTijianPdfFileInfo[] = refreshRes.Data || [];
|
||||||
pdf_url: pdfUrlValue,
|
const addItemFromApi = list.filter((item) => item.pdf_type === 3);
|
||||||
payment_status: res.Data?.payment_status ?? prev.payment_status ?? null,
|
if (addItemFromApi.length > 0) {
|
||||||
payment_status_name: res.Data?.payment_status_name ?? prev.payment_status_name ?? null,
|
const addItemList: AddItemBillItem[] = addItemFromApi.map((item) => ({
|
||||||
is_signed: true,
|
pdf_sort: item.combination_code ?? 0,
|
||||||
|
combinationCode: String(item.combination_code ?? ''),
|
||||||
|
payment_status: item.is_pay != null ? String(item.is_pay) : null,
|
||||||
|
payment_status_name: item.is_pay_name ?? null,
|
||||||
|
pdf_name: item.pdf_name || '加项单',
|
||||||
|
pdf_url: item.sign_pdf_url || item.pdf_url || '',
|
||||||
|
is_signed: item.is_sign === 1,
|
||||||
|
}));
|
||||||
|
setAddItemBillList(addItemList);
|
||||||
|
const unsigned = addItemList.find((bill) => bill.is_signed !== true);
|
||||||
|
setCurrentAddItemBill(unsigned || addItemList[0] || null);
|
||||||
|
} else {
|
||||||
|
setAddItemBillList([]);
|
||||||
|
setCurrentAddItemBill(null);
|
||||||
}
|
}
|
||||||
: prev
|
} catch (e) {
|
||||||
);
|
console.error('刷新加项单列表失败', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setShowAddItemBillSignature(false);
|
setShowAddItemBillSignature(false);
|
||||||
@@ -1146,7 +1079,7 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 加项单直接打印
|
// 加项单直接打印
|
||||||
const handleAddItemBillDirectPrint = async (target: AddItemBillPdfInfo | null) => {
|
const handleAddItemBillDirectPrint = async (target: AddItemBillItem | null) => {
|
||||||
if (!target?.pdf_url) return;
|
if (!target?.pdf_url) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -1443,23 +1376,7 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
|
|||||||
for (const item of consentList) {
|
for (const item of consentList) {
|
||||||
if (item.combination_code === undefined || item.combination_code === null) continue;
|
if (item.combination_code === undefined || item.combination_code === null) continue;
|
||||||
if (!signedCombinationCodes.includes(Number(item.combination_code))) continue;
|
if (!signedCombinationCodes.includes(Number(item.combination_code))) continue;
|
||||||
|
const targetUrl = item.pdf_url;
|
||||||
let targetUrl = item.pdf_url;
|
|
||||||
|
|
||||||
// 如果本地已保存签名后的PDF列表,则优先使用签名后的PDF地址
|
|
||||||
if (examId) {
|
|
||||||
const storedList = getTongyishuPdfList(examId);
|
|
||||||
if (storedList && item.combination_code !== undefined && item.combination_code !== null) {
|
|
||||||
const code = Number(item.combination_code);
|
|
||||||
const matched = storedList.find(
|
|
||||||
(pdf) => pdf.combination_code !== null && Number(pdf.combination_code) === code,
|
|
||||||
);
|
|
||||||
if (matched && matched.pdf_url) {
|
|
||||||
targetUrl = matched.pdf_url;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!targetUrl) continue;
|
if (!targetUrl) continue;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -1769,24 +1686,7 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
|
|||||||
className='py-1.5 px-3 bg-blue-600 hover:bg-blue-700 text-white'
|
className='py-1.5 px-3 bg-blue-600 hover:bg-blue-700 text-white'
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (busy) return;
|
if (busy) return;
|
||||||
let target = item;
|
handleDirectPrint(item);
|
||||||
// 如果本地已保存签名后的PDF列表,则优先使用签名后的PDF地址
|
|
||||||
if (examId) {
|
|
||||||
const storedList = getTongyishuPdfList(examId);
|
|
||||||
if (storedList && item.combination_code !== undefined && item.combination_code !== null) {
|
|
||||||
const code = Number(item.combination_code);
|
|
||||||
const matched = storedList.find(
|
|
||||||
(pdf) => pdf.combination_code !== null && Number(pdf.combination_code) === code,
|
|
||||||
);
|
|
||||||
if (matched && matched.pdf_url) {
|
|
||||||
target = {
|
|
||||||
...item,
|
|
||||||
pdf_url: matched.pdf_url,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
handleDirectPrint(target);
|
|
||||||
}}
|
}}
|
||||||
disabled={busy}
|
disabled={busy}
|
||||||
>
|
>
|
||||||
@@ -1798,26 +1698,7 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (busy) return;
|
if (busy) return;
|
||||||
|
|
||||||
let target = item;
|
setPreviewPdf(item);
|
||||||
|
|
||||||
// 如果本地已保存签名后的PDF列表,则优先使用签名后的PDF地址
|
|
||||||
if (examId) {
|
|
||||||
const storedList = getTongyishuPdfList(examId);
|
|
||||||
if (storedList && item.combination_code !== undefined && item.combination_code !== null) {
|
|
||||||
const code = Number(item.combination_code);
|
|
||||||
const matched = storedList.find(
|
|
||||||
(pdf) => pdf.combination_code !== null && Number(pdf.combination_code) === code,
|
|
||||||
);
|
|
||||||
if (matched && matched.pdf_url) {
|
|
||||||
target = {
|
|
||||||
...item,
|
|
||||||
pdf_url: matched.pdf_url,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setPreviewPdf(target);
|
|
||||||
}}
|
}}
|
||||||
disabled={busy}
|
disabled={busy}
|
||||||
>
|
>
|
||||||
@@ -2128,6 +2009,17 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
|
|||||||
>
|
>
|
||||||
打印
|
打印
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
className='py-1 px-3 hover:bg-blue-700 text-white'
|
||||||
|
onClick={() => {
|
||||||
|
if (busy) return;
|
||||||
|
setShowDaojiandanPreview(false);
|
||||||
|
setShowDaojiandanSignature(true);
|
||||||
|
}}
|
||||||
|
disabled={busy}
|
||||||
|
>
|
||||||
|
签名
|
||||||
|
</Button>
|
||||||
<Button className='py-1 px-3' onClick={() => !busy && setShowDaojiandanPreview(false)} disabled={busy}>
|
<Button className='py-1 px-3' onClick={() => !busy && setShowDaojiandanPreview(false)} disabled={busy}>
|
||||||
关闭
|
关闭
|
||||||
</Button>
|
</Button>
|
||||||
@@ -2217,19 +2109,17 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
|
|||||||
打印
|
打印
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{!currentAddItemBill.is_signed && (
|
<Button
|
||||||
<Button
|
className='py-1 px-3 hover:bg-blue-700 text-white'
|
||||||
className='py-1 px-3 bg-blue-600 hover:bg-blue-700 text-white'
|
onClick={() => {
|
||||||
onClick={() => {
|
if (busy) return;
|
||||||
if (busy) return;
|
setShowAddItemBillPreview(false);
|
||||||
setShowAddItemBillPreview(false);
|
setShowAddItemBillSignature(true);
|
||||||
setShowAddItemBillSignature(true);
|
}}
|
||||||
}}
|
disabled={busy}
|
||||||
disabled={busy}
|
>
|
||||||
>
|
签名
|
||||||
签名
|
</Button>
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
<Button className='py-1 px-3' onClick={() => !busy && setShowAddItemBillPreview(false)} disabled={busy}>
|
<Button className='py-1 px-3' onClick={() => !busy && setShowAddItemBillPreview(false)} disabled={busy}>
|
||||||
关闭
|
关闭
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -40,20 +40,9 @@ export interface ExamActionRecord {
|
|||||||
/**
|
/**
|
||||||
* 获取操作记录
|
* 获取操作记录
|
||||||
*/
|
*/
|
||||||
export const getExamActionRecord = (examId: string | number): ExamActionRecord | null => {
|
export const getExamActionRecord = (_examId: string | number): ExamActionRecord | null => {
|
||||||
if (typeof window === 'undefined') return null;
|
// 已废弃,不再从本地存储读取
|
||||||
|
return null;
|
||||||
const key = getExamActionKey(examId);
|
|
||||||
const raw = localStorage.getItem(key);
|
|
||||||
if (!raw) return null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const parsed = JSON.parse(raw);
|
|
||||||
return parsed as ExamActionRecord;
|
|
||||||
} catch (err) {
|
|
||||||
console.warn('操作记录解析失败', err);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,223 +53,13 @@ export const setExamActionRecord = (
|
|||||||
action: keyof ExamActionRecord,
|
action: keyof ExamActionRecord,
|
||||||
value: boolean = true
|
value: boolean = true
|
||||||
): void => {
|
): void => {
|
||||||
if (typeof window === 'undefined') return;
|
// 已废弃,不再写入本地存储
|
||||||
|
|
||||||
const key = getExamActionKey(examId);
|
|
||||||
const existing = getExamActionRecord(examId) || {};
|
|
||||||
|
|
||||||
const updated: ExamActionRecord = {
|
|
||||||
...existing,
|
|
||||||
[action]: value,
|
|
||||||
timestamp: new Date().toISOString(),
|
|
||||||
};
|
|
||||||
|
|
||||||
localStorage.setItem(key, JSON.stringify(updated));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查操作是否已完成
|
* 检查操作是否已完成
|
||||||
*/
|
*/
|
||||||
export const isExamActionDone = (examId: string | number, action: keyof ExamActionRecord): boolean => {
|
export const isExamActionDone = (examId: string | number, action: keyof ExamActionRecord): boolean => {
|
||||||
const record = getExamActionRecord(examId);
|
// 已废弃,统一由实时数据决定
|
||||||
return record?.[action] === true;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* 知情同意书PDF信息
|
|
||||||
*/
|
|
||||||
export interface TongyishuPdfInfo {
|
|
||||||
/** PDF文件名称 */
|
|
||||||
pdf_name: string;
|
|
||||||
/** PDF文件地址 */
|
|
||||||
pdf_url: string;
|
|
||||||
/** 组合代码 */
|
|
||||||
combination_code?: number | null;
|
|
||||||
/** 是否已签名 */
|
|
||||||
is_signed?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取知情同意书PDF列表的存储 key
|
|
||||||
*/
|
|
||||||
export const getTongyishuPdfListKey = (examId: string | number): string => {
|
|
||||||
const today = getTodayString();
|
|
||||||
return `yh_tongyishu_pdf_list_${today}_${examId}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 存储知情同意书PDF列表
|
|
||||||
*/
|
|
||||||
export const setTongyishuPdfList = (
|
|
||||||
examId: string | number,
|
|
||||||
pdfList: TongyishuPdfInfo[]
|
|
||||||
): void => {
|
|
||||||
if (typeof window === 'undefined') return;
|
|
||||||
|
|
||||||
const key = getTongyishuPdfListKey(examId);
|
|
||||||
localStorage.setItem(key, JSON.stringify(pdfList));
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取知情同意书PDF列表
|
|
||||||
*/
|
|
||||||
export const getTongyishuPdfList = (examId: string | number): TongyishuPdfInfo[] | null => {
|
|
||||||
if (typeof window === 'undefined') return null;
|
|
||||||
|
|
||||||
const key = getTongyishuPdfListKey(examId);
|
|
||||||
const raw = localStorage.getItem(key);
|
|
||||||
if (!raw) return null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const parsed = JSON.parse(raw);
|
|
||||||
return Array.isArray(parsed) ? parsed as TongyishuPdfInfo[] : null;
|
|
||||||
} catch (err) {
|
|
||||||
console.warn('知情同意书PDF列表解析失败', err);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 导检单PDF信息
|
|
||||||
*/
|
|
||||||
export interface DaojiandanPdfInfo {
|
|
||||||
/** PDF文件名称 */
|
|
||||||
pdf_name: string;
|
|
||||||
/** PDF文件地址 */
|
|
||||||
pdf_url: string;
|
|
||||||
/** 是否已签名 */
|
|
||||||
is_signed?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取导检单PDF的存储 key
|
|
||||||
*/
|
|
||||||
export const getDaojiandanPdfKey = (examId: string | number): string => {
|
|
||||||
const today = getTodayString();
|
|
||||||
return `yh_daojiandan_pdf_${today}_${examId}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 存储导检单PDF信息
|
|
||||||
*/
|
|
||||||
export const setDaojiandanPdf = (
|
|
||||||
examId: string | number,
|
|
||||||
pdfInfo: DaojiandanPdfInfo
|
|
||||||
): void => {
|
|
||||||
if (typeof window === 'undefined') return;
|
|
||||||
|
|
||||||
const key = getDaojiandanPdfKey(examId);
|
|
||||||
localStorage.setItem(key, JSON.stringify(pdfInfo));
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取导检单PDF信息
|
|
||||||
*/
|
|
||||||
export const getDaojiandanPdf = (examId: string | number): DaojiandanPdfInfo | null => {
|
|
||||||
if (typeof window === 'undefined') return null;
|
|
||||||
|
|
||||||
const key = getDaojiandanPdfKey(examId);
|
|
||||||
const raw = localStorage.getItem(key);
|
|
||||||
if (!raw) return null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const parsed = JSON.parse(raw);
|
|
||||||
return parsed as DaojiandanPdfInfo;
|
|
||||||
} catch (err) {
|
|
||||||
console.warn('导检单PDF信息解析失败', err);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 加项单PDF信息
|
|
||||||
*/
|
|
||||||
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_name: string;
|
|
||||||
/** PDF文件地址 */
|
|
||||||
pdf_url: string;
|
|
||||||
/** 是否已签名 */
|
|
||||||
is_signed?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取加项单PDF的存储 key
|
|
||||||
*/
|
|
||||||
export const getAddItemBillPdfKey = (examId: string | number): string => {
|
|
||||||
const today = getTodayString();
|
|
||||||
return `yh_add_item_bill_pdf_${today}_${examId}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 存储加项单PDF信息
|
|
||||||
*/
|
|
||||||
export const setAddItemBillPdf = (
|
|
||||||
examId: string | number,
|
|
||||||
pdfInfo: AddItemBillPdfInfo
|
|
||||||
): void => {
|
|
||||||
if (typeof window === 'undefined') return;
|
|
||||||
|
|
||||||
const key = getAddItemBillPdfKey(examId);
|
|
||||||
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信息
|
|
||||||
*/
|
|
||||||
export const getAddItemBillPdf = (examId: string | number): AddItemBillPdfInfo[] | null => {
|
|
||||||
if (typeof window === 'undefined') return null;
|
|
||||||
|
|
||||||
const key = getAddItemBillPdfKey(examId);
|
|
||||||
const raw = localStorage.getItem(key);
|
|
||||||
if (!raw) return null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const parsed = JSON.parse(raw);
|
|
||||||
if (Array.isArray(parsed)) {
|
|
||||||
return parsed as AddItemBillPdfInfo[];
|
|
||||||
}
|
|
||||||
if (parsed && typeof parsed === 'object') {
|
|
||||||
// 兼容旧版本(单个对象)
|
|
||||||
return [parsed as AddItemBillPdfInfo];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
} catch (err) {
|
|
||||||
console.warn('加项单PDF信息解析失败', err);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user