Files
yuanhe-checkin-electron/src/pages/UI7/UI7.tsx
2025-12-31 09:23:25 +08:00

878 lines
28 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState, useEffect, useRef, useCallback } from "react";
import { Document, Page } from "react-pdf";
import "./UI7.css";
import "../../assets/css/basic.css";
import { useNavigate } from "react-router-dom";
import BackButton from "../../components/BackButton";
import ConfirmButton from "../../components/ConfirmButton";
import DecorLine from "../../components/DecorLine";
import WaitButton from "../../components/WaitButton";
import {
getTongyishuPdf,
signIn,
submitDaojiandanSign,
submitTongyishuSign,
} from "../../api/hisApi";
// 独立的 PDF 渲染组件,使用 React.memo 避免不必要的重新渲染
const PdfRenderer = React.memo<{
pdfFile: Uint8Array | null;
loading: boolean;
error: string;
pageCount: number;
onPageCountUpdate: (numPages: number) => void;
loadMessage: string;
documentKey: string;
}>(
({
pdfFile,
loading,
error,
pageCount,
onPageCountUpdate,
loadMessage,
documentKey,
}) => {
const pagesToRender = Math.max(pageCount, 1);
return (
<>
{loading && (
<div style={{ padding: "20px", textAlign: "center", fontSize: "32px" }}>
<span style={{ fontSize: "32px" }}>{loadMessage}</span>
</div>
)}
{error && (
<div style={{ padding: "20px", color: "red", textAlign: "center" }}>
{error}
</div>
)}
{!loading && !error && !pdfFile && (
<div style={{ padding: "20px", textAlign: "center", fontSize: "28px" }}>
PDF
</div>
)}
{!loading && !error && pdfFile && (
<div style={{ marginBottom: "20px" }}>
<Document
key={documentKey}
file={{ data: pdfFile }}
loading=""
onLoadSuccess={({ numPages }) => onPageCountUpdate(numPages)}
>
{Array.from({ length: pagesToRender }, (_, pageIndex) => (
<Page
key={`pdf-page-${pageIndex + 1}`}
pageNumber={pageIndex + 1}
renderTextLayer={false}
renderAnnotationLayer={false}
width={900}
/>
))}
</Document>
</div>
)}
</>
);
}
);
PdfRenderer.displayName = "PdfRenderer";
type PdfMeta = {
pdf_url: string;
pdf_name: string;
combination_code: number | null;
};
const UI7: React.FC = () => {
const navigate = useNavigate();
const hasFetchedRef = useRef(false);
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const dprRef = useRef<number>(1);
const lastPointRef = useRef<{ x: number; y: number } | null>(null);
const [countdown, setCountdown] = useState(5);
const [showWaitButton, setShowWaitButton] = useState(true);
const [pdfFiles, setPdfFiles] = useState<Uint8Array[]>([]);
const [pdfInfoList, setPdfInfoList] = useState<PdfMeta[]>([]);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string>("");
const [loadMessage, setLoadMessage] = useState("PDF加载中...");
const [isDrawing, setIsDrawing] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const [currentStep, setCurrentStep] = useState(0);
const [hasSignature, setHasSignature] = useState(false);
const [currentPdfPageCount, setCurrentPdfPageCount] = useState(0);
const titleRef = useRef<HTMLSpanElement | null>(null);
const [isTitleCompact, setIsTitleCompact] = useState(false);
const totalRequiredSignatures = pdfInfoList.length + 1;
const isInstructionStep = currentStep === 0;
const currentPdfIndex = isInstructionStep ? -1 : currentStep - 1;
const currentPdfFile =
currentPdfIndex >= 0 && currentPdfIndex < pdfFiles.length
? pdfFiles[currentPdfIndex]
: null;
const currentPdfMeta =
currentPdfIndex >= 0 && currentPdfIndex < pdfInfoList.length
? pdfInfoList[currentPdfIndex]
: null;
const currentPdfName = currentPdfMeta?.pdf_name || "";
// 辅助函数:从 URL 获取 PDF 的 Uint8Array
const fetchPdfBytes = async (url: string) => {
try {
window.electronAPI?.log("info", `[UI7] 通过 fetch 获取 PDF: ${url}`);
const response = await fetch(url);
if (!response.ok) {
throw new Error(`网络请求失败: ${response.status}`);
}
const arrayBuffer = await response.arrayBuffer();
return new Uint8Array(arrayBuffer);
} catch (error) {
throw new Error(`获取 PDF 失败: ${(error as Error).message}`);
}
};
// 从多份 PDF 中获取数据
useEffect(() => {
if (hasFetchedRef.current) {
window.electronAPI?.log("info", "[UI7] 已获取过 PDF跳过重复请求");
return;
}
hasFetchedRef.current = true;
const fetchPdfs = async () => {
const examId = Number(localStorage.getItem("selectedExamId"));
window.electronAPI?.log("info", `[UI7] 开始获取知情同意书PDF列表exam_id=${examId}`);
try {
const res = await getTongyishuPdf(examId);
window.electronAPI?.log("info", `[UI7] 获取PDF列表响应: Status=${res.Status}, Message=${res.Message || "null"}`);
if (res.Status !== 200) {
window.electronAPI?.log("error", `[UI7] 获取PDF数据失败: ${res.Message}`);
setLoadMessage(res.Message || "获取PDF数据失败");
return;
}
const rawList = res.Data?.list_pdf_url || [];
window.electronAPI?.log("info", `[UI7] 原始PDF列表数量: ${rawList.length}`);
const normalizedList = rawList
.map((item, idx): PdfMeta => {
if (typeof item === "string") {
return {
pdf_url: item,
pdf_name: `知情同意书${idx + 1}`,
combination_code: null,
};
}
if (item && typeof item === "object") {
const codeValue =
item.combination_code != null
? Number(item.combination_code)
: null;
return {
pdf_url: item.pdf_url || "",
pdf_name: item.pdf_name || `知情同意书${idx + 1}`,
combination_code:
codeValue != null && !Number.isNaN(codeValue)
? codeValue
: null,
};
}
return {
pdf_url: "",
pdf_name: `知情同意书${idx + 1}`,
combination_code: null,
};
})
.filter(
(item): item is PdfMeta =>
typeof item.pdf_url === "string" && item.pdf_url.length > 0
);
window.electronAPI?.log("info", `[UI7] 规范化后PDF列表数量: ${normalizedList.length}`);
if (normalizedList.length === 0) {
window.electronAPI?.log(
"info",
`[UI7] 没有获取到知情同意书,将只进行导检单签名`
);
setPdfFiles([]);
setPdfInfoList([]);
setLoading(false);
setError("");
window.electronAPI?.log(
"info",
`[UI7] PDF 获取完成,共 0 份知情同意书,总签名步骤: 1 (仅导检单)`
);
return;
}
normalizedList.forEach((item, idx) => {
window.electronAPI?.log(
"info",
`[UI7] PDF[${idx}]: name="${item.pdf_name}", combination_code=${item.combination_code || "null"}, url=${item.pdf_url.substring(0, 80)}...`
);
});
setLoading(true);
setError("");
setPdfFiles([]);
setPdfInfoList([]);
window.electronAPI?.log("info", `[UI7] 开始下载 ${normalizedList.length} 份 PDF 文件`);
const files: Uint8Array[] = [];
for (let idx = 0; idx < normalizedList.length; idx++) {
const { pdf_url: url, pdf_name: name } = normalizedList[idx];
window.electronAPI?.log(
"info",
`[UI7] 下载第 ${idx + 1}/${normalizedList.length} 份 PDF: ${name}`
);
const startTime = Date.now();
const pdfBytes = await fetchPdfBytes(url);
const duration = Date.now() - startTime;
files.push(pdfBytes);
window.electronAPI?.log(
"info",
`[UI7] 第 ${idx + 1} 份 PDF 下载完成,大小: ${pdfBytes.length} bytes耗时: ${duration}ms`
);
}
setPdfFiles(files);
setPdfInfoList(normalizedList);
setLoading(false);
setError("");
window.electronAPI?.log(
"info",
`[UI7] PDF 获取完成,共 ${normalizedList.length} 份,总签名步骤: ${normalizedList.length + 1} (导检单 + ${normalizedList.length} 份知情同意书)`
);
} catch (err) {
const errorMsg = `PDF获取失败: ${err}`;
console.error(errorMsg);
window.electronAPI?.log("error", `[UI7] ${errorMsg}`);
setError("PDF 获取失败,请检查文件");
setLoading(false);
}
};
fetchPdfs();
}, []);
useEffect(() => {
window.electronAPI?.log("info", "[UI7] 初始化:清理旧的签名缓存数据");
localStorage.removeItem("consentSignatureList");
localStorage.removeItem("tongyishuSignedPdfUrls");
window.electronAPI?.log("info", "[UI7] 初始化完成:签名缓存已清理");
}, []);
useEffect(() => {
if (countdown > 0) {
const timer = setTimeout(() => {
setCountdown(countdown - 1);
}, 1000);
return () => clearTimeout(timer);
} else {
setShowWaitButton(false);
}
}, [countdown]);
useEffect(() => {
setCurrentPdfPageCount(0);
if (currentStep > 0) {
const meta = pdfInfoList[currentStep - 1];
window.electronAPI?.log(
"info",
`[UI7] 步骤切换: currentStep=${currentStep}, 当前PDF: ${meta?.pdf_name || "未知"}`
);
} else {
window.electronAPI?.log(
"info",
`[UI7] 步骤切换: currentStep=${currentStep} (导检单说明页)`
);
}
}, [currentStep, currentPdfFile, pdfInfoList]);
useEffect(() => {
setHasSignature(false);
window.electronAPI?.log(
"info",
`[UI7] 步骤 ${currentStep} 签名状态已重置`
);
}, [currentStep]);
// 检测标题文本是否超过一行,如果超过则应用缩小样式
useEffect(() => {
const checkTitleOverflow = () => {
if (!titleRef.current) return;
const element = titleRef.current;
// 先移除 compact 类,以正常大小检测
element.classList.remove('compact');
// 使用 requestAnimationFrame 确保 DOM 已更新
requestAnimationFrame(() => {
if (!element) return;
// 获取实际内容高度
const scrollHeight = element.scrollHeight;
// 单行高度为 91pxline-height
const singleLineHeight = 91;
// 如果内容高度超过单行高度,说明换行了
const needsCompact = scrollHeight > singleLineHeight;
setIsTitleCompact(needsCompact);
// 如果需要 compact添加类名
if (needsCompact) {
element.classList.add('compact');
}
});
};
// 延迟执行,确保 DOM 已渲染
const timer = setTimeout(() => {
checkTitleOverflow();
}, 100);
// 监听窗口大小变化
window.addEventListener('resize', checkTitleOverflow);
return () => {
clearTimeout(timer);
window.removeEventListener('resize', checkTitleOverflow);
};
}, [currentStep, currentPdfName, isInstructionStep]);
const resetCountdown = useCallback(() => {
setCountdown(5);
setShowWaitButton(true);
}, []);
useEffect(() => {
const initCanvas = () => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d", {
willReadFrequently: true,
alpha: true,
});
if (!ctx) return;
const rect = canvas.getBoundingClientRect();
const scale = 3;
dprRef.current = scale;
canvas.width = rect.width * scale;
canvas.height = rect.height * scale;
ctx.setTransform(scale, 0, 0, scale, 0, 0);
canvas.style.width = `${rect.width}px`;
canvas.style.height = `${rect.height}px`;
ctx.strokeStyle = "#000000";
ctx.lineWidth = 10;
ctx.lineCap = "round";
ctx.lineJoin = "round";
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = "high";
};
const timer = setTimeout(initCanvas, 150);
window.addEventListener("resize", initCanvas);
return () => {
clearTimeout(timer);
window.removeEventListener("resize", initCanvas);
};
}, []);
const getCoordinates = useCallback(
(event: React.PointerEvent<HTMLCanvasElement>) => {
const canvas = canvasRef.current;
if (!canvas) return { x: 0, y: 0 };
const rect = canvas.getBoundingClientRect();
return {
x: event.clientX - rect.left,
y: event.clientY - rect.top,
};
},
[]
);
const startDrawing = useCallback(
(event: React.PointerEvent<HTMLCanvasElement>) => {
if (event.cancelable) {
event.preventDefault();
}
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
const { x, y } = getCoordinates(event);
lastPointRef.current = { x, y };
ctx.beginPath();
ctx.moveTo(x, y);
setIsDrawing(true);
canvas.setPointerCapture(event.pointerId);
},
[getCoordinates]
);
const draw = useCallback(
(event: React.PointerEvent<HTMLCanvasElement>) => {
if (event.cancelable) {
event.preventDefault();
}
if (!isDrawing || !lastPointRef.current) return;
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
const { x, y } = getCoordinates(event);
const lastPoint = lastPointRef.current;
const midX = (lastPoint.x + x) / 2;
const midY = (lastPoint.y + y) / 2;
ctx.quadraticCurveTo(lastPoint.x, lastPoint.y, midX, midY);
ctx.stroke();
lastPointRef.current = { x, y };
setHasSignature(true);
},
[getCoordinates, isDrawing]
);
const stopDrawing = useCallback(
(event?: React.PointerEvent<HTMLCanvasElement>) => {
setIsDrawing(false);
lastPointRef.current = null;
if (event && canvasRef.current?.hasPointerCapture(event.pointerId)) {
canvasRef.current.releasePointerCapture(event.pointerId);
}
},
[]
);
const clearCanvas = useCallback(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.restore();
ctx.strokeStyle = "#000000";
ctx.lineWidth = 10;
ctx.lineCap = "round";
ctx.lineJoin = "round";
setHasSignature(false);
}, []);
const isCanvasBlank = useCallback(() => {
const canvas = canvasRef.current;
if (!canvas) return true;
const ctx = canvas.getContext("2d");
if (!ctx) return true;
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const pixelBuffer = new Uint32Array(imageData.data.buffer);
return !pixelBuffer.some((color) => color !== 0);
}, []);
const canvasToBlob = useCallback((canvas: HTMLCanvasElement) => {
return new Promise<Blob>((resolve, reject) => {
canvas.toBlob(
(blob) => {
if (blob) {
resolve(blob);
} else {
reject(new Error("签名生成失败,请重试"));
}
},
"image/png",
1
);
});
}, []);
const handleBack = useCallback(() => {
navigate(-1);
}, [navigate]);
const handleConfirm = useCallback(() => {
const execute = async () => {
if (isSubmitting) {
window.electronAPI?.log("warn", "[UI7] 检测到重复提交,已忽略");
return;
}
const canvas = canvasRef.current;
if (!canvas) {
window.electronAPI?.log("error", "[UI7] 画布初始化失败");
alert("画布初始化失败,请刷新页面");
return;
}
if (isCanvasBlank()) {
window.electronAPI?.log("warn", "[UI7] 画布为空,无法提交");
alert("请先完成签名后再继续");
return;
}
const examIdStr = localStorage.getItem("selectedExamId");
const examId = Number(examIdStr);
if (!examIdStr || Number.isNaN(examId)) {
window.electronAPI?.log("error", `[UI7] 体检ID无效: ${examIdStr}`);
alert("未找到体检ID请返回重试");
return;
}
const totalSteps = pdfInfoList.length + 1;
const currentStepNum = currentStep + 1;
window.electronAPI?.log(
"info",
`[UI7] ========== 开始提交签名 ==========`
);
window.electronAPI?.log(
"info",
`[UI7] 当前步骤: ${currentStepNum}/${totalSteps}, currentStep=${currentStep}, pdfInfoList.length=${pdfInfoList.length}`
);
setIsSubmitting(true);
try {
const blob = await canvasToBlob(canvas);
window.electronAPI?.log(
"info",
`[UI7] 签名图片生成成功,大小: ${blob.size} bytes`
);
// 本地缓存成列表,方便 UI8 或调试使用
try {
const dataUrl = canvas.toDataURL("image/png");
const storedRaw = localStorage.getItem("consentSignatureList");
const storedList: string[] = storedRaw ? JSON.parse(storedRaw) : [];
storedList[currentStep] = dataUrl;
localStorage.setItem("consentSignatureList", JSON.stringify(storedList));
window.electronAPI?.log(
"info",
`[UI7] 签名已缓存到 localStorage[${currentStep}]`
);
} catch (cacheErr) {
window.electronAPI?.log(
"warn",
`[UI7] 签名缓存失败: ${(cacheErr as Error).message}`
);
}
if (currentStep === 0) {
window.electronAPI?.log(
"info",
`[UI7] >>> 提交导检单签名(步骤 ${currentStepNum}/${totalSteps}exam_id=${examId}`
);
const startTime = Date.now();
const daojiandanRes = await submitDaojiandanSign(examId, blob);
const duration = Date.now() - startTime;
window.electronAPI?.log(
"info",
`[UI7] 导检单签名响应: Status=${daojiandanRes.Status}, Message=${daojiandanRes.Message || "null"}, 耗时: ${duration}ms`
);
if (daojiandanRes.Status !== 200) {
throw new Error(daojiandanRes.Message || "提交导检单签名失败");
}
const pdfUrl = daojiandanRes.Data.pdf_url || "";
localStorage.setItem("consentSignature", pdfUrl);
window.electronAPI?.log(
"info",
`[UI7] 导检单签名成功PDF URL已保存: ${pdfUrl.substring(0, 80)}...`
);
// 如果没有知情同意书,导检单签名完成后直接跳转
if (pdfInfoList.length === 0) {
window.electronAPI?.log(
"info",
`[UI7] 没有知情同意书需要签名,导检单签名完成后直接跳转`
);
window.electronAPI?.log(
"info",
`[UI7] ✓ 所有签名已完成准备跳转到UI8仅导检单`
);
sign();
navigate("/UI8");
return;
}
} else {
const meta = pdfInfoList[currentStep - 1];
if (!meta) {
window.electronAPI?.log(
"error",
`[UI7] 无法找到当前PDF元信息currentStep=${currentStep}, pdfInfoList.length=${pdfInfoList.length}`
);
throw new Error("当前PDF信息缺失无法提交签名");
}
if (meta.combination_code == null) {
window.electronAPI?.log(
"error",
`[UI7] 当前知情同意书缺少组合代码: pdf_name="${meta.pdf_name}", pdf_url=${meta.pdf_url.substring(0, 80)}...`
);
throw new Error("当前知情同意书缺少组合代码,无法提交签名");
}
window.electronAPI?.log(
"info",
`[UI7] >>> 提交知情同意书签名(步骤 ${currentStepNum}/${totalSteps}`
);
window.electronAPI?.log(
"info",
`[UI7] PDF信息: name="${meta.pdf_name}", combination_code=${meta.combination_code}, exam_id=${examId}`
);
const startTime = Date.now();
const tongyishuRes = await submitTongyishuSign(
examId,
meta.combination_code,
blob
);
const duration = Date.now() - startTime;
window.electronAPI?.log(
"info",
`[UI7] 知情同意书签名响应: Status=${tongyishuRes.Status}, Message=${tongyishuRes.Message || "null"}, 耗时: ${duration}ms`
);
if (tongyishuRes.Status !== 200) {
throw new Error(tongyishuRes.Message || "提交知情同意书签名失败");
}
// 保存签名后的知情同意书 PDF URL可能返回多个
try {
const returnedList = Array.isArray(
tongyishuRes.Data?.list_pdf_url
)
? tongyishuRes.Data.list_pdf_url
: [];
window.electronAPI?.log(
"info",
`[UI7] 返回的PDF URL列表数量: ${returnedList.length}`
);
const urls = returnedList
.map((item) =>
typeof item === "string" ? item : item?.pdf_url || ""
)
.filter((url): url is string => Boolean(url));
urls.forEach((url, idx) => {
window.electronAPI?.log(
"info",
`[UI7] 返回的PDF URL[${idx}]: ${url.substring(0, 80)}...`
);
});
const storedRaw = localStorage.getItem("tongyishuSignedPdfUrls");
const storedList: string[] = storedRaw ? JSON.parse(storedRaw) : [];
window.electronAPI?.log(
"info",
`[UI7] 原有缓存的PDF URL数量: ${storedList.length}`
);
const merged = [...storedList];
let addedCount = 0;
urls.forEach((url) => {
if (!merged.includes(url)) {
merged.push(url);
addedCount++;
}
});
localStorage.setItem(
"tongyishuSignedPdfUrls",
JSON.stringify(merged)
);
window.electronAPI?.log(
"info",
`[UI7] 知情同意书PDF URL已保存新增: ${addedCount}, 总计: ${merged.length}`
);
} catch (cacheErr) {
window.electronAPI?.log(
"warn",
`[UI7] 知情同意书 PDF URL 缓存失败: ${(cacheErr as Error).message}`
);
}
}
window.electronAPI?.log(
"info",
`[UI7] 检查是否完成所有签名: currentStep=${currentStep}, pdfInfoList.length=${pdfInfoList.length}, 条件: currentStep > 0 && currentStep >= pdfInfoList.length`
);
if (currentStep > 0 && currentStep >= pdfInfoList.length) {
window.electronAPI?.log(
"info",
`[UI7] ✓ 所有签名已完成准备跳转到UI8`
);
window.electronAPI?.log(
"info",
`[UI7] 已签名: 导检单(1份) + 知情同意书(${pdfInfoList.length}份) = 共${totalSteps}`
);
sign();
navigate("/UI8");
return;
}
const nextStep = currentStep + 1;
window.electronAPI?.log(
"info",
`[UI7] 继续下一步: 从步骤 ${currentStepNum} 进入步骤 ${nextStep + 1}/${totalSteps}`
);
clearCanvas();
setCurrentStep((prev) => prev + 1);
resetCountdown();
window.electronAPI?.log(
"info",
`[UI7] ========== 步骤 ${currentStepNum} 完成,进入步骤 ${nextStep + 1} ==========`
);
} catch (err) {
const msg = (err as Error).message || "签名提交失败,请稍后重试";
window.electronAPI?.log("error", `[UI7] 签名提交失败: ${msg}`);
window.electronAPI?.log("error", `[UI7] 错误堆栈: ${(err as Error).stack || "无"}`);
alert(msg);
} finally {
setIsSubmitting(false);
window.electronAPI?.log("info", `[UI7] 提交流程结束isSubmitting=false`);
}
};
execute();
}, [
canvasToBlob,
clearCanvas,
currentStep,
isCanvasBlank,
isSubmitting,
navigate,
pdfInfoList,
resetCountdown,
totalRequiredSignatures,
]);
const handlePageCountUpdate = useCallback((numPages: number) => {
setCurrentPdfPageCount(numPages);
}, []);
const sign = async () => {
const physical_exam_id = localStorage.getItem("selectedExamId");
if (!physical_exam_id) {
window.electronAPI?.log("error", "[UI7] 签到失败体检ID不存在");
alert("体检ID不存在");
return;
}
window.electronAPI?.log(
"info",
`[UI7] 开始执行签到exam_id=${physical_exam_id}`
);
const startTime = Date.now();
const res = await signIn(Number(physical_exam_id));
const duration = Date.now() - startTime;
window.electronAPI?.log(
"info",
`[UI7] 签到响应: Status=${res.Status}, Message=${res.Message || "null"}, is_success=${res.Data?.is_success}, 耗时: ${duration}ms`
);
if (res.Status === 200) {
if (res.Data.is_success === 0) {
window.electronAPI?.log("info", "[UI7] 签到成功");
return;
} else {
window.electronAPI?.log("warn", `[UI7] 签到返回异常: ${res.Message}`);
console.log(res.Data);
alert(res.Message);
}
} else {
window.electronAPI?.log("error", `[UI7] 签到失败: ${res.Message}`);
alert(res.Message);
}
};
return (
<div className="basic-root">
<div className="basic-white-block">
<div className="basic-content">
<span
ref={titleRef}
className={`ui7-basic-title ${isTitleCompact ? 'compact' : ''}`}
>
{!isInstructionStep && currentPdfName ? `${currentPdfName}` : "检测项目知情同意书确认"}
</span>
<DecorLine />
{isInstructionStep && (
<div
style={{
padding: "40px",
fontSize: "28px",
lineHeight: "48px",
color: "#333",
}}
>
<br />
<br />
<br />
{/* <p style={{ textAlign: "center", fontSize: "36px" }}>
请仔细阅读后签字确认
</p> */}
</div>
)}
{!isInstructionStep && (
<div className="ui7-text-wrapper">
<div className="ui7-text-content">
<PdfRenderer
pdfFile={currentPdfFile}
loading={loading}
error={error}
pageCount={currentPdfPageCount}
onPageCountUpdate={handlePageCountUpdate}
loadMessage={loadMessage}
documentKey={`pdf-step-${currentStep}`}
/>
</div>
</div>
)}
<span className="ui7-text_4">
</span>
<div className="ui7-signature-wrapper">
<canvas
ref={canvasRef}
className="ui7-signature-canvas"
onPointerDown={startDrawing}
onPointerMove={draw}
onPointerUp={stopDrawing}
onPointerLeave={stopDrawing}
onPointerCancel={stopDrawing}
/>
<button className="ui7-clear-button" onClick={clearCanvas}>
</button>
</div>
<div className="basic-confirm-section">
<BackButton text="返回" onClick={handleBack} />
{showWaitButton ? (
<WaitButton text={`等待(${countdown})S`} />
) : (
<ConfirmButton
text={isSubmitting ? "提交中..." : "确定"}
onClick={handleConfirm}
disabled={!hasSignature || isSubmitting}
/>
)}
</div>
</div>
</div>
</div>
);
};
export default UI7;