fix: "export as .pdf" broken

This commit is contained in:
AnotiaWang
2025-02-13 00:08:49 +08:00
parent d062977775
commit f1dd76b297
7 changed files with 196 additions and 509 deletions

View File

@ -4,12 +4,15 @@
writeFinalReport, writeFinalReport,
type WriteFinalReportParams, type WriteFinalReportParams,
} from '~/lib/deep-research' } from '~/lib/deep-research'
import jsPDF from 'jspdf'
interface CustomReportParams extends WriteFinalReportParams { interface CustomReportParams extends WriteFinalReportParams {
visitedUrls: string[] visitedUrls: string[]
} }
const { t } = useI18n() const { t, locale } = useI18n()
const toast = useToast()
const error = ref('') const error = ref('')
const loading = ref(false) const loading = ref(false)
const loadingExportPdf = ref(false) const loadingExportPdf = ref(false)
@ -20,6 +23,7 @@
const isExportButtonDisabled = computed( const isExportButtonDisabled = computed(
() => !reportContent.value || loading.value || loadingExportPdf.value, () => !reportContent.value || loading.value || loadingExportPdf.value,
) )
let pdf: jsPDF | undefined
async function generateReport(params: CustomReportParams) { async function generateReport(params: CustomReportParams) {
loading.value = true loading.value = true
@ -44,49 +48,88 @@
const element = document.getElementById('report-content') const element = document.getElementById('report-content')
if (!element) return if (!element) return
// Create a temp container
const tempContainer = document.createElement('div')
loadingExportPdf.value = true loadingExportPdf.value = true
try { try {
// Dinamically import html2pdf // 创建 PDF 实例
// @ts-ignore if (!pdf) {
const html2pdf = (await import('html2pdf.js')).default pdf = new jsPDF({
orientation: 'portrait',
tempContainer.innerHTML = element.innerHTML
tempContainer.className = element.className
// Use print-friendly styles
tempContainer.style.cssText = `
font-family: Arial, sans-serif;
color: black;
background-color: white;
padding: 20px;
`
document.body.appendChild(tempContainer)
const opt = {
margin: [10, 10],
filename: 'research-report.pdf',
image: { type: 'jpeg', quality: 0.98 },
html2canvas: {
scale: 2,
useCORS: true,
backgroundColor: '#ffffff',
},
jsPDF: {
unit: 'mm', unit: 'mm',
format: 'a4', format: 'a4',
orientation: 'portrait', })
},
} }
await html2pdf().set(opt).from(tempContainer).save() // Load Chinese font
if (locale.value === 'zh') {
try {
if (!pdf.getFontList().SourceHanSans?.length) {
toast.add({
title: t('researchReport.downloadingFonts'),
duration: 5000,
color: 'info',
})
const fontUrl = '/fonts/SourceHanSansCN-VF.ttf'
pdf.addFont(fontUrl, 'SourceHanSans', 'normal')
pdf.setFont('SourceHanSans')
}
} catch (e: any) {
toast.add({
title: t('researchReport.downloadFontFailed'),
description: e.message,
duration: 8000,
color: 'error',
})
console.warn(
'Failed to load Chinese font, fallback to default font:',
e,
)
}
}
// 设置字体大小和行高
const fontSize = 10.5
const lineHeight = 1.5
pdf.setFontSize(fontSize)
// 设置页面边距单位mm
const margin = {
top: 20,
right: 20,
bottom: 20,
left: 20,
}
// 获取纯文本内容
const content = element.innerText
// 计算可用宽度mm
const pageWidth = pdf.internal.pageSize.getWidth()
const maxWidth = pageWidth - margin.left - margin.right
// 分割文本为行
const lines = pdf.splitTextToSize(content, maxWidth)
// 计算当前位置
let y = margin.top
// 逐行添加文本
for (const line of lines) {
// 检查是否需要新页
if (y > pdf.internal.pageSize.getHeight() - margin.bottom) {
pdf.addPage()
y = margin.top
}
// 添加文本
pdf.text(line, margin.left, y)
y += fontSize * lineHeight
}
pdf.save('research-report.pdf')
} catch (error) { } catch (error) {
console.error('Export to PDF failed:', error) console.error('Export to PDF failed:', error)
} finally { } finally {
document.body.removeChild(tempContainer)
loadingExportPdf.value = false loadingExportPdf.value = false
} }
} }

View File

@ -69,6 +69,8 @@
"sources": "Sources", "sources": "Sources",
"waiting": "Waiting for report...", "waiting": "Waiting for report...",
"generating": "Generating report...", "generating": "Generating report...",
"error": "Generate report failed: {0}" "error": "Generate report failed: {0}",
"downloadingFonts": "Downloading necessary fonts, this may take some time...",
"downloadFontFailed": "Download font failed"
} }
} }

View File

@ -69,6 +69,8 @@
"sources": "来源", "sources": "来源",
"waiting": "等待报告...", "waiting": "等待报告...",
"generating": "生成报告中...", "generating": "生成报告中...",
"error": "生成报告失败:{0}" "error": "生成报告失败:{0}",
"downloadingFonts": "正在下载必要字体,可能需要较长时间...",
"downloadFontFailed": "下载字体失败"
} }
} }

View File

@ -268,7 +268,7 @@ export async function deepResearch({
}) })
try { try {
const result = await useTavily().search(searchQuery.query, { const result = await useTavily().search(searchQuery.query, {
maxResults: 5, maxResults: 1,
}) })
console.log( console.log(
`Ran ${searchQuery.query}, found ${result.results.length} contents`, `Ran ${searchQuery.query}, found ${result.results.length} contents`,

View File

@ -21,8 +21,8 @@
"@tailwindcss/typography": "^0.5.16", "@tailwindcss/typography": "^0.5.16",
"@tavily/core": "^0.0.3", "@tavily/core": "^0.0.3",
"ai": "^4.1.28", "ai": "^4.1.28",
"html2pdf.js": "^0.9.3",
"js-tiktoken": "^1.0.18", "js-tiktoken": "^1.0.18",
"jspdf": "^2.5.2",
"marked": "^15.0.7", "marked": "^15.0.7",
"nuxt": "^3.15.4", "nuxt": "^3.15.4",
"p-limit": "^6.2.0", "p-limit": "^6.2.0",

582
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

Binary file not shown.