新增: 导出 JSON + 导入 JSON + 打印/PDF 导出(P4)
- 工具栏新增三个按钮: 导出 JSON / 导入 JSON / 打印 PDF - 导出 JSON: Blob 下载 store.exportJSON() - 导入 JSON: 文件选择 + FileReader → store.importJSON() - 打印 PDF: window.print() + @media print CSS,逐页 page-break - 打印时隐藏编辑器/HUD/工具栏,只保留幻灯片内容 - 零新依赖(PDF 走浏览器原生打印)
This commit is contained in:
46
src/App.vue
46
src/App.vue
@@ -71,6 +71,49 @@ function onSave() {
|
||||
}
|
||||
function openAi() { switchTab('ai') }
|
||||
|
||||
/* ---------- 导出/导入 ---------- */
|
||||
function onExportJson() {
|
||||
const json = store.exportJSON()
|
||||
const blob = new Blob([json], { type: 'application/json' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.download = 'u-ppt-export.json'
|
||||
a.click()
|
||||
URL.revokeObjectURL(url)
|
||||
toast('已导出 JSON')
|
||||
}
|
||||
|
||||
function onImportJson() {
|
||||
const input = document.createElement('input')
|
||||
input.type = 'file'
|
||||
input.accept = '.json,application/json'
|
||||
input.onchange = () => {
|
||||
const file = input.files?.[0]
|
||||
if (!file) return
|
||||
const reader = new FileReader()
|
||||
reader.onload = () => {
|
||||
try {
|
||||
store.importJSON(reader.result as string)
|
||||
toast('已导入')
|
||||
} catch (e: any) {
|
||||
toast('导入失败:' + (e?.message || String(e)))
|
||||
}
|
||||
}
|
||||
reader.readAsText(file)
|
||||
}
|
||||
input.click()
|
||||
}
|
||||
|
||||
function onExportPdf() {
|
||||
// 用浏览器打印 + @media print CSS 实现 PDF 导出
|
||||
// 演示模式的 slide-layer 已有 print 样式
|
||||
document.body.classList.add('printing')
|
||||
window.print()
|
||||
setTimeout(() => document.body.classList.remove('printing'), 500)
|
||||
toast('在打印对话框中选择「另存为 PDF」')
|
||||
}
|
||||
|
||||
/* ---------- 全局快捷键(编辑模式) ---------- */
|
||||
function onKey(e: KeyboardEvent) {
|
||||
if (document.body.dataset.mode === 'present') return
|
||||
@@ -119,6 +162,9 @@ onUnmounted(() => {
|
||||
@open-ai="openAi"
|
||||
@save="onSave"
|
||||
@open-templates="templateVisible = true"
|
||||
@export-json="onExportJson"
|
||||
@import-json="onImportJson"
|
||||
@export-pdf="onExportPdf"
|
||||
/>
|
||||
|
||||
<div class="editor-body">
|
||||
|
||||
@@ -13,6 +13,9 @@ const emit = defineEmits<{
|
||||
(e: 'open-ai'): void
|
||||
(e: 'save'): void
|
||||
(e: 'open-templates'): void
|
||||
(e: 'export-json'): void
|
||||
(e: 'import-json'): void
|
||||
(e: 'export-pdf'): void
|
||||
}>()
|
||||
|
||||
const themeKeys = Object.keys(themes)
|
||||
@@ -70,6 +73,9 @@ defineProps<{ disabledActions?: string[] }>()
|
||||
<span class="sep"></span>
|
||||
<button class="btn" data-action="library" title="我的演示文库" @click="action('library')">📁 文库</button>
|
||||
<button class="btn" data-action="save" title="保存到文库" :disabled="disabledActions?.includes('save')" @click="action('save')">💾 保存</button>
|
||||
<button class="btn" data-action="export-json" title="导出 JSON" @click="emit('export-json')">📤 导出</button>
|
||||
<button class="btn" data-action="import-json" title="导入 JSON" @click="emit('import-json')">📥 导入</button>
|
||||
<button class="btn" data-action="export-pdf" title="打印/导出 PDF" @click="emit('export-pdf')">🖨 PDF</button>
|
||||
<button class="btn" data-action="open-ai" title="打开 AI 助手" @click="action('open-ai')">🤖 AI</button>
|
||||
<button class="btn" data-action="reset" title="重置为内置示例" :disabled="disabledActions?.includes('reset')" @click="action('reset')">⟲ 重置</button>
|
||||
<button class="btn primary" data-action="present" title="开始演示 (F5)" :disabled="disabledActions?.includes('present')" @click="action('present')">▶ 演示</button>
|
||||
|
||||
@@ -121,3 +121,18 @@
|
||||
.present-stage .slide-inner.animated .el[data-type="list"] .li { opacity: 1 !important; animation: none !important; }
|
||||
.slide-inner.enter-next, .slide-inner.enter-prev { animation: none !important; }
|
||||
}
|
||||
|
||||
/* ===== 打印 / 导出 PDF ===== */
|
||||
@media print {
|
||||
/* 隐藏编辑器、工具栏、HUD、toast */
|
||||
.app-editor, .toolbar, .canvas-status, .present-hud, .toast, .panel-tabs, .side-panel, .thumb-bar { display: none !important; }
|
||||
body.printing .app-present { display: block !important; }
|
||||
body.printing .present-stage { position: static; }
|
||||
body.printing .present-hud { display: none !important; }
|
||||
body.printing .slide-layer { transform: none !important; }
|
||||
body.printing .slide-inner { page-break-after: always; }
|
||||
/* 编辑模式打印:隐藏画布手柄等编辑装饰 */
|
||||
.handle, .canvas-placeholder { display: none !important; }
|
||||
/* 如果不在演示模式,打印当前页 */
|
||||
body.printing:not(:has(.app-present:not(.hidden))) .canvas-stage { display: block; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user