fix: 问题修复
1. 编辑新增药品的信息同步his; 2. pdf调整(主要是压缩生成的pdf文件)
This commit is contained in:
parent
f1c1812516
commit
552dc546be
@ -286,6 +286,9 @@ async function addHlwDrugInfo(ctx) {
|
|||||||
}
|
}
|
||||||
newDrug.createTime = Date.now();
|
newDrug.createTime = Date.now();
|
||||||
const res = await db.collection("drug-info").insertOne(newDrug);
|
const res = await db.collection("drug-info").insertOne(newDrug);
|
||||||
|
if (res && res.insertedId) {
|
||||||
|
updateHisDrugInfo({ id: res.insertedId.toString() })
|
||||||
|
}
|
||||||
return { success: true, message: "新增药品成功", id: res.insertedId }
|
return { success: true, message: "新增药品成功", id: res.insertedId }
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return { success: false, message: e.message || '新增药品失败' }
|
return { success: false, message: e.message || '新增药品失败' }
|
||||||
@ -337,6 +340,7 @@ async function editHlwDrugInfo(ctx) {
|
|||||||
if (res.matchedCount === 0) {
|
if (res.matchedCount === 0) {
|
||||||
return { success: false, message: "未找到对应药品" }
|
return { success: false, message: "未找到对应药品" }
|
||||||
}
|
}
|
||||||
|
updateHisDrugInfo({ id })
|
||||||
return { success: true, message: "更新成功" }
|
return { success: true, message: "更新成功" }
|
||||||
}
|
}
|
||||||
return { success: true, message: "更新成功" }
|
return { success: true, message: "更新成功" }
|
||||||
|
|||||||
@ -12,9 +12,15 @@ let browserPromise;
|
|||||||
let activeJobs = 0;
|
let activeJobs = 0;
|
||||||
const waiters = [];
|
const waiters = [];
|
||||||
const cachedPdfJobs = new Map();
|
const cachedPdfJobs = new Map();
|
||||||
const PDF_TEMPLATE_VERSION = "v7";
|
const PDF_TEMPLATE_VERSION = "v11";
|
||||||
const MAX_SIGNATURE_IMAGE_BYTES = 1024 * 1024;
|
const MAX_SIGNATURE_IMAGE_BYTES = 1024 * 1024;
|
||||||
const SIGNATURE_DOWNLOAD_TIMEOUT_MS = 5000;
|
const SIGNATURE_DOWNLOAD_TIMEOUT_MS = 5000;
|
||||||
|
const MAX_PRESCRIPTION_PDF_BYTES = 100 * 1024;
|
||||||
|
const RASTER_PAGE_SIZES = {
|
||||||
|
// A5 (148mm) 减去左右 8mm 页边距后的实际打印宽度为 132mm。
|
||||||
|
A5: { viewport: { width: 500, height: 733, deviceScaleFactor: 1 }, contentWidth: "132mm", contentHeight: "194mm" },
|
||||||
|
A4: { viewport: { width: 733, height: 1062, deviceScaleFactor: 1 }, contentWidth: "194mm", contentHeight: "281mm" },
|
||||||
|
};
|
||||||
const PRESCRIPTION_SEAL_IMAGE_URL = process.env.CONFIG_PRESCRIPTION_SEAL_IMAGE_URL
|
const PRESCRIPTION_SEAL_IMAGE_URL = process.env.CONFIG_PRESCRIPTION_SEAL_IMAGE_URL
|
||||||
|| "https://hlwyy.hnkhhospital.com/upload-files/other/stamp.png";
|
|| "https://hlwyy.hnkhhospital.com/upload-files/other/stamp.png";
|
||||||
|
|
||||||
@ -96,7 +102,7 @@ async function getBrowser() {
|
|||||||
browserPromise = puppeteer.launch({
|
browserPromise = puppeteer.launch({
|
||||||
executablePath: getExecutablePath(),
|
executablePath: getExecutablePath(),
|
||||||
headless: true,
|
headless: true,
|
||||||
args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage"],
|
args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage", "--disable-gpu"],
|
||||||
});
|
});
|
||||||
const browser = await browserPromise;
|
const browser = await browserPromise;
|
||||||
browser.on("disconnected", () => {
|
browser.on("disconnected", () => {
|
||||||
@ -273,12 +279,12 @@ function buildPrescriptionHtml(record, paperSize = "A5", signatureImages = {}) {
|
|||||||
<style>
|
<style>
|
||||||
@page { size: ${size} portrait; margin: 8mm; }
|
@page { size: ${size} portrait; margin: 8mm; }
|
||||||
* { box-sizing: border-box; }
|
* { box-sizing: border-box; }
|
||||||
html, body { margin: 0; padding: 0; color: #111; font-family: "Noto Sans CJK SC", "Noto Sans SC", SimSun, "Songti SC", serif; font-size: 14px; }
|
html, body { margin: 0; padding: 0; color: #111; background: #fff; font-family: "Noto Sans CJK SC", "Noto Sans SC", SimSun, "Songti SC", serif; font-size: 14px; }
|
||||||
body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
||||||
.prescription { min-height: ${size === "A5" ? "194mm" : "281mm"}; display: flex; flex-direction: column; line-height: 1.28; overflow: hidden; }
|
.prescription { width: calc(100% - 1px); min-height: ${size === "A5" ? "194mm" : "281mm"}; display: flex; flex-direction: column; line-height: 1.28; overflow: hidden; }
|
||||||
.header { position: relative; border-bottom: 1px solid #111; padding: 3px 0 4px; }
|
.header { position: relative; border-bottom: 1px solid #111; padding: 3px 0 4px; }
|
||||||
.title { font-weight: 700; font-size: 17px; text-align: center; line-height: 1.4; }
|
.title { font-weight: 700; font-size: 17px; text-align: center; line-height: 1.4; }
|
||||||
.rx-type { position: absolute; top: 0; right: 0; width: 40px; border: 1px solid #333; text-align: center; color: #666; font-size: 12px; line-height: 1.3; }
|
.rx-type { position: absolute; top: 0; right: 1px; width: 40px; border: 1px solid #333; text-align: center; color: #666; font-size: 12px; line-height: 1.3; }
|
||||||
.rx-number { margin-top: 5px; }
|
.rx-number { margin-top: 5px; }
|
||||||
.patient-info { padding-top: 5px; }
|
.patient-info { padding-top: 5px; }
|
||||||
.info-row { display: flex; padding: 2px; }
|
.info-row { display: flex; padding: 2px; }
|
||||||
@ -323,30 +329,66 @@ function buildPrescriptionHtml(record, paperSize = "A5", signatureImages = {}) {
|
|||||||
</body></html>`;
|
</body></html>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildRasterPdfHtml(image, paperSize) {
|
||||||
|
const size = RASTER_PAGE_SIZES[paperSize];
|
||||||
|
return `<!doctype html><html><head><meta charset="utf-8" /><style>
|
||||||
|
@page { size: ${paperSize} portrait; margin: 8mm; }
|
||||||
|
html, body { margin: 0; padding: 0; }
|
||||||
|
img { display: block; width: ${size.contentWidth}; height: ${size.contentHeight}; }
|
||||||
|
</style></head><body><img src="${image}" alt="处方笺" /></body></html>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generateCompactPrescriptionPdf(html, paperSize) {
|
||||||
|
// Chrome 会为中文文本嵌入字体子集;即使没有图片,单页也约 280KB。
|
||||||
|
// 将实际打印区域以 96dpi 截图并转为 JPEG,可保留处方可读性且避免字体嵌入。
|
||||||
|
const browser = await getBrowser();
|
||||||
|
const size = RASTER_PAGE_SIZES[paperSize];
|
||||||
|
const rasterPage = await browser.newPage();
|
||||||
|
try {
|
||||||
|
await rasterPage.setViewport(size.viewport);
|
||||||
|
await rasterPage.setContent(html, { waitUntil: "load" });
|
||||||
|
await rasterPage.evaluate(() => document.fonts && document.fonts.ready);
|
||||||
|
const screenshot = await rasterPage.screenshot({
|
||||||
|
type: "jpeg",
|
||||||
|
quality: 72,
|
||||||
|
clip: { x: 0, y: 0, width: size.viewport.width, height: size.viewport.height },
|
||||||
|
});
|
||||||
|
const compactPage = await browser.newPage();
|
||||||
|
try {
|
||||||
|
await compactPage.setContent(
|
||||||
|
buildRasterPdfHtml(`data:image/jpeg;base64,${Buffer.from(screenshot).toString("base64")}`, paperSize),
|
||||||
|
{ waitUntil: "load" }
|
||||||
|
);
|
||||||
|
const pdf = Buffer.from(await compactPage.pdf({
|
||||||
|
format: paperSize,
|
||||||
|
preferCSSPageSize: true,
|
||||||
|
printBackground: true,
|
||||||
|
margin: { top: 0, right: 0, bottom: 0, left: 0 },
|
||||||
|
}));
|
||||||
|
if (pdf.length > MAX_PRESCRIPTION_PDF_BYTES) {
|
||||||
|
throw new Error(`处方 PDF 压缩后仍超过 ${MAX_PRESCRIPTION_PDF_BYTES} 字节`);
|
||||||
|
}
|
||||||
|
return pdf;
|
||||||
|
} finally {
|
||||||
|
await compactPage.close().catch(() => {});
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await rasterPage.close().catch(() => {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function generatePrescriptionPdf(record, options = {}) {
|
async function generatePrescriptionPdf(record, options = {}) {
|
||||||
await acquireSlot();
|
await acquireSlot();
|
||||||
let page;
|
|
||||||
try {
|
try {
|
||||||
const browser = await getBrowser();
|
|
||||||
page = await browser.newPage();
|
|
||||||
const [doctor, pharmacist, seal] = await Promise.all([
|
const [doctor, pharmacist, seal] = await Promise.all([
|
||||||
loadSignatureDataImage(record.doctorSignImg),
|
loadSignatureDataImage(record.doctorSignImg),
|
||||||
loadSignatureDataImage(record.pharmacistSignImg),
|
loadSignatureDataImage(record.pharmacistSignImg),
|
||||||
loadSignatureDataImage(PRESCRIPTION_SEAL_IMAGE_URL),
|
loadSignatureDataImage(PRESCRIPTION_SEAL_IMAGE_URL),
|
||||||
]);
|
]);
|
||||||
await page.setContent(
|
const html = buildPrescriptionHtml(record, options.paperSize, { doctor, pharmacist, seal });
|
||||||
buildPrescriptionHtml(record, options.paperSize, { doctor, pharmacist, seal }),
|
const paperSize = options.paperSize === "A4" ? "A4" : "A5";
|
||||||
{ waitUntil: "load" }
|
return generateCompactPrescriptionPdf(html, paperSize);
|
||||||
);
|
|
||||||
await page.evaluate(() => document.fonts && document.fonts.ready);
|
|
||||||
return Buffer.from(await page.pdf({
|
|
||||||
format: options.paperSize === "A4" ? "A4" : "A5",
|
|
||||||
preferCSSPageSize: true,
|
|
||||||
printBackground: true,
|
|
||||||
margin: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
||||||
}));
|
|
||||||
} finally {
|
} finally {
|
||||||
if (page) await page.close().catch(() => {});
|
|
||||||
releaseSlot();
|
releaseSlot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user