更新打印

This commit is contained in:
xianyi
2025-11-27 16:32:53 +08:00
parent 7d09614463
commit 43d1033296

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect, useCallback } from "react";
import React, { useState, useEffect, useCallback, useRef } from "react";
import { Document, Page } from "react-pdf";
import "react-pdf/dist/esm/Page/AnnotationLayer.css";
import "./UI8.css";
@@ -10,6 +10,10 @@ import ui8A from "../../assets/ui8A.png";
import ui8B from "../../assets/ui8B.png";
import { getDaojiandanPdf } from "../../api/hisApi";
const PREFERRED_PRINTER_KEY = "preferredPrinterName";
type PrinterInfo = ElectronPrinterInfo;
// 独立的 PDF 渲染组件,使用 React.memo 避免不必要的重新渲染
const PdfRenderer = React.memo<{
pdfFiles: string[];
@@ -62,6 +66,13 @@ const UI8: React.FC = () => {
const [pdfFiles, setPdfFiles] = useState<string[]>([]);
const [pageCounts, setPageCounts] = useState<Record<number, number>>({});
const [originPdfUrls, setOriginPdfUrls] = useState<string[]>([]);
const [printers, setPrinters] = useState<PrinterInfo[]>([]);
const [selectedPrinter, setSelectedPrinter] = useState<string>("");
const [printersLoading, setPrintersLoading] = useState<boolean>(false);
const [printerError, setPrinterError] = useState<string>("");
const [isPrinterDropdownOpen, setIsPrinterDropdownOpen] =
useState<boolean>(false);
const printerDropdownRef = useRef<HTMLDivElement | null>(null);
const getExamId = () => {
const storedId = localStorage.getItem("selectedExamId");
@@ -118,15 +129,20 @@ const UI8: React.FC = () => {
setPageCounts({});
const examId = getExamId();
window.electronAPI?.log("info", `[UI8] 开始获取导检单 PDFexam_id=${examId}`);
const res = await getDaojiandanPdf(parseInt(examId, 10));
if (res.Status !== 200) {
throw new Error(res.Message || "获取导检单PDF失败");
}
const pdfUrl = res.Data?.pdf_url;
if (!pdfUrl) {
throw new Error("获取导检单 PDF");
let pdfUrl = "";
const consentSignature = localStorage.getItem("consentSignature");
if (consentSignature) {
pdfUrl = consentSignature;
} else {
window.electronAPI?.log("info", `[UI8] 开始获取导检单 PDFexam_id=${examId}`);
const res = await getDaojiandanPdf(parseInt(examId, 10));
if (res.Status !== 200) {
throw new Error(res.Message || "获取导检单PDF失败");
}
pdfUrl = res.Data?.pdf_url;
if (!pdfUrl) {
throw new Error("未获取到导检单 PDF");
}
}
setOriginPdfUrls([pdfUrl]);
@@ -147,6 +163,107 @@ const UI8: React.FC = () => {
loadPdf();
}, []);
// 加载打印机列表并恢复上次选择
useEffect(() => {
if (!window.electronAPI?.getPrinters) {
setPrinterError("当前环境不支持打印机列表读取");
return;
}
let isMounted = true;
const fetchPrinters = async () => {
setPrintersLoading(true);
try {
window.electronAPI.log("info", "[UI8] 开始获取打印机列表");
const result = await window.electronAPI.getPrinters();
if (!isMounted) return;
if (!result.success) {
throw new Error(result.error || "获取打印机列表失败");
}
const list = result.printers || [];
setPrinters(list);
const stored = localStorage.getItem(PREFERRED_PRINTER_KEY);
const matchedStored =
stored && list.some((printer) => printer.name === stored)
? stored
: "";
const defaultPrinter =
matchedStored ||
list.find((printer) => printer.isDefault)?.name ||
list[0]?.name ||
"";
if (defaultPrinter) {
setSelectedPrinter(defaultPrinter);
localStorage.setItem(PREFERRED_PRINTER_KEY, defaultPrinter);
window.electronAPI.log(
"info",
`[UI8] 默认打印机设置为 ${defaultPrinter}`
);
}
setPrinterError("");
} catch (error) {
const msg =
(error as Error).message || "打印机列表获取失败,请联系管理员";
if (isMounted) {
setPrinterError(msg);
}
window.electronAPI.log("error", `[UI8] ${msg}`);
} finally {
if (isMounted) {
setPrintersLoading(false);
}
}
};
fetchPrinters();
return () => {
isMounted = false;
};
}, []);
const handlePrinterChange = useCallback(
(printerName: string) => {
setSelectedPrinter(printerName);
localStorage.setItem(PREFERRED_PRINTER_KEY, printerName);
window.electronAPI?.log(
"info",
`[UI8] 用户选择打印机: ${printerName}`
);
setIsPrinterDropdownOpen(false);
},
[]
);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
printerDropdownRef.current &&
!printerDropdownRef.current.contains(event.target as Node)
) {
setIsPrinterDropdownOpen(false);
}
};
if (isPrinterDropdownOpen) {
document.addEventListener("mousedown", handleClickOutside);
} else {
document.removeEventListener("mousedown", handleClickOutside);
}
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [isPrinterDropdownOpen]);
const togglePrinterDropdown = useCallback(() => {
if (printersLoading || printers.length === 0) return;
setIsPrinterDropdownOpen((prev) => !prev);
}, [printers.length, printersLoading]);
const handleBack = useCallback(() => {
navigate(-1);
}, [navigate]);
@@ -168,6 +285,11 @@ const UI8: React.FC = () => {
return;
}
if (printers.length > 0 && !selectedPrinter) {
alert("请先选择打印机");
return;
}
setIsPrinting(true);
try {
const printData =
@@ -176,10 +298,13 @@ const UI8: React.FC = () => {
: originPdfUrls[0];
const dataType = printData.startsWith("data:") ? "base64数据" : "远程文件路径";
window.electronAPI.log("info", `开始打印PDF (${dataType}): ${printData.substring(0, 100)}...`);
const result = await window.electronAPI.printPdf(printData);
if (!result.success) {
const errorMsg = `打印失败: ${result.error || "未知错误"}`;
const printResult = await window.electronAPI.printPdf(
printData,
selectedPrinter ? { printerName: selectedPrinter } : undefined
);
if (!printResult.success) {
const errorMsg = `打印失败: ${printResult.error || "未知错误"}`;
window.electronAPI.log("error", errorMsg);
alert(errorMsg);
} else {
@@ -194,7 +319,7 @@ const UI8: React.FC = () => {
setIsPrinting(false);
navigate("/UI9");
}
}, [originPdfUrls, pdfFiles]);
}, [originPdfUrls, pdfFiles, printers.length, selectedPrinter]);
const handlePageCountUpdate = useCallback((index: number, numPages: number) => {
window.electronAPI?.log("info", `[UI8] PDF渲染成功 (index=${index}),共 ${numPages}`);
@@ -216,6 +341,59 @@ const UI8: React.FC = () => {
return (
<div className="basic-root">
<div className="ui8-printer-panel" ref={printerDropdownRef}>
<div
className={`ui8-printer-trigger ${printersLoading || printers.length === 0 ? "disabled" : ""
} ${isPrinterDropdownOpen ? "open" : ""}`}
role="button"
tabIndex={0}
onClick={togglePrinterDropdown}
onKeyDown={(event) => {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
togglePrinterDropdown();
}
}}
>
<span>
{printersLoading
? "打印机加载中..."
: selectedPrinter
? printers.find((p) => p.name === selectedPrinter)?.displayName ||
selectedPrinter
: printers.length === 0
? "未检测到打印机"
: "请选择打印机"}
</span>
<span className="ui8-printer-arrow" />
</div>
{isPrinterDropdownOpen && printers.length > 0 && (
<div className="ui8-printer-options">
{printers.map((printer) => (
<div
key={printer.name}
className={`ui8-printer-option ${printer.name === selectedPrinter ? "active" : ""
}`}
onClick={() => handlePrinterChange(printer.name)}
role="button"
tabIndex={0}
onKeyDown={(event) => {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
handlePrinterChange(printer.name);
}
}}
>
{printer.displayName || printer.name}
</div>
))}
</div>
)}
{printerError && (
<div className="ui8-printer-error">{printerError}</div>
)}
</div>
<div className="basic-white-block">
<div className="basic-content">
<div className="ui8-pdf-container">
@@ -230,14 +408,14 @@ const UI8: React.FC = () => {
</div>
<div className="ui8-right-section">
<img src={ui8A} alt="" />
<img src={ui8B} alt="" />
<img src={ui8A} alt="" />
<img src={ui8B} alt="" />
</div>
<div className="basic-confirm-section">
<BackButton text="返回" onClick={handleBack} />
<ConfirmButton
text={isPrinting ? "打印中..." : "打印"}
<ConfirmButton
text={isPrinting ? "打印中..." : "打印"}
onClick={handleConfirm}
/>
</div>