新增: u-ppt Vue 3 在线演示工具初始版本

- 核心架构: Vue 3 + Vite + TypeScript,零运行时依赖(仅 Vue)
- 编辑器: 幻灯片增删排序、元素拖拽八向缩放、双击编辑、12 种元素类型
  (标题/正文/列表/数据/金句/图片/形状/图表/卡片/表格/代码/公式)
- 图表: 8 种 SVG 自绘(柱状/条形/折线/面积/饼图/环形/雷达/进度)
- 富文本: 结构化 segments(加粗/斜体/颜色/高亮/上下标/代码/链接)
- AI 能力: 对话编辑、生成整套、润色本页、大纲→逐页生成、一键美化、AI 配图
- 会话绑定: 每份 PPT 独立会话,切换 PPT 自动切换对话历史
- 模板系统: 7 个内置版式 + 用户自存模板
- 演示模式: 全屏播放、键盘/鼠标/滚轮导航、入场动画
- 持久化: localStorage 存 deck/文库/会话/模板/配置
- 支持多服务商: 智谱/DeepSeek/通义/Kimi/豆包/OpenAI/Anthropic/Gemini/Groq/Ollama
This commit is contained in:
lxy
2026-07-12 13:12:09 +08:00
commit cfeabf2d37
35 changed files with 7919 additions and 0 deletions
+79
View File
@@ -0,0 +1,79 @@
<!-- =====================================================================
Toolbar.vue 顶部工具栏
===================================================================== -->
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import { store } from '../../core/store'
import { themes } from '../../core/sample'
const emit = defineEmits<{
(e: 'present'): void
(e: 'open-library'): void
(e: 'open-settings'): void
(e: 'open-ai'): void
(e: 'save'): void
(e: 'open-templates'): void
}>()
const themeKeys = Object.keys(themes)
const currentTheme = ref(store.getTheme())
// store 主题变化时同步下拉
watch(() => store.theme.value, (v) => { currentTheme.value = v })
function onThemeChange() {
store.setTheme(currentTheme.value)
}
function action(a: string) {
switch (a) {
case 'add-slide': store.addSlide(store.getCurrentIndex()); break
case 'dup-slide': store.dupSlide(); break
case 'del-slide':
if (store.delSlide()) { /* ok */ }
break
case 'reset':
if (confirm('重置为内置示例?当前编辑内容将丢失(可用 Ctrl+Z 撤销)。')) {
store.reset()
}
break
case 'present': emit('present'); break
case 'library': emit('open-library'); break
case 'save': emit('save'); break
case 'open-ai': emit('open-ai'); break
case 'settings': emit('open-settings'); break
}
}
defineProps<{ disabledActions?: string[] }>()
</script>
<template>
<header class="toolbar">
<div class="brand">
<span class="logo"></span>
<span class="name">u-ppt</span>
<span class="sub">在线演示工具</span>
</div>
<div class="tools">
<button class="btn" data-action="add-slide" title="新建幻灯片" :disabled="disabledActions?.includes('add-slide')" @click="action('add-slide')"> 幻灯片</button>
<button class="btn" data-action="templates" title="从模板新建页" @click="emit('open-templates')">📋 模板</button>
<button class="btn" data-action="dup-slide" title="复制当前页" :disabled="disabledActions?.includes('dup-slide')" @click="action('dup-slide')"> 复制</button>
<button class="btn" data-action="del-slide" title="删除当前页" :disabled="disabledActions?.includes('del-slide')" @click="action('del-slide')">🗑 删除</button>
<span class="sep"></span>
<label class="theme-select">
主题
<select v-model="currentTheme" @change="onThemeChange">
<option v-for="k in themeKeys" :key="k" :value="k">{{ themes[k].name }}</option>
</select>
</label>
<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="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>
<button class="btn ghost" data-action="settings" title="AI 设置" @click="action('settings')"></button>
</div>
</header>
</template>