报告打印PDF
This commit is contained in:
commit
c023644ade
8
.env
Normal file
8
.env
Normal file
@ -0,0 +1,8 @@
|
||||
# API基础地址
|
||||
API_BASE_URL=https://pacs.gw12320.com/adminInspect/admin-api
|
||||
# 服务端口
|
||||
PORT=3000
|
||||
# PDF输出目录(可以是相对路径或绝对路径)
|
||||
# 相对路径示例:public/pdfs
|
||||
# 绝对路径示例:D:/reports/pdfs
|
||||
PDF_OUTPUT_DIR=public/pdfs
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
node_modules
|
BIN
chrome/chrome.exe
Normal file
BIN
chrome/chrome.exe
Normal file
Binary file not shown.
26
logs/debug.log
Normal file
26
logs/debug.log
Normal file
@ -0,0 +1,26 @@
|
||||
2025-04-07T07:41:54.892Z: PDF输出目录设置为: D:\桌面\个人\开发环境\report-service\public\pdfs
|
||||
2025-04-07T07:42:17.998Z: 找到小结内容,来源项目: "身高", 内容: "未见异常..."
|
||||
2025-04-07T07:42:18.000Z: 第一个数据项的完整结构: {"itemCode":"TW001","itemName":"体温","examDescription":null,"itemResult":"36","itemValue":36,"itemStatus":"1","analyse":"未见异常","inspectdoctor":"丁国凯","inspecttime":1743996963000,"pacsData":null,"pacsDataType":null,"data":null,"type":"体温"}
|
||||
2025-04-07T07:42:18.005Z: 替换完成,内容长度: 4
|
||||
2025-04-07T07:42:18.078Z - 替换后的内容: <div class="report-summary" id="ultrasound-summary">
|
||||
<p>
|
||||
<strong...
|
||||
2025-04-07T07:42:33.577Z: 调试HTML已保存至: D:\桌面\个人\开发环境\report-service\logs\last-report.html
|
||||
2025-04-07T07:45:44.525Z: 使用绝对路径: D:/桌面/1
|
||||
2025-04-07T07:45:44.527Z: PDF输出目录设置为: D:/桌面/1
|
||||
2025-04-07T07:45:50.538Z: 找到小结内容,来源项目: "身高", 内容: "未见异常..."
|
||||
2025-04-07T07:45:50.540Z: 第一个数据项的完整结构: {"itemCode":"TW001","itemName":"体温","examDescription":null,"itemResult":"36","itemValue":36,"itemStatus":"1","analyse":"未见异常","inspectdoctor":"丁国凯","inspecttime":1743996963000,"pacsData":null,"pacsDataType":null,"data":null,"type":"体温"}
|
||||
2025-04-07T07:45:50.544Z: 替换完成,内容长度: 4
|
||||
2025-04-07T07:45:50.624Z - 替换后的内容: <div class="report-summary" id="ultrasound-summary">
|
||||
<p>
|
||||
<strong...
|
||||
2025-04-07T07:46:03.524Z: 调试HTML已保存至: D:\桌面\个人\开发环境\report-service\logs\last-report.html
|
||||
2025-04-07T07:48:10.657Z: 找到小结内容,来源项目: "身高", 内容: "未见异常..."
|
||||
2025-04-07T07:48:10.657Z: 第一个数据项的完整结构: {"itemCode":"TW001","itemName":"体温","examDescription":null,"itemResult":"36","itemValue":36,"itemStatus":"1","analyse":"未见异常","inspectdoctor":"丁国凯","inspecttime":1743996963000,"pacsData":null,"pacsDataType":null,"data":null,"type":"体温"}
|
||||
2025-04-07T07:48:10.659Z: 替换完成,内容长度: 4
|
||||
2025-04-07T07:48:10.702Z - 替换后的内容: <div class="report-summary" id="ultrasound-summary">
|
||||
<p>
|
||||
<strong...
|
||||
2025-04-07T07:48:21.063Z: 调试HTML已保存至: D:\桌面\个人\开发环境\report-service\logs\last-report.html
|
||||
2025-04-07T09:30:53.039Z: 使用绝对路径: D:/桌面/1
|
||||
2025-04-07T09:30:53.040Z: PDF输出目录设置为: D:/桌面/1
|
2247
logs/last-report.html
Normal file
2247
logs/last-report.html
Normal file
File diff suppressed because one or more lines are too long
2089
package-lock.json
generated
Normal file
2089
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
23
package.json
Normal file
23
package.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "report-service",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start": "node server.js",
|
||||
"dev": "nodemon server.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"axios": "^1.8.4",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.4.7",
|
||||
"express": "^5.1.0",
|
||||
"fs-extra": "^11.3.0",
|
||||
"path": "^0.12.7",
|
||||
"puppeteer": "^24.4.0"
|
||||
}
|
||||
}
|
1521
pnpm-lock.yaml
Normal file
1521
pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load Diff
2078
public/templates/report-template.html
Normal file
2078
public/templates/report-template.html
Normal file
File diff suppressed because it is too large
Load Diff
BIN
public/templates/首页标签.png
Normal file
BIN
public/templates/首页标签.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
BIN
public/test.html
Normal file
BIN
public/test.html
Normal file
Binary file not shown.
97
routes/report.js
Normal file
97
routes/report.js
Normal file
@ -0,0 +1,97 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const PDFGenerator = require('../utils/pdfGenerator');
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
const axios = require('axios');
|
||||
|
||||
const pdfGenerator = new PDFGenerator();
|
||||
// 从配置文件读取基础URL
|
||||
const BASE_URL = process.env.API_BASE_URL || 'http://192.168.1.100:8080'; // 请替换为实际的医疗系统地址
|
||||
|
||||
// 从医疗系统获取报告数据
|
||||
async function getReportData(medicalSn) {
|
||||
try {
|
||||
console.log(`正在请求医疗系统API: ${BASE_URL}/inspect/patient/getReportAll`);
|
||||
console.log('请求参数:', { medicalSn });
|
||||
const response = await axios.get(`${BASE_URL}/inspect/patient/getReportAll`, {
|
||||
params: {
|
||||
medicalSn: medicalSn
|
||||
}
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('获取报告数据失败:', {
|
||||
message: error.message,
|
||||
code: error.code,
|
||||
response: error.response?.data,
|
||||
status: error.response?.status
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 生成PDF的路由
|
||||
router.post('/generate', async (req, res) => {
|
||||
try {
|
||||
console.log('收到生成PDF请求:', req.body);
|
||||
const { medicalSn } = req.body;
|
||||
if (!medicalSn) {
|
||||
throw new Error('体检编号不能为空');
|
||||
}
|
||||
const reportData = await getReportData(medicalSn);
|
||||
|
||||
// 检查是否为无效数据
|
||||
const isInvalidData = reportData.code === 0 &&
|
||||
reportData.data &&
|
||||
Object.values(reportData.data).every(value => value === null);
|
||||
|
||||
if (!reportData || isInvalidData) {
|
||||
throw new Error('体检编号不存在或未查询到相关报告数据');
|
||||
}
|
||||
|
||||
console.log('开始生成PDF...');
|
||||
const result = await pdfGenerator.generatePDF(reportData);
|
||||
console.log('PDF生成成功:', result);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
fileName: result.fileName,
|
||||
path: `/pdfs/${result.fileName}`
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('生成PDF失败,详细错误:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: error.message || '生成PDF失败'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 下载PDF的路由
|
||||
router.get('/download/:fileName', async (req, res) => {
|
||||
try {
|
||||
const { fileName } = req.params;
|
||||
const filePath = path.join(__dirname, '../public/pdfs', fileName);
|
||||
|
||||
const exists = await fs.pathExists(filePath);
|
||||
if (!exists) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'PDF文件不存在'
|
||||
});
|
||||
}
|
||||
|
||||
res.download(filePath);
|
||||
} catch (error) {
|
||||
console.error('下载PDF失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '下载PDF失败'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
42
server.js
Normal file
42
server.js
Normal file
@ -0,0 +1,42 @@
|
||||
// 加载环境变量
|
||||
const dotenv = require('dotenv');
|
||||
const path = require('path');
|
||||
|
||||
// 加载环境变量
|
||||
dotenv.config();
|
||||
|
||||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
const reportRoutes = require('./routes/report');
|
||||
|
||||
const app = express();
|
||||
|
||||
// 中间件
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
|
||||
// 静态文件服务
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
|
||||
// 路由
|
||||
app.use('/api/report', reportRoutes);
|
||||
|
||||
// 添加模板预览路由
|
||||
app.get('/preview-template', (req, res) => {
|
||||
res.sendFile(path.join(__dirname, 'public/templates/report-template.html'));
|
||||
});
|
||||
|
||||
// 错误处理中间件
|
||||
app.use((err, req, res, next) => {
|
||||
console.error(err.stack);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器内部错误'
|
||||
});
|
||||
});
|
||||
|
||||
const PORT = process.env.PORT || 3000;
|
||||
app.listen(PORT, () => {
|
||||
console.log(`服务器运行在端口 ${PORT}`);
|
||||
console.log(`API基础地址: ${process.env.API_BASE_URL}`);
|
||||
});
|
31
service_log.txt
Normal file
31
service_log.txt
Normal file
@ -0,0 +1,31 @@
|
||||
启动时间: 周一 2025/04/07 17:30:48.68
|
||||
[信息] 检查 Node.js 安装状态...
|
||||
[成功] Node.js 已安装
|
||||
v22.11.0
|
||||
[信息] 检查 PM2 安装状态...
|
||||
[成功] PM2 已安装
|
||||
当前工作目录: D:\桌面\个人\开发环境\report-service
|
||||
[信息] 检查 package.json...
|
||||
[成功] 找到 package.json
|
||||
[信息] 正在安装项目依赖...
|
||||
|
||||
up to date in 1s
|
||||
|
||||
24 packages are looking for funding
|
||||
run `npm fund` for details
|
||||
[成功] 依赖安装完成
|
||||
[信息] 服务已在运行,正在重启...
|
||||
Use --update-env to update environment variables
|
||||
[PM2] Applying action restartProcessId on app [report-service](ids: [ 0 ])
|
||||
[PM2] [report-service](0) ✓
|
||||
┌────┬───────────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
|
||||
│ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │
|
||||
├────┼───────────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
|
||||
│ 0 │ report-service │ default │ 1.0.0 │ fork │ 30000 │ 0s │ 1 │ online │ 0% │ 55.1mb │ 桜 │ disabled │
|
||||
└────┴───────────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
|
||||
┌────┬───────────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
|
||||
│ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │
|
||||
├────┼───────────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
|
||||
│ 0 │ report-service │ default │ 1.0.0 │ fork │ 30000 │ 2s │ 1 │ online │ 0% │ 69.9mb │ 桜 │ disabled │
|
||||
└────┴───────────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
|
||||
[成功] 服务已启动!
|
124
start-service.bat
Normal file
124
start-service.bat
Normal file
@ -0,0 +1,124 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
title 报告服务启动程序
|
||||
|
||||
:: 创建日志文件
|
||||
echo 启动时间: %date% %time% > service_log.txt
|
||||
|
||||
:: 设置控制台颜色
|
||||
color 0A
|
||||
|
||||
echo ======================================
|
||||
echo 报告服务启动程序
|
||||
echo ======================================
|
||||
echo.
|
||||
|
||||
:: 检查 Node.js 是否安装
|
||||
echo [信息] 检查 Node.js 安装状态... >> service_log.txt
|
||||
where node >nul 2>nul
|
||||
if %errorlevel% neq 0 (
|
||||
echo [错误] 未安装 Node.js,请先安装 Node.js
|
||||
echo [错误] 未安装 Node.js,请先安装 Node.js >> service_log.txt
|
||||
echo 请访问 https://nodejs.org 下载安装
|
||||
echo.
|
||||
echo 按任意键退出...
|
||||
pause > nul
|
||||
exit /b 1
|
||||
)
|
||||
echo [成功] Node.js 已安装 >> service_log.txt
|
||||
|
||||
:: 显示 Node.js 版本
|
||||
echo [信息] Node.js 版本:
|
||||
node -v
|
||||
node -v >> service_log.txt
|
||||
|
||||
:: 检查是否已安装 PM2
|
||||
echo [信息] 检查 PM2 安装状态... >> service_log.txt
|
||||
where pm2 >nul 2>nul
|
||||
if %errorlevel% neq 0 (
|
||||
echo [信息] 正在安装 PM2...
|
||||
echo [信息] 正在安装 PM2... >> service_log.txt
|
||||
call npm install -g pm2
|
||||
if %errorlevel% neq 0 (
|
||||
echo [错误] PM2 安装失败,请以管理员权限运行此脚本
|
||||
echo [错误] PM2 安装失败 >> service_log.txt
|
||||
echo 请右键选择"以管理员身份运行"此脚本
|
||||
echo.
|
||||
echo 按任意键退出...
|
||||
pause > nul
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
echo [成功] PM2 已安装 >> service_log.txt
|
||||
|
||||
:: 切换到脚本所在目录
|
||||
echo [信息] 切换工作目录... >> service_log.txt
|
||||
cd /d "%~dp0"
|
||||
echo 当前工作目录: %cd%
|
||||
echo 当前工作目录: %cd% >> service_log.txt
|
||||
|
||||
:: 检查是否存在 package.json
|
||||
echo [信息] 检查 package.json... >> service_log.txt
|
||||
dir package.json 2>nul | find "package.json" >nul
|
||||
if %errorlevel% neq 0 (
|
||||
echo [错误] 未找到 package.json 文件
|
||||
echo [错误] 未找到 package.json 文件 >> service_log.txt
|
||||
echo 请确保在正确的项目目录中运行此脚本
|
||||
echo 当前目录: %cd%
|
||||
echo.
|
||||
echo 目录内容:
|
||||
dir
|
||||
echo.
|
||||
echo 按任意键退出...
|
||||
pause > nul
|
||||
exit /b 1
|
||||
)
|
||||
echo [成功] 找到 package.json >> service_log.txt
|
||||
|
||||
:: 安装依赖
|
||||
echo [信息] 正在安装项目依赖...
|
||||
echo [信息] 正在安装项目依赖... >> service_log.txt
|
||||
call npm install >> service_log.txt 2>&1
|
||||
if %errorlevel% neq 0 (
|
||||
echo [错误] 依赖安装失败
|
||||
echo [错误] 依赖安装失败 >> service_log.txt
|
||||
echo 详细错误信息请查看 service_log.txt
|
||||
echo.
|
||||
echo 按任意键退出...
|
||||
pause > nul
|
||||
exit /b 1
|
||||
)
|
||||
echo [成功] 依赖安装完成 >> service_log.txt
|
||||
|
||||
:: 检查服务是否已在运行
|
||||
echo [信息] 检查服务状态... >> service_log.txt
|
||||
call pm2 list | findstr "report-service" >nul
|
||||
if %errorlevel% equ 0 (
|
||||
echo [信息] 服务已在运行,正在重启...
|
||||
echo [信息] 服务已在运行,正在重启... >> service_log.txt
|
||||
call pm2 restart report-service >> service_log.txt 2>&1
|
||||
) else (
|
||||
echo [信息] 正在启动服务...
|
||||
echo [信息] 正在启动服务... >> service_log.txt
|
||||
call pm2 start server.js --name "report-service" >> service_log.txt 2>&1
|
||||
)
|
||||
|
||||
:: 显示服务状态
|
||||
echo.
|
||||
echo [信息] 服务状态:
|
||||
call pm2 list
|
||||
call pm2 list >> service_log.txt
|
||||
|
||||
echo.
|
||||
echo [成功] 服务已启动!
|
||||
echo [成功] 服务已启动! >> service_log.txt
|
||||
echo 访问地址: http://localhost:3000
|
||||
echo.
|
||||
echo 提示:请不要关闭此窗口,服务将在后台继续运行
|
||||
echo 如需停止服务,请运行 stop-service.bat
|
||||
echo 如遇问题,请查看 service_log.txt 文件了解详细信息
|
||||
echo ======================================
|
||||
|
||||
echo.
|
||||
echo 按任意键继续...
|
||||
pause > nul
|
37
stop-service.bat
Normal file
37
stop-service.bat
Normal file
@ -0,0 +1,37 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
title 报告服务停止程序
|
||||
|
||||
:: 设置控制台颜色
|
||||
color 0C
|
||||
|
||||
echo ======================================
|
||||
echo 报告服务停止程序
|
||||
echo ======================================
|
||||
echo.
|
||||
|
||||
:: 检查 PM2 是否运行
|
||||
where pm2 >nul 2>nul
|
||||
if %errorlevel% neq 0 (
|
||||
echo [错误] PM2 未安装,服务可能未在运行
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
|
||||
:: 检查服务是否在运行
|
||||
call pm2 list | findstr "report-service" >nul
|
||||
if %errorlevel% neq 0 (
|
||||
echo [信息] 服务当前未运行
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
|
||||
:: 停止服务
|
||||
echo [信息] 正在停止服务...
|
||||
call pm2 stop report-service
|
||||
call pm2 delete report-service
|
||||
|
||||
echo.
|
||||
echo [成功] 服务已停止!
|
||||
echo ======================================
|
||||
pause
|
856
utils/pdfGenerator.js
Normal file
856
utils/pdfGenerator.js
Normal file
@ -0,0 +1,856 @@
|
||||
const puppeteer = require('puppeteer');
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
const dotenv = require('dotenv');
|
||||
|
||||
// 加载环境变量
|
||||
dotenv.config();
|
||||
|
||||
const logFile = path.join(__dirname, '../logs/debug.log');
|
||||
fs.ensureDirSync(path.dirname(logFile));
|
||||
|
||||
function logToFile(message) {
|
||||
const timestamp = new Date().toISOString();
|
||||
fs.appendFileSync(logFile, `${timestamp}: ${message}\n`);
|
||||
}
|
||||
|
||||
class PDFGenerator {
|
||||
constructor(options = {}) {
|
||||
// 从环境变量获取PDF输出目录,支持绝对路径和相对路径
|
||||
let outputDirFromEnv;
|
||||
|
||||
if (process.env.PDF_OUTPUT_DIR) {
|
||||
// 检查是否是绝对路径
|
||||
if (path.isAbsolute(process.env.PDF_OUTPUT_DIR)) {
|
||||
outputDirFromEnv = process.env.PDF_OUTPUT_DIR;
|
||||
logToFile(`使用绝对路径: ${outputDirFromEnv}`);
|
||||
} else {
|
||||
// 相对路径,相对于项目根目录
|
||||
outputDirFromEnv = path.join(__dirname, '..', process.env.PDF_OUTPUT_DIR);
|
||||
logToFile(`使用相对路径: ${outputDirFromEnv}`);
|
||||
}
|
||||
} else {
|
||||
// 默认路径
|
||||
outputDirFromEnv = path.join(__dirname, '../public/pdfs');
|
||||
logToFile(`使用默认路径: ${outputDirFromEnv}`);
|
||||
}
|
||||
|
||||
this.options = {
|
||||
templatePath: path.join(__dirname, '../public/templates/report-template.html'),
|
||||
outputDir: outputDirFromEnv,
|
||||
...options
|
||||
};
|
||||
|
||||
logToFile(`PDF输出目录设置为: ${this.options.outputDir}`);
|
||||
}
|
||||
|
||||
async generatePDF(reportData) {
|
||||
// 确保我们使用的是 data 字段中的数据
|
||||
const data = reportData.data;
|
||||
|
||||
let browser = null;
|
||||
try {
|
||||
logToFile('开始生成PDF...');
|
||||
logToFile('启动浏览器...');
|
||||
browser = await puppeteer.launch({
|
||||
headless: 'new',
|
||||
defaultViewport: { width: 1200, height: 800 },
|
||||
executablePath: 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
|
||||
args: [
|
||||
'--allow-file-access-from-files',
|
||||
'--disable-web-security',
|
||||
'--no-sandbox',
|
||||
'--disable-setuid-sandbox',
|
||||
'--disable-dev-shm-usage'
|
||||
],
|
||||
timeout: 60000 // 增加浏览器启动超时时间
|
||||
});
|
||||
logToFile('浏览器启动成功');
|
||||
|
||||
const page = await browser.newPage();
|
||||
logToFile('创建新页面成功');
|
||||
|
||||
// 设置页面超时时间
|
||||
page.setDefaultNavigationTimeout(60000);
|
||||
page.setDefaultTimeout(60000);
|
||||
|
||||
logToFile('读取模板文件...');
|
||||
let htmlContent = await fs.readFile(this.options.templatePath, 'utf-8');
|
||||
logToFile('模板文件读取成功');
|
||||
|
||||
// 更新模板内容
|
||||
logToFile('开始更新模板内容...');
|
||||
htmlContent = await this.updateTemplateContent(htmlContent, data);
|
||||
logToFile('模板内容更新完成');
|
||||
|
||||
logToFile('设置页面内容...');
|
||||
try {
|
||||
await page.setContent(htmlContent, {
|
||||
waitUntil: 'domcontentloaded', // 改为domcontentloaded,不等待所有资源加载完成
|
||||
timeout: 60000 // 增加超时时间
|
||||
});
|
||||
logToFile('页面内容设置完成');
|
||||
} catch (error) {
|
||||
logToFile(`设置页面内容失败: ${error.message}`);
|
||||
// 尝试使用更宽松的加载策略
|
||||
await page.setContent(htmlContent, {
|
||||
waitUntil: 'load', // 只等待页面加载完成
|
||||
timeout: 60000
|
||||
});
|
||||
logToFile('使用备用策略设置页面内容完成');
|
||||
}
|
||||
|
||||
// 等待一段时间确保页面稳定
|
||||
logToFile('等待页面稳定...');
|
||||
await page.evaluate(() => {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(resolve, 2000);
|
||||
});
|
||||
});
|
||||
logToFile('页面稳定等待完成');
|
||||
|
||||
// 隐藏所有 print-only pdf-container 容器
|
||||
logToFile('开始处理PDF容器...');
|
||||
await page.evaluate(() => {
|
||||
const containers = document.querySelectorAll('.print-only.pdf-container');
|
||||
containers.forEach(container => {
|
||||
container.style.display = 'none';
|
||||
});
|
||||
});
|
||||
logToFile('PDF容器处理完成');
|
||||
|
||||
// 等待图片加载
|
||||
logToFile('开始等待图片加载...');
|
||||
try {
|
||||
await page.evaluate(async () => {
|
||||
console.log('开始检查图片加载状态...');
|
||||
try {
|
||||
const imageLoadResults = await Promise.all(
|
||||
Array.from(document.images)
|
||||
.map((img, index) => {
|
||||
console.log(`图片 ${index}: ${img.src}`);
|
||||
if (img.complete) {
|
||||
console.log(`图片 ${index} 已完成加载`);
|
||||
return Promise.resolve(img.src);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
img.onload = () => {
|
||||
console.log(`图片 ${index} 加载成功`);
|
||||
resolve(img.src);
|
||||
};
|
||||
img.onerror = (e) => {
|
||||
console.error(`图片 ${index} 加载失败: ${img.src}`);
|
||||
resolve('error');
|
||||
};
|
||||
});
|
||||
})
|
||||
);
|
||||
console.log('所有图片加载结果:', imageLoadResults);
|
||||
} catch (error) {
|
||||
console.error('图片加载过程中出错:', error);
|
||||
}
|
||||
});
|
||||
logToFile('图片加载完成');
|
||||
} catch (error) {
|
||||
logToFile(`图片加载过程中出错: ${error.message}`);
|
||||
// 继续执行,不中断流程
|
||||
}
|
||||
|
||||
// 调整主检医生签名样式
|
||||
await page.evaluate(() => {
|
||||
const doctorImages = document.querySelectorAll('#summary-doctor-image, #summary-doctor-image2');
|
||||
doctorImages.forEach(img => {
|
||||
img.style.width = '30px';
|
||||
img.style.height = '30px';
|
||||
});
|
||||
});
|
||||
|
||||
// 等待所有 PDF 渲染完成
|
||||
console.log('等待 PDF 渲染完成...');
|
||||
await page.evaluate(async () => {
|
||||
return new Promise((resolve) => {
|
||||
// 获取所有需要转换的图片容器
|
||||
const containers = document.querySelectorAll('.ultrasound-image');
|
||||
if (containers.length === 0) {
|
||||
console.log('没有找到需要渲染的图片容器');
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
let completedCount = 0;
|
||||
const totalCount = containers.length;
|
||||
|
||||
// 为每个容器执行渲染
|
||||
containers.forEach(async (container) => {
|
||||
const pdfUrl = container.getAttribute('data-pdf-url');
|
||||
if (pdfUrl) {
|
||||
try {
|
||||
await renderPDFAsImage(pdfUrl, container);
|
||||
completedCount++;
|
||||
console.log(`PDF渲染进度: ${completedCount}/${totalCount}`);
|
||||
|
||||
if (completedCount === totalCount) {
|
||||
console.log('所有 PDF 渲染完成');
|
||||
resolve();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('PDF 渲染失败:', error);
|
||||
completedCount++;
|
||||
if (completedCount === totalCount) {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
completedCount++;
|
||||
if (completedCount === totalCount) {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 设置超时保护
|
||||
setTimeout(() => {
|
||||
if (completedCount < totalCount) {
|
||||
console.log('PDF 渲染超时,继续执行');
|
||||
resolve();
|
||||
}
|
||||
}, 60000);
|
||||
});
|
||||
});
|
||||
|
||||
// 额外等待一段时间确保渲染完全完成
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
// 添加额外的检查,确保所有加载提示都被隐藏
|
||||
await page.evaluate(() => {
|
||||
document.querySelectorAll('.pdf-container').forEach(container => {
|
||||
const loadingElem = container.querySelector('.pdf-loading');
|
||||
const imageContainer = container.querySelector('.pdf-image-container');
|
||||
// 只要图片容器存在,就隐藏加载提示
|
||||
if (imageContainer && loadingElem) {
|
||||
loadingElem.style.display = 'none';
|
||||
imageContainer.style.display = 'block'; // 确保图片容器显示
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 再次等待一段时间,确保所有更改都已应用
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// 处理汇总内容分页
|
||||
if (data.summaryResult) {
|
||||
await new Promise(resolve => {
|
||||
page.evaluate((summaryResult) => {
|
||||
if (typeof handleSummaryPagination === 'function') {
|
||||
handleSummaryPagination(summaryResult);
|
||||
|
||||
// 检查分页结果
|
||||
const summaryPage2 = document.getElementById('summary-page-2');
|
||||
if (summaryPage2) {
|
||||
const content = document.getElementById('summary-content-2');
|
||||
if (content && content.textContent.trim() !== '') {
|
||||
summaryPage2.style.display = 'block';
|
||||
}
|
||||
}
|
||||
}
|
||||
}, data.summaryResult);
|
||||
|
||||
// 等待分页处理完成
|
||||
setTimeout(resolve, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
// 确保输出目录存在
|
||||
await fs.ensureDir(this.options.outputDir);
|
||||
|
||||
// 生成PDF文件名 - 修改为"体检编号-姓名"格式
|
||||
const fileName = `${data.medicalSn || 'unknown'}-${data.pName || 'noname'}.pdf`;
|
||||
const pdfPath = path.join(this.options.outputDir, fileName);
|
||||
|
||||
console.log('生成PDF...');
|
||||
const pdf = await page.pdf({
|
||||
path: pdfPath,
|
||||
format: 'A4',
|
||||
printBackground: true,
|
||||
margin: {
|
||||
top: '0',
|
||||
right: '0',
|
||||
bottom: '0',
|
||||
left: '0'
|
||||
},
|
||||
preferCSSPageSize: true
|
||||
});
|
||||
|
||||
// 在PDF生成完成后保存HTML用于调试
|
||||
try {
|
||||
const debugHtmlPath = path.join(__dirname, '../logs/last-report.html');
|
||||
fs.writeFileSync(debugHtmlPath, htmlContent);
|
||||
} catch (error) {
|
||||
logToFile(`保存调试HTML出错: ${error.message}`);
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
fileName,
|
||||
path: pdfPath
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error('PDF生成失败:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async updateTemplateContent(html, data) {
|
||||
// 添加数据结构调试
|
||||
console.log('Received data:', JSON.stringify(data, null, 2));
|
||||
|
||||
// 替换图片相对路径为base64编码
|
||||
try {
|
||||
const templateDir = path.dirname(this.options.templatePath);
|
||||
const imagePath = path.join(templateDir, '首页标签.png');
|
||||
console.log('图片路径:', imagePath);
|
||||
|
||||
// 读取图片文件并转换为Base64
|
||||
const imageBuffer = await fs.readFile(imagePath);
|
||||
const base64Image = `data:image/png;base64,${imageBuffer.toString('base64')}`;
|
||||
console.log('图片已转换为base64格式');
|
||||
|
||||
// 替换所有图片引用
|
||||
html = html.replace(/<img\s+src="首页标签.png"/g, `<img src="${base64Image}"`);
|
||||
} catch (error) {
|
||||
console.error('处理图片时出错:', error);
|
||||
}
|
||||
|
||||
// 计算年龄
|
||||
let age = '--';
|
||||
if (data.birthday) {
|
||||
const birthDate = new Date(data.birthday);
|
||||
const today = new Date();
|
||||
let calculatedAge = today.getFullYear() - birthDate.getFullYear();
|
||||
const m = today.getMonth() - birthDate.getMonth();
|
||||
if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) {
|
||||
calculatedAge--;
|
||||
}
|
||||
age = calculatedAge.toString();
|
||||
}
|
||||
|
||||
// 更新所有报告标题中的填充值
|
||||
const reportTitleRegex = /<div class="report-title">\s*<div[^>]*>\s*<span[^>]*>.*?<\/span>\s*<span[^>]*>填充值<\/span>\s*<\/div>\s*<\/div>/g;
|
||||
const replacement = `<div class="report-title">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<span></span>
|
||||
<span>${data.medicalSn || '--'} ${data.pName || '--'} ${data.gender || '--'} ${age}岁</span>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
html = html.replace(reportTitleRegex, replacement);
|
||||
|
||||
// 更新体检编号
|
||||
const reportTitleRegex2 = /体检编号:.*?</;
|
||||
html = html.replace(reportTitleRegex2, `体检编号:${data.medicalSn || '--'}<`);
|
||||
|
||||
// 更新头像
|
||||
if (data.headPicUrl) {
|
||||
const avatarSrc = data.headPicUrl.startsWith('data:image') ? data.headPicUrl : `data:image/jpeg;base64,${data.headPicUrl}`;
|
||||
html = html.replace(/<img[^>]*class="avatar-image"[^>]*>/g, `<img src="${avatarSrc}" alt="头像" class="avatar-image" id="avatar-image">`);
|
||||
}
|
||||
|
||||
// 格式化体检日期
|
||||
let examDate = '--';
|
||||
if (data.medicalDateTime) {
|
||||
const date = new Date(data.medicalDateTime);
|
||||
examDate = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
// 更新个人信息
|
||||
const personalInfoMap = {
|
||||
'姓名': data.pName || '--',
|
||||
'性别': data.gender || '--',
|
||||
'年龄': age,
|
||||
'身份证号': data.cardId || '--',
|
||||
'联系电话': data.phoneNum || '--',
|
||||
'体检日期': examDate
|
||||
};
|
||||
|
||||
// 遍历并更新每个字段
|
||||
Object.entries(personalInfoMap).forEach(([title, value]) => {
|
||||
// 根据字段名称确定是否需要 letter-spacing 样式
|
||||
const needsLetterSpacing = ['身份证号', '联系电话', '体检日期'].includes(title);
|
||||
|
||||
// 构建正则表达式
|
||||
let regex;
|
||||
if (needsLetterSpacing) {
|
||||
// 对于需要 letter-spacing 的字段,匹配带有样式的标签
|
||||
regex = new RegExp(`<span class="person_title" style="letter-spacing: 4px;">${title}</span><span[^>]*class="person_content"[^>]*>[^<]*</span>`);
|
||||
} else {
|
||||
// 对于普通字段,匹配不带样式的标签
|
||||
regex = new RegExp(`<span class="person_title">${title}</span><span[^>]*class="person_content"[^>]*>[^<]*</span>`);
|
||||
}
|
||||
|
||||
const matches = html.match(regex);
|
||||
|
||||
if (matches) {
|
||||
// 构建替换内容,保持原有样式
|
||||
const replacement = needsLetterSpacing
|
||||
? `<span class="person_title" style="letter-spacing: 4px;">${title}</span><span class="person_content">${value}</span>`
|
||||
: `<span class="person_title">${title}</span><span class="person_content">${value}</span>`;
|
||||
|
||||
html = html.replace(regex, replacement);
|
||||
}
|
||||
});
|
||||
|
||||
// 更新前言中的姓名
|
||||
const prefaceNameRegex = /<span class="underline">.*?<\/span>/;
|
||||
if (data.pName) {
|
||||
html = html.replace(prefaceNameRegex, `<span class="underline">${data.pName}</span>`);
|
||||
}
|
||||
|
||||
// 更新一般检查数据
|
||||
if (data.data && Array.isArray(data.data)) {
|
||||
// 使用更精确的正则表达式来匹配表格单元格
|
||||
const examData = {
|
||||
'体温': `${data.data.find(item => item.itemName === '体温')?.itemResult || '--'}`,
|
||||
'脉率': `${data.data.find(item => item.itemName === '脉率')?.itemResult || '--'}`,
|
||||
'呼吸频率': `${data.data.find(item => item.itemName === '呼吸频率')?.itemResult || '--'}`,
|
||||
'血压': `${data.data.find(item => item.itemName === '血压')?.itemResult || '--'}`,
|
||||
'身高': `${data.data.find(item => item.itemName === '身高')?.itemResult || '--'}`,
|
||||
'体重': `${data.data.find(item => item.itemName === '体重')?.itemResult || '--'}`,
|
||||
'腰围': `${data.data.find(item => item.itemName === '腰围')?.itemResult || '--'}`,
|
||||
'体质指数': `${data.data.find(item => item.itemName === '体质指数(BMI)')?.itemResult || '--'}`
|
||||
};
|
||||
|
||||
// 添加单位
|
||||
const units = {
|
||||
'体温': '℃',
|
||||
'脉率': '次/分',
|
||||
'呼吸频率': '次/分',
|
||||
'血压': 'mmHg',
|
||||
'身高': 'cm',
|
||||
'体重': 'kg',
|
||||
'腰围': 'cm',
|
||||
'体质指数': 'kg/m²'
|
||||
};
|
||||
|
||||
// 遍历并更新每个检查项目
|
||||
Object.entries(examData).forEach(([key, value]) => {
|
||||
if (value !== '--') {
|
||||
const unit = units[key] || '';
|
||||
// 对于 BMI,使用特殊的正则表达式
|
||||
const regex = key === '体质指数'
|
||||
? new RegExp(`<td>体质指数\\(BMI\\)</td>\\s*<td[^>]*>.*?</td>`, 'g')
|
||||
: new RegExp(`<td>${key}</td>\\s*<td[^>]*>.*?</td>`, 'g');
|
||||
const replacement = key === '体质指数'
|
||||
? `<td>体质指数(BMI)</td><td>${value}${unit}</td>`
|
||||
: `<td>${key}</td><td>${value}${unit}</td>`;
|
||||
html = html.replace(regex, replacement);
|
||||
}
|
||||
});
|
||||
|
||||
// 查找身高项目数据
|
||||
const heightData = data.data.find(item => item.itemName === '身高');
|
||||
// 尝试多种可能的项目名和字段
|
||||
let summaryItem = null;
|
||||
let summaryContent = '--';
|
||||
|
||||
// 1. 先记录所有带有analyse字段的项目
|
||||
const itemsWithAnalyse = data.data.filter(item => item.analyse && item.analyse.trim() !== '--');
|
||||
|
||||
// 2. 优先使用身高项目
|
||||
summaryItem = data.data.find(item =>
|
||||
item.itemName === '身高' && item.analyse && item.analyse.trim() !== '--'
|
||||
);
|
||||
|
||||
// 3. 如果没找到,尝试一般检查
|
||||
if (!summaryItem) {
|
||||
summaryItem = data.data.find(item =>
|
||||
(item.itemName === '一般检查' || item.itemName === '体格检查') &&
|
||||
item.analyse && item.analyse.trim() !== '--'
|
||||
);
|
||||
}
|
||||
|
||||
// 4. 如果还没找到,尝试使用第一个有analyse的项目
|
||||
if (!summaryItem && itemsWithAnalyse.length > 0) {
|
||||
summaryItem = itemsWithAnalyse[0];
|
||||
}
|
||||
|
||||
// 5. 记录找到的结果
|
||||
if (summaryItem) {
|
||||
summaryContent = summaryItem.analyse;
|
||||
} else {
|
||||
logToFile(`未找到任何带有analyse字段的项目,使用默认值"--"`);
|
||||
}
|
||||
// 使用找到的内容替换
|
||||
try {
|
||||
html = html.replace(
|
||||
/<div class="report-summary" id="general-exam-summary">[\s\S]*?<\/div>/s,
|
||||
`<div class="report-summary" id="general-exam-summary">
|
||||
<h4>一般检查小结:</h4>
|
||||
<p id="general-summary-content">${summaryContent}</p>
|
||||
</div>`
|
||||
);
|
||||
} catch (error) {
|
||||
logToFile(`替换出错: ${error.message}`);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 更新体质辨识结果
|
||||
html = html.replace(
|
||||
/(<td width="50%" class="constitution-value" style="text-align: center;">).*?(<\/td>)/s,
|
||||
`$1${data.zybs || '--'}$2`
|
||||
);
|
||||
|
||||
// 更新汇总结果
|
||||
if (data.summaryResult) {
|
||||
// 先更新第一页内容
|
||||
html = html.replace(
|
||||
/(<p id="summary-content-1"[^>]*class="summary-content"[^>]*>).*?(<\/p>)/s,
|
||||
`$1${data.summaryResult}$2`
|
||||
);
|
||||
|
||||
// 更新主检医生签名
|
||||
if (data.sign) {
|
||||
// 更新第一页签名
|
||||
const summaryDoctorImageRegex = /(<img[^>]*id="summary-doctor-image"[^>]*>)/;
|
||||
if (html.match(summaryDoctorImageRegex)) {
|
||||
html = html.replace(summaryDoctorImageRegex, `<img id="summary-doctor-image" src="${data.sign}" alt="主检医生签名">`);
|
||||
}
|
||||
|
||||
// 更新第二页签名
|
||||
const summaryDoctorImage2Regex = /(<img[^>]*id="summary-doctor-image2"[^>]*>)/;
|
||||
if (html.match(summaryDoctorImage2Regex)) {
|
||||
html = html.replace(summaryDoctorImage2Regex, `<img id="summary-doctor-image2" src="${data.sign}" alt="主检医生签名">`);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加分页处理脚本到 HTML 中
|
||||
const paginationScript = `
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
if (typeof handleSummaryPagination === 'function') {
|
||||
handleSummaryPagination('${data.summaryResult.replace(/'/g, "\\'")}');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
`;
|
||||
html = html.replace('</body>', `${paginationScript}</body>`);
|
||||
}
|
||||
|
||||
// 更新体质结果详情
|
||||
if (data.features) {
|
||||
html = html.replace(
|
||||
/(<div id="constitution-detail-container".*?>).*?(<\/div>)/s,
|
||||
`$1${data.features.replace(/\\r\\n/g, '<br>')}$2`
|
||||
);
|
||||
}
|
||||
|
||||
// 更新图片和 PDF 链接
|
||||
if (data.data && Array.isArray(data.data)) {
|
||||
data.data.forEach((item) => {
|
||||
if (item.data) {
|
||||
let contentId = '';
|
||||
let summaryId = '';
|
||||
let pdfContainerId = '';
|
||||
switch (item.pacsDataType) {
|
||||
case 'cbc':
|
||||
contentId = 'blood-exam-content';
|
||||
summaryId = 'blood-summary-div';
|
||||
pdfContainerId = 'blood-exam-pdf-container';
|
||||
break;
|
||||
case 'ecg':
|
||||
contentId = 'ecg-exam-content';
|
||||
summaryId = 'ecg-summary';
|
||||
break;
|
||||
case 'bt':
|
||||
contentId = 'biochemistry-exam-content';
|
||||
summaryId = 'biochemistry-summary-div';
|
||||
pdfContainerId = 'biochemistry-exam-pdf-container';
|
||||
break;
|
||||
case 'US':
|
||||
contentId = 'ultrasound-exam-content';
|
||||
summaryId = 'ultrasound-summary';
|
||||
break;
|
||||
case 'rt':
|
||||
contentId = 'urine-exam-content';
|
||||
summaryId = 'urine-summary-div';
|
||||
pdfContainerId = 'urine-exam-pdf-container';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (contentId) {
|
||||
console.log(`处理报告类型: ${item.pacsDataType}, 内容ID: ${contentId}`);
|
||||
|
||||
// 构建正则表达式来匹配特定ID的内容区域
|
||||
const contentRegex = new RegExp(
|
||||
`(<div class="report-content" id="${contentId}">)(.*?)(</div>)`, 's'
|
||||
);
|
||||
|
||||
// 为PDF URL添加参数
|
||||
const pdfUrl = item.data + '#toolbar=0&navpanes=0&view=Fit';
|
||||
console.log(`PDF URL: ${pdfUrl}`);
|
||||
|
||||
// 准备替换内容
|
||||
let replacement;
|
||||
if (item.pacsDataType === 'ecg') {
|
||||
// 心电图使用img标签
|
||||
replacement = `$1<img src="${item.data}" alt="心电图检查">$3`;
|
||||
|
||||
// 更新心电图所见所得
|
||||
if (item.analyse) {
|
||||
console.log(`处理心电图报告,ID: ${summaryId}`);
|
||||
console.log('原始analyse:', item.analyse);
|
||||
|
||||
// 对于心电图,使用 analyse 按行分割
|
||||
const analyseLines = item.analyse.split('\n');
|
||||
const findings = analyseLines[0]?.replace('检查所见:', '') || '--';
|
||||
const conclusion = analyseLines[1]?.replace('检查结果:', '') || '--';
|
||||
|
||||
console.log('处理后的所见:', findings);
|
||||
console.log('处理后的所得:', conclusion);
|
||||
|
||||
// 更新所见所得
|
||||
const findingsRegex = new RegExp(
|
||||
`(<div[^>]*id="ecg-summary"[^>]*>).*?(</div>)`, 's'
|
||||
);
|
||||
const findingsMatch = html.match(findingsRegex);
|
||||
console.log('所见正则匹配结果:', findingsMatch ? '成功' : '失败');
|
||||
html = html.replace(findingsRegex, `$1<p><strong>【所见】</strong><br>${findings}</p><p><strong>【所得】</strong><br>${conclusion}</p>$2`);
|
||||
}
|
||||
} else if (item.pacsDataType === 'US') {
|
||||
// 超声检查报告处理 - 设置 PDF URL
|
||||
replacement = `$1<img src="" alt="超声检查报告" class="ultrasound-image" data-pdf-url="${pdfUrl}">$3`;
|
||||
|
||||
// 更新超声所见所得
|
||||
if (item.analyse) {
|
||||
// 使用与心电图报告相同的处理逻辑
|
||||
console.log(`处理超声报告,ID: ${summaryId}`);
|
||||
|
||||
// 记录分析内容
|
||||
const logMessage = `超声报告分析内容: ${item.analyse}`;
|
||||
console.log(logMessage);
|
||||
// 同时写入文件日志
|
||||
// fs.appendFileSync(path.join(__dirname, '../logs/debug.log'),
|
||||
//`${new Date().toISOString()} - ${logMessage}\n`);
|
||||
|
||||
// 处理分析结果
|
||||
const analyseLines = item.analyse.split('\n');
|
||||
const findings = analyseLines[0]?.replace('检查所见:', '') || '--';
|
||||
const conclusion = analyseLines[1]?.replace('检查结果:', '') || '--';
|
||||
|
||||
// 使用更精确的正则表达式匹配模板中的结构
|
||||
const findingsRegex = new RegExp(
|
||||
`(<div class="report-summary" id="ultrasound-summary">\\s*<p>\\s*<strong>【所见】</strong>\\s*<br>)[^<]*(</p>\\s*<p>\\s*<strong>【所得】</strong>\\s*<br>)[^<]*(</p>\\s*</div>)`, 's'
|
||||
);
|
||||
const findingsMatch = html.match(findingsRegex);
|
||||
|
||||
// 记录匹配结果
|
||||
const matchResult = `超声所见所得匹配结果: ${findingsMatch ? '成功' : '失败'}`;
|
||||
console.log(matchResult);
|
||||
//fs.appendFileSync(path.join(__dirname, '../logs/debug.log'),
|
||||
//`${new Date().toISOString()} - ${matchResult}\n`);
|
||||
|
||||
// 使用更精确的替换,保持原有的HTML结构
|
||||
html = html.replace(findingsRegex, `$1${findings}$2${conclusion}$3`);
|
||||
|
||||
// 检查替换后的内容
|
||||
const afterReplacement = html.match(/<div class="report-summary" id="ultrasound-summary">[\s\S]*?<\/div>/);
|
||||
const resultMessage = `替换后的内容: ${afterReplacement ? afterReplacement[0].substring(0, 100) + '...' : '未找到'}`;
|
||||
console.log(resultMessage);
|
||||
fs.appendFileSync(path.join(__dirname, '../logs/debug.log'),
|
||||
`${new Date().toISOString()} - ${resultMessage}\n`);
|
||||
}
|
||||
} else if (item.pacsDataType === 'cbc') {
|
||||
// 血常规PDF处理
|
||||
console.log('处理血常规数据:', item);
|
||||
console.log('血常规data字段:', item.data);
|
||||
|
||||
if (!item.data) {
|
||||
console.error('血常规数据中缺少data字段');
|
||||
return;
|
||||
}
|
||||
|
||||
const pdfUrl = item.data + '#toolbar=0&navpanes=0&view=Fit';
|
||||
console.log(`血常规PDF URL: ${pdfUrl}`);
|
||||
|
||||
// 检查是否为弃检项目
|
||||
if (item.itemStatus === "2") {
|
||||
console.log('血常规项目已弃检');
|
||||
// 隐藏内容区域
|
||||
const contentRegex = new RegExp(
|
||||
`(<div[^>]*id="blood-exam-content"[^>]*>).*?(</div>)`, 's'
|
||||
);
|
||||
html = html.replace(contentRegex, `$1<div style="display:none;">$2</div>`);
|
||||
|
||||
// 更新小结区域为弃检提示
|
||||
const summaryRegex = new RegExp(
|
||||
`(<div[^>]*id="blood-summary-div"[^>]*>).*?(</div>)`, 's'
|
||||
);
|
||||
const abandonNotice = '<p class="abandon-notice" style="color:red;font-weight:bold;text-align:center;padding:20px;">该项目已弃检</p>';
|
||||
html = html.replace(summaryRegex, `$1${abandonNotice}$2`);
|
||||
} else {
|
||||
// 构建替换内容
|
||||
replacement = `$1<img src="" alt="血常规检查报告" class="blood-image" data-pdf-url="${pdfUrl}">$3`;
|
||||
|
||||
// 更新小结内容
|
||||
if (item.analyse) {
|
||||
console.log('更新血常规小结内容:', item.analyse);
|
||||
const summaryRegex = new RegExp(
|
||||
`(<div[^>]*id="blood-summary-div"[^>]*>\\s*<h4>血常规小结:</h4>\\s*<p[^>]*style="white-space: pre-line"[^>]*>).*?(</p>)`, 's'
|
||||
);
|
||||
html = html.replace(summaryRegex, `$1${item.analyse}$2`);
|
||||
}
|
||||
}
|
||||
} else if (item.pacsDataType === 'bt') {
|
||||
// 生化检查报告处理
|
||||
if (!item.data) {
|
||||
console.error('生化数据中缺少data字段');
|
||||
return;
|
||||
}
|
||||
|
||||
const pdfUrl = item.data + '#toolbar=0&navpanes=0&view=Fit';
|
||||
|
||||
// 检查是否为弃检项目
|
||||
if (item.itemStatus === "2") {
|
||||
// 隐藏内容区域
|
||||
const contentRegex = new RegExp(
|
||||
`(<div[^>]*id="biochemistry-exam-content"[^>]*>).*?(</div>)`, 's'
|
||||
);
|
||||
html = html.replace(contentRegex, `$1<div style="display:none;">$2</div>`);
|
||||
|
||||
// 更新小结区域为弃检提示
|
||||
const summaryRegex = new RegExp(
|
||||
`(<div[^>]*id="biochemistry-summary-div"[^>]*>).*?(</div>)`, 's'
|
||||
);
|
||||
const abandonNotice = '<p class="abandon-notice" style="color:red;font-weight:bold;text-align:center;padding:20px;">该项目已弃检</p>';
|
||||
html = html.replace(summaryRegex, `$1${abandonNotice}$2`);
|
||||
} else {
|
||||
// 构建替换内容
|
||||
replacement = `$1<img src="" alt="生化检查报告" class="biochemistry-image" data-pdf-url="${pdfUrl}">$3`;
|
||||
|
||||
// 更新小结内容
|
||||
if (item.analyse) {
|
||||
const summaryRegex = new RegExp(
|
||||
`(<div[^>]*id="biochemistry-summary-div"[^>]*>\\s*<h4>生化小结:</h4>\\s*<p[^>]*style="white-space: pre-line"[^>]*>).*?(</p>)`, 's'
|
||||
);
|
||||
html = html.replace(summaryRegex, `$1${item.analyse}$2`);
|
||||
}
|
||||
}
|
||||
} else if (item.pacsDataType === 'rt') {
|
||||
// 尿常规检查报告处理
|
||||
if (!item.data) {
|
||||
console.error('尿常规数据中缺少data字段');
|
||||
return;
|
||||
}
|
||||
|
||||
const pdfUrl = item.data + '#toolbar=0&navpanes=0&view=Fit';
|
||||
|
||||
// 检查是否为弃检项目
|
||||
if (item.itemStatus === "2") {
|
||||
// 隐藏内容区域
|
||||
const contentRegex = new RegExp(
|
||||
`(<div[^>]*id="urine-exam-content"[^>]*>).*?(</div>)`, 's'
|
||||
);
|
||||
html = html.replace(contentRegex, `$1<div style="display:none;">$2</div>`);
|
||||
|
||||
// 更新小结区域为弃检提示
|
||||
const summaryRegex = new RegExp(
|
||||
`(<div[^>]*id="urine-summary-div"[^>]*>).*?(</div>)`, 's'
|
||||
);
|
||||
const abandonNotice = '<p class="abandon-notice" style="color:red;font-weight:bold;text-align:center;padding:20px;">该项目已弃检</p>';
|
||||
html = html.replace(summaryRegex, `$1${abandonNotice}$2`);
|
||||
} else {
|
||||
// 构建替换内容
|
||||
replacement = `$1<img src="" alt="尿常规检查报告" class="urine-image" data-pdf-url="${pdfUrl}">$3`;
|
||||
|
||||
// 更新小结内容
|
||||
if (item.analyse) {
|
||||
const summaryRegex = new RegExp(
|
||||
`(<div[^>]*id="urine-summary-div"[^>]*>\\s*<h4>尿常规小结:</h4>\\s*<p[^>]*style="white-space: pre-line"[^>]*>).*?(</p>)`, 's'
|
||||
);
|
||||
html = html.replace(summaryRegex, `$1${item.analyse}$2`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 执行替换
|
||||
const match = html.match(contentRegex);
|
||||
if (match) {
|
||||
console.log(`成功匹配内容区域: ${contentId}`);
|
||||
html = html.replace(contentRegex, replacement);
|
||||
} else {
|
||||
console.log(`未找到内容区域: ${contentId}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
console.log('一般检查数据:',
|
||||
JSON.stringify(data.data.find(item => item.itemName === '一般检查'), null, 2)
|
||||
);
|
||||
|
||||
console.log('替换后的小结内容:',
|
||||
html.match(/<div class="report-summary" id="general-exam-summary">([\s\S]*?)<\/div>/)[0]
|
||||
);
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
replaceExamContent(html, examType, content) {
|
||||
const examIds = {
|
||||
'超声': 'ultrasound',
|
||||
'尿常规': 'urine',
|
||||
'生化': 'biochemistry',
|
||||
'血常规': 'blood',
|
||||
'心电图': 'ecg'
|
||||
};
|
||||
|
||||
const id = examIds[examType];
|
||||
if (id) {
|
||||
const regex = new RegExp(`(<div id="${id}-summary".*?>).*?(<\/div>)`, 's');
|
||||
return html.replace(regex, `$1${content}$2`);
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
updatePdfUrl(html, examType, pdfUrl) {
|
||||
const examIds = {
|
||||
'超声': 'ultrasound',
|
||||
'生化': 'biochemistry',
|
||||
'血常规': 'blood'
|
||||
};
|
||||
|
||||
const id = examIds[examType];
|
||||
if (id) {
|
||||
// 更新屏幕显示的iframe
|
||||
const iframeRegex = new RegExp(`(<div class="${id}-exam".*?<iframe.*?src=").*?(")`);
|
||||
html = html.replace(iframeRegex, `$1${pdfUrl}$2`);
|
||||
|
||||
// 更新打印用的PDF容器
|
||||
const containerRegex = new RegExp(`(<div class="${id}-exam".*?data-pdf-url=").*?(")`);
|
||||
html = html.replace(containerRegex, `$1${pdfUrl}$2`);
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
generateExamHtml(examType, examData) {
|
||||
if (examType === '超声' || examType === '心电图') {
|
||||
const [findings = '', conclusion = ''] = (examData.analyse || '').split('\n');
|
||||
return `
|
||||
<p><strong>【所见】</strong><br>${findings.replace('检查所见:', '')}</p>
|
||||
<p><strong>【所得】</strong><br>${conclusion.replace('检查结果:', '')}</p>
|
||||
`;
|
||||
} else {
|
||||
return `<p>${examData.analyse || ''}</p>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PDFGenerator;
|
Loading…
Reference in New Issue
Block a user