feat(S3): content export - Markdown download and PDF print

- Backend: GET /api/export/:type/:id returns .md file download
- Backend: POST /api/export/text for direct text-to-md export
- Frontend: downloadMarkdown() utility for client-side .md download
- Frontend: printAsPdf() utility via browser print dialog
- GenerateView: export dropdown button (MD / PDF) on result card

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
renjianbo
2026-05-03 09:51:48 +08:00
parent 53d179dd00
commit 5cd7e1eb30
4 changed files with 165 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
/**
* 导出文本为 Markdown / PDF
*/
export function downloadMarkdown(content: string, title = '导出内容') {
const now = new Date().toISOString().slice(0, 19).replace(/[:T]/g, '-')
const markdown = `# ${title}\n\n> 导出时间: ${new Date().toLocaleString('zh-CN')}\n\n${content}\n`
const blob = new Blob([markdown], { type: 'text/markdown; charset=utf-8' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = `${title.replace(/\s+/g, '_')}_${now}.md`
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(url)
}
export function printAsPdf(content: string, title = '导出内容') {
const w = window.open('', '_blank', 'width=800,height=600')
if (!w) return
w.document.write(`
<!DOCTYPE html>
<html><head><meta charset="utf-8">
<title>${title}</title>
<style>
body { font-family: system-ui, sans-serif; max-width: 800px; margin: 0 auto; padding: 2rem; line-height: 1.7; }
pre { white-space: pre-wrap; background: #f5f5f5; padding: 1rem; border-radius: 6px; }
</style></head>
<body><h1>${title}</h1><p><em>导出时间: ${new Date().toLocaleString('zh-CN')}</em></p><pre>${escapeHtml(content)}</pre></body></html>
`)
w.document.close()
setTimeout(() => w.print(), 300)
}
function escapeHtml(text: string) {
return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
}

View File

@@ -4,6 +4,8 @@ import { useRouter } from 'vue-router'
import { fetchGenerateMeta, fetchTemplatesByCategory, generatePrompt, continuePrompt } from '@/api/modules/prompt'
import { quickAddFavorite } from '@/api/modules/favorite'
import type { PromptTemplateItem } from '@/api/types/template'
import { ArrowDownBold } from '@element-plus/icons-vue'
import { downloadMarkdown, printAsPdf } from '@/utils/export'
const metaLoading = ref(true)
const templatesLoading = ref(false)
@@ -279,6 +281,19 @@ function resetConversation() {
ElMessage.info('已开始新对话')
}
function exportMarkdown() {
if (!result.value) return
const tpl = selectedTemplate.value
downloadMarkdown(result.value.generated_text, tpl?.name || '提示词生成结果')
ElMessage.success('Markdown 已下载')
}
function exportPdf() {
if (!result.value) return
const tpl = selectedTemplate.value
printAsPdf(result.value.generated_text, tpl?.name || '提示词生成结果')
}
async function addToFavorites() {
if (!result.value || selectedTemplateId.value == null) return
const tpl = selectedTemplate.value
@@ -439,6 +454,17 @@ onMounted(async () => {
加入收藏
</el-button>
<el-button type="success" plain size="small" @click="copyResult">复制</el-button>
<el-dropdown trigger="click" @command="(cmd: string) => cmd === 'md' ? exportMarkdown() : exportPdf()">
<el-button type="info" plain size="small">
导出 <el-icon class="el-icon--right"><ArrowDownBold /></el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="md">Markdown (.md)</el-dropdown-item>
<el-dropdown-item command="pdf">PDF (打印)</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-space>
</div>
</template>