支持获取pdf展示体检同意书

This commit is contained in:
xianyi
2025-11-24 11:17:10 +08:00
parent 88973906a4
commit 3c984f3b68
2 changed files with 117 additions and 56 deletions
File diff suppressed because one or more lines are too long
+116 -56
View File
@@ -1,4 +1,5 @@
import React, { useState, useEffect, useRef } from "react";
import { pdfjs } from "react-pdf";
import "./UI7.css";
import "../../assets/css/basic.css";
import { useNavigate } from "react-router-dom";
@@ -7,7 +8,20 @@ import ConfirmButton from "../../components/ConfirmButton";
import DecorLine from "../../components/DecorLine";
import WaitButton from "../../components/WaitButton";
const UI6: React.FC = () => {
// @ts-ignore - Vite 会处理 ?raw 导入
import testPdfBase64Raw from "../../assets/testPdfBase64?raw";
const testPdfBase64 = testPdfBase64Raw.trim();
// 配置 PDF.js worker
const workerPath = new URL("pdf.worker.min.js", document.baseURI).href;
pdfjs.GlobalWorkerOptions.workerSrc = workerPath;
if (window.electronAPI) {
window.electronAPI.log("info", `[UI7] PDF Worker 路径: ${workerPath}`);
}
const UI7: React.FC = () => {
const navigate = useNavigate();
const [countdown, setCountdown] = useState(5);
const [showWaitButton, setShowWaitButton] = useState(true);
@@ -15,6 +29,95 @@ const UI6: React.FC = () => {
const [isDrawing, setIsDrawing] = useState(false);
const dprRef = useRef<number>(1);
const lastPointRef = useRef<{ x: number; y: number } | null>(null);
// PDF 文本提取相关状态
const [pdfText, setPdfText] = useState<string>("");
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string>("");
// 从 PDF 中提取文本
useEffect(() => {
const extractTextFromPdf = async () => {
try {
setLoading(true);
window.electronAPI?.log("info", "[UI7] 开始从 PDF 提取文本");
// 清理 base64 字符串(移除可能的换行和空格)
const cleanBase64 = testPdfBase64.replace(/\s/g, "");
window.electronAPI?.log("info", `[UI7] Base64 长度: ${cleanBase64.length}`);
// 将 base64 转换为 Uint8Array
const binaryString = atob(cleanBase64);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
window.electronAPI?.log("info", `[UI7] 转换为 Uint8Array,长度: ${bytes.length}`);
// 加载 PDF 文档(使用 Uint8Array)
const loadingTask = pdfjs.getDocument({ data: bytes });
const pdf = await loadingTask.promise;
window.electronAPI?.log("info", `[UI7] PDF 加载成功,共 ${pdf.numPages} 页`);
// 提取所有页面的文本
let allText = "";
for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
const page = await pdf.getPage(pageNum);
const textContent = await page.getTextContent();
// 将文本项合并为字符串,保留换行信息
let pageText = "";
let lastY = null;
for (let i = 0; i < textContent.items.length; i++) {
const item = textContent.items[i] as any;
const currentY = item.transform ? item.transform[5] : null; // Y 坐标
// 如果 Y 坐标变化较大(超过行高的一半),认为是新行
if (lastY !== null && currentY !== null && Math.abs(currentY - lastY) > 5) {
pageText += "\n";
}
pageText += item.str;
// 如果当前项后面有空格标记,添加空格
if (item.hasEOL) {
pageText += "\n";
} else if (i < textContent.items.length - 1) {
const nextItem = textContent.items[i + 1] as any;
const nextX = nextItem.transform ? nextItem.transform[4] : null;
const currentX = item.transform ? item.transform[4] : null;
const currentWidth = item.width || 0;
// 如果下一个文本项距离较远,添加空格
if (currentX !== null && nextX !== null && (nextX - (currentX + currentWidth)) > 2) {
pageText += " ";
}
}
lastY = currentY;
}
allText += pageText + "\n\n";
}
window.electronAPI?.log("info", `[UI7] PDF 文本提取完成,长度: ${allText.length}`);
setPdfText(allText.trim());
setLoading(false);
setError("");
} catch (err) {
const errorMsg = `PDF文本提取失败: ${err}`;
console.error(errorMsg);
window.electronAPI?.log("error", `[UI7] ${errorMsg}`);
setError("PDF 文本提取失败,请检查文件");
setLoading(false);
}
};
extractTextFromPdf();
}, []);
useEffect(() => {
if (countdown > 0) {
@@ -191,62 +294,19 @@ const UI6: React.FC = () => {
<div className="ui7-text-wrapper">
<div className="ui7-text-content">
<span className="paragraph_1">
尊敬的受检者:
<br />
<br />
欢迎您参加本次健康体检!为保障您的合法权益,现就体检相关事项告知如下,请您认真阅读并确认签署。
<br />
一、体检目的
<br />
<br />
本次健康体检旨在通过常规医学检查,了解您的身体健康状况,发现潜在的疾病风险,供您及医生进行健康指导与干预。
<br />
1.
<br />
体检结果仅供参考,不等同于临床诊断。如发现异常,请及时到相关专科医院进一步检查与治疗。
<br />
2.
<br />
二、体检须知
<br />
3.
<br />
空腹要求:抽血、腹部B超等项目需空腹8小时以上。
<br />
4.
<br />
女性注意事项:经期及妊娠期女性请提前告知医护人员,避免放射类检查(如胸片、CT等)。
<br />
5.
<br />
贵重物品:请妥善保管个人物品,中心不承担遗失责任。
<br />
身体状况告知:如有重大疾病史、药物过敏史、手术史等,请提前告知医护人员。
<br />
体检安全:如在体检过程中出现不适,应立即告知现场医护人员。
<br />
<br />
三、隐私与信息保护
<br />
1.
<br />
体检机构将严格遵守国家相关法律法规,对您的个人信息和体检资料予以保密,未经本人授权,不会向第三方泄露。
<br />
2.
<br />
四、体检风险告知
<br />
3.
<br />
部分检查(如抽血、X射线、CT、宫颈刮片等)可能带来轻微不适或风险。
<br />
体检结果可能存在一定误差,需结合临床进一步判断。
<br />
若因个人隐瞒病史或未遵守体检要求导致结果偏差,责任由受检者自
{loading && <div style={{ padding: "20px", textAlign: "center" }}>PDF文本提取中...</div>}
{error && <div style={{ padding: "20px", color: "red", textAlign: "center" }}>{error}</div>}
{!loading && !error && pdfText && (
<span className="paragraph_1">
{pdfText.split("\n").map((line, index) => (
<React.Fragment key={index}>
{line}
{index < pdfText.split("\n").length - 1 && <br />}
</React.Fragment>
))}
</span>
)}
</div>
</div>
<span className="ui7-text_4">请阅读后在下方签名确认</span>
@@ -282,4 +342,4 @@ const UI6: React.FC = () => {
);
};
export default UI6;
export default UI7;