jspdf+html2canvas
网页pdf
@\components\common\HeartReport.vue
<-template->
<-div class="report-container"->
<-div id="pdf-content"->
<-h1 class="title"->心脏诊疗报告<-/h1->
<-p class="content"->心脏参数:<-/p->
<-ul class="content"->
<-li->年龄: {{ docStore.heartParameters.age }}<-/li->
<-li->性别: {{ docStore.heartParameters.sex }}<-/li->
<-li->胸部疼痛类型: {{ docStore.heartParameters.cp }}<-/li->
<-li->血压: {{ docStore.heartParameters.trestbps }}<-/li->
<-li->胆固醇: {{ docStore.heartParameters.chol }}<-/li->
<-li->空腹血糖: {{ docStore.heartParameters.fbs }}<-/li->
<-li->心电图结果: {{ docStore.heartParameters.restecg }}<-/li->
<-li->最大心跳数: {{ docStore.heartParameters.thalach }}<-/li->
<-li->运动时是否有心绞痛: {{ docStore.heartParameters.exang }}<-/li->
<-li->相对于休息的 ST segment 的倾斜度: {{ docStore.heartParameters.oldpeak }}<-/li->
<-li->透视检查看到的血管数: {{ docStore.heartParameters.ca }}<-/li->
<-li->缺陷种类: {{ docStore.heartParameters.thal }}<-/li->
<-/ul->
<-h2 class="section-title"->诊疗建议:<-/h2->
<-span v-html="docStore.parsedMessage"-><-/span->
<-/div->
<-/div->
<-/template->
<-script setup->
import { ref, computed, onMounted } from 'vue';
import useDocStore from '@/stores/document.js';
import useImageStore from '@/stores/images.js';
const docStore = useDocStore();
const imageStore = useImageStore();
import { marked } from 'marked';
// 在组件挂载时调用函数
onMounted(async() =-> {
await docStore.fetchHeartParameters(imageStore.segImgId)
await docStore.fetchWenxinAdvice("注意休息早点睡觉")
});
<-/script->
<-style scoped->
@font-face {
font-family: 'FZFS';
src: url('@/assets/fonts/fzfs.ttf') format('truetype');
}
.report-container {
font-family: 'FZFS', 'Times New Roman', sans-serif;
width: 800px;
margin: auto;
padding: 20px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
border: 1px solid #ddd;
border-radius: 8px;
display: flex;
flex-direction: column;
}
.title {
font-size: 24px;
text-align: center;
margin-bottom: 20px;
}
.section-title {
font-size: 20px;
margin-top: 20px;
margin-bottom: 10px;
}
.content {
font-size: 16px;
line-height: 1.6;
}
ul {
list-style-type: none;
padding: 0;
}
ul li {
margin-bottom: 5px;
}
<-/style->
import { defineStore } from 'pinia';
import { ref } from 'vue';
import heartimg from '@/assets/heart.png';
import { heartParametersService, wenxinService } from '@/api/doc.js'; // 确保引入服务
import { downloadPDF } from '@/utils/htmlToPdf.js'; // 新增引入
import { ChatService } from '@/api/wenxin';
import { marked } from 'marked';
const useDocStore = defineStore('document', () =-> {
const heartParameters = ref({
id: 1,
age: 18,
sex: '男',
cp: 0,
trestbps: 120,
chol: 200,
fbs: 0,
restecg: 0,
thalach: 150,
exang: 0,
oldpeak: 1.0,
ca: 0,
thal: 1
});
const images = ref([heartimg]);
const suggestedAdvice = ref("根据心脏参数,建议定期检查并保持健康的生活方式。如有进一步不适,请及时就医。");
const parsedMessage = ref();
const fetchHeartParameters = async (fileId) =-> {
const Id = {
id: fileId
};
console.log(Id);
try {
const response = await heartParametersService(Id);
if (response.code === 0) {
const data = response.data;
// 为每个字段设置默认值,如果为空则使用默认值
heartParameters.value = {
id: data.id || heartParameters.value.id,
age: data.age !== null ? data.age : 18,
sex: data.sex || '男',
cp: data.cp !== null ? data.cp : 0,
trestbps: data.trestbps !== null ? data.trestbps : 120,
chol: data.chol !== null ? data.chol : 200,
fbs: data.fbs !== null ? data.fbs : 0,
restecg: data.restecg !== null ? data.restecg : 0,
thalach: data.thalach !== null ? data.thalach : 150,
exang: data.exang !== null ? data.exang : 0,
oldpeak: data.oldpeak !== null ? data.oldpeak : 1.0,
ca: data.ca !== null ? data.ca : 0,
thal: data.thal !== null ? data.thal : 1
};
console.log(heartParameters.value)
} else {
console.error('Error fetching heart parameters:', response.message);
}
} catch (error) {
console.error('Error fetching heart parameters:', error);
}
};
const fetchWenxinAdvice = async (doctorAdvice) =-> {
// 构造提问内容
const message = `
医生的建议如下:
"${doctorAdvice}"
现在,我正在处理一组心脏健康相关的数据,其中包含多个字段:
- age(年龄):${heartParameters.value.age},表示对象的年龄。
- sex(性别):${heartParameters.value.sex},表示对象的性别,取值范围为 female 和 male。
- cp(胸部疼痛类型):${heartParameters.value.cp},痛感由重到轻依次为 typical、atypical、non-anginal 和 asymptomatic。
- trestbps(血压):${heartParameters.value.trestbps},表示血压数值。
- chol(胆固醇):${heartParameters.value.chol},表示胆固醇数值。
- fbs(空腹血糖):${heartParameters.value.fbs},当血糖含量大于120mg/dl时为 true,否则为 false。
- restecg(心电图结果):${heartParameters.value.restecg},表示心电图结果,取值范围为 norm 和 hyp。
- thalach(最大心跳数):${heartParameters.value.thalach},表示最大心跳数。
- exang(运动时是否心绞痛):${heartParameters.value.exang},表示是否在运动时有心绞痛,true 为是,false 为否。
- oldpeak(运动相对于休息的ST depression):${heartParameters.value.oldpeak},表示 ST 段压数值。
- slop(心电图ST segment的倾斜度):${heartParameters.value.slop},表示 ST segment 的倾斜度,取值范围为 down、flat 和 up。
- ca(透视检查看到的血管数):${heartParameters.value.ca},表示透视检查看到的血管数。
- thal(缺陷种类):${heartParameters.value.thal},表示并发种类,取值范围为 norm、fix 和 rev。
- status(是否患病):${heartParameters.value.status},表示对象是否患病,取值范围为 buff 和 sick。
请基于以上信息,并结合医生的建议,站在医生的角度给我提供一个段落形式的建议。可以参考以下模板:
鉴于您... 我建议您...
`;
try {
// 调用 API 获取响应
const response = await ChatService({ question: message });
let advice = response.data; // 假设响应数据在 response.data 中
// 裁剪建议内容
const suggestionIndex = advice.indexOf("建议:");
if (suggestionIndex !== -1) {
advice = advice.slice(suggestionIndex + 3); // 只保留“建议:”后的内容
}
suggestedAdvice.value = advice !== null ? advice : "根据心脏参数,建议定期检查并保持健康的生活方式。如有进一步不适,请及时就医。"; // 假设响应数据在 response.data 中
console.log(suggestedAdvice.value);
parsedMessage.value = marked(suggestedAdvice.value);
} catch (error) {
console.error('API 请求失败:', error);
}
};
const addImage = (image) =-> {
images.value.push(image);
};
// 生成 PDF 的函数
const generatePDF = async () =-> {
const pdfContent = document.getElementById("pdf-content"); // 替换为你的 HTML 内容的 ID
if (!pdfContent) {
console.error("未找到要渲染的内容");
return;
}
// 调用 utils 下载 PDF 方法
await downloadPDF(pdfContent);
};
return {
heartParameters,
suggestedAdvice,
parsedMessage,
fetchHeartParameters,
fetchWenxinAdvice,
addImage,
generatePDF
};
}, { persist: true });
export default useDocStore;
import html2canvas from "html2canvas";
import { jsPDF } from "jspdf";
// 导出 PDF
export const downloadPDF = async (page) =-> {
const canvas = await html2canvas(page, {
useCORS: true, // 允许跨域请求
allowTaint: true, // 允许跨域
scale: 2, // 设置放大倍数
backgroundColor: '#ffffff' // 背景色
});
canvas2PDF(canvas);
};
// 将 canvas 转换为 PDF
const canvas2PDF = (canvas) =-> {
const PDF = new jsPDF({
orientation: 'p', // 参数: l:横向 p:纵向
unit: 'mm', // 参数:测量单位("pt","mm", "cm", "m", "in" or "px")
format: 'a4' // A4纸
});
const ctx = canvas.getContext('2d');
const a4w = 190; // A4 宽度
const a4h = 277; // A4 高度
const imgHeight = Math.floor(a4h * canvas.width / a4w); // 计算每页的高度
let renderedHeight = 0;
while (renderedHeight <- canvas.height) {
let page = document.createElement("canvas");
page.width = canvas.width;
page.height = Math.min(imgHeight, canvas.height - renderedHeight);
// 用 getImageData 剪裁指定区域
page.getContext('2d').putImageData(
ctx.getImageData(0, renderedHeight, canvas.width, Math.min(imgHeight, canvas.height - renderedHeight)),
0, 0
);
// canvas 转成图片并添加到 PDF
PDF.addImage(page.toDataURL('image/jpeg', 0.2), 'JPEG', 10, 10, a4w, Math.min(a4h, a4w * page.height / page.width));
renderedHeight += imgHeight;
// 判断是否需要分页
if (renderedHeight <- canvas.height) {
PDF.addPage();
}
}
PDF.save("心脏诊疗报告.pdf"); // 保存 PDF 文件
console.log("成功下载文件");
};

