支持获取pdf展示体检同意书
This commit is contained in:
1
src/assets/testPdfBase64
Normal file
1
src/assets/testPdfBase64
Normal file
File diff suppressed because one or more lines are too long
@@ -1,4 +1,5 @@
|
|||||||
import React, { useState, useEffect, useRef } from "react";
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
|
import { pdfjs } from "react-pdf";
|
||||||
import "./UI7.css";
|
import "./UI7.css";
|
||||||
import "../../assets/css/basic.css";
|
import "../../assets/css/basic.css";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
@@ -7,7 +8,20 @@ import ConfirmButton from "../../components/ConfirmButton";
|
|||||||
import DecorLine from "../../components/DecorLine";
|
import DecorLine from "../../components/DecorLine";
|
||||||
import WaitButton from "../../components/WaitButton";
|
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 navigate = useNavigate();
|
||||||
const [countdown, setCountdown] = useState(5);
|
const [countdown, setCountdown] = useState(5);
|
||||||
const [showWaitButton, setShowWaitButton] = useState(true);
|
const [showWaitButton, setShowWaitButton] = useState(true);
|
||||||
@@ -16,6 +30,95 @@ const UI6: React.FC = () => {
|
|||||||
const dprRef = useRef<number>(1);
|
const dprRef = useRef<number>(1);
|
||||||
const lastPointRef = useRef<{ x: number; y: number } | null>(null);
|
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(() => {
|
useEffect(() => {
|
||||||
if (countdown > 0) {
|
if (countdown > 0) {
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
@@ -191,62 +294,19 @@ const UI6: React.FC = () => {
|
|||||||
|
|
||||||
<div className="ui7-text-wrapper">
|
<div className="ui7-text-wrapper">
|
||||||
<div className="ui7-text-content">
|
<div className="ui7-text-content">
|
||||||
|
{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">
|
<span className="paragraph_1">
|
||||||
尊敬的受检者:
|
{pdfText.split("\n").map((line, index) => (
|
||||||
<br />
|
<React.Fragment key={index}>
|
||||||
<br />
|
{line}
|
||||||
欢迎您参加本次健康体检!为保障您的合法权益,现就体检相关事项告知如下,请您认真阅读并确认签署。
|
{index < pdfText.split("\n").length - 1 && <br />}
|
||||||
<br />
|
</React.Fragment>
|
||||||
一、体检目的
|
))}
|
||||||
<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 />
|
|
||||||
若因个人隐瞒病史或未遵守体检要求导致结果偏差,责任由受检者自
|
|
||||||
</span>
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span className="ui7-text_4">请阅读后在下方签名确认</span>
|
<span className="ui7-text_4">请阅读后在下方签名确认</span>
|
||||||
@@ -282,4 +342,4 @@ const UI6: React.FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default UI6;
|
export default UI7;
|
||||||
|
|||||||
Reference in New Issue
Block a user