jvs-ppt-ui
v0.0.34
Published
#### 方式 A:npm 安装(发布到 npm 或公司私有库后) ```bash npm install jvs-ppt-ui # 或者 pnpm add jvs-ppt-ui ```
Readme
1. 安装方式
方式 A:npm 安装(发布到 npm 或公司私有库后)
npm install jvs-ppt-ui
# 或者
pnpm add jvs-ppt-ui方式 B:Monorepo / 本地直接引用
在同一个 workspace 或通过 npm link 引用本地构建好的 dist/lib 产物。
2. 全局注册(Plugin 模式)
在宿主应用的 main.ts 中引入并注册:
import { createApp } from 'vue'
import App from './App.vue'
// 引入 JVS-PPT 插件及其独立样式(已自包含所有内置组件与样式,宿主无需安装 Element Plus)
import JvsPptUi from 'jvs-ppt-ui'
import 'jvs-ppt-ui/dist/lib/style.css'
const app = createApp(App)
app.use(JvsPptUi) // 注册全局组件: <JvsPptEditor /> 和 <PptEditor />
app.mount('#app')💡 零外部依赖与资产完全自包含(开箱即用):
jvs-ppt-ui已内置自包含所需的组件与完整样式,宿主项目无需安装、引入 Element Plus,也不受宿主项目自身 UI 库的任何限制,绝不产生依赖冲突。- 图标库 100% 自包含:组件库已将阿里巴巴 Iconfont SVG Symbol 与字体样式全量打入 JS/CSS 产物中,宿主应用的
index.html彻底不需要引入任何<script>或<link>图标文件,直接使用即可完整回显所有图标。
3. 按需引入组件
无需全局注册,可在具体页面中直接引入组件与类型:
<template>
<div class="editor-wrapper">
<PptEditor
ref="pptEditorRef"
v-model="currentDeck"
:show-header="true"
:config="{ showSaveButton: true }"
@save="onSave"
@change="onChange"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { PptEditor, type PptEditorApi, type DeckData } from 'jvs-ppt-ui'
import 'jvs-ppt-ui/dist/lib/style.css'
const pptEditorRef = ref<PptEditorApi | null>(null)
const currentDeck = ref<DeckData | null>(null)
const onSave = (deck: DeckData) => {
console.log('保存 PPT 数据:', deck)
// 调用后端 API 存储幻灯片数据
}
const onChange = (data: any) => {
console.log('内容变更:', data)
}
</script>
<style scoped>
.editor-wrapper {
width: 100vw;
height: 100vh;
}
</style>四、组件 Props 配置清单
| 属性名 | 类型 | 默认值 | 说明 |
| :--- | :--- | :--- | :--- |
| modelValue | Slide[] \| DeckData \| null | null | 幻灯片数据,支持 v-model 双向绑定 |
| readonly | boolean | false | 是否只读/预览模式(禁用工具栏与编辑篡改,彻底屏蔽双击添加、元素拖拽、选中框选,同时完整保留浏览缩放、幻灯片放映、PPT 导入与 PPT 导出能力) |
| showHeader | boolean | true | 是否展示顶部导航与工具栏 |
| showMenu | boolean | true | 是否显示顶部左上角主菜单按钮(导入/导出/快捷键等弹窗菜单)。设置为 false 时,彻底隐藏主菜单按钮,左上角仅保留文档标题与重命名图标。适合弹窗嵌入或已有自定义操作栏场景 |
| toolbarMode | 'auto' \| 'more' \| 'full' | 'auto' | 工具栏布局模式:'auto' 智能自适应窄屏(宽度不足时自动折叠收纳);'more' 强制收纳扩展功能为【··· 更多】功能模块面板(核心保留撤销/恢复/文本/形状/线条,其余收纳至弹出卡片,极其适合弹窗与小屏幕场景);'full' 强制全平铺展示 |
| showSidebarLeft | boolean | true | 是否展示左侧幻灯片缩略图列表 |
| showSidebarRight | boolean | true | 是否展示右侧对象/页面属性面板 |
| enableAi | boolean | undefined | 是否启用 AI 功能(未配置或为 false 时不显示一键美化功能) |
| aiApiUrl | string | '' | AI 一键美化请求接口地址。若未配置或未开启,则不显示一键美化功能 |
| aiHeaders | Record<string, string> | {} | AI 请求接口的附加 Headers(如鉴权 Token) |
| enableMaterial | boolean | undefined | 是否启用素材库功能(未配置或为 false 时不显示素材功能) |
| materialApiUrl | string | '' | 素材库获取所有内容的 API 地址。只要配置了此地址,顶部工具栏就会自动展示【素材】按钮 |
| materialListApiUrl | string | '' | 素材库获取所有内容 API 地址的别名(与 materialApiUrl 等价) |
| materialTypeApiUrl | string | '' | 素材库获取单独类型/分类素材的 API 地址(可选,点击分类标签时触发) |
| materialSaveApiUrl | string | '' | 素材库保存当前页为素材的 API 地址(可选,配置后出现【保存当前页】按钮) |
| materialHeaders | Record<string, string> | {} | 素材库各接口请求时附加的统一 Headers(如鉴权 Token) |
| enableMcp | boolean | false | 是否监听并从 URL ?deckId= 自动拉取 MCP 稿件 |
| customUpload | CustomUploadHandler | undefined | 自定义资源上传函数:导入 PPTX/JSON、上传图片、视频、音频时自动走该函数并返回服务器 URL |
| uploadHandler | CustomUploadHandler | undefined | 自定义资源上传函数别名(与 customUpload 等价) |
| enableTemplate | boolean | true | 是否启用左侧“更多布局”中的【模板】库功能(设为 false 时隐藏“模板”Tab 与“加载本地模板”按钮,仅展示“版式”) |
| enableTemplates | boolean | true | enableTemplate 的别名 |
| templates | SlideTemplateInput[] | undefined | 自定义传入的模板列表(支持传入完整 Deck JSON 对象或按需懒加载索引项) |
| templateBaseUrl | string | '' | 模板静态资源的自定义基础路径(如 'https://cdn.example.com/ppt-templates'),用于按需拉取 templates-first-page.json 与模板 JSON |
| replaceBuiltinTemplates | boolean | true | 传入自定义 templates 时是否替换默认内置模板(设为 false 则与内置模板合并展示) |
| config | PptEditorConfig | {} | 高级配置项(详见下方) |
config 对象字段说明:
interface PptEditorConfig {
/** 演示文稿初始标题 */
title?: string
/** 是否显示左上角主菜单按钮(导入/导出/快捷键等弹窗菜单),默认为 true */
showMenu?: boolean
/** 工具栏布局模式:'auto' (自适应折叠)、'more' (收纳更多功能模式,适合弹窗/小屏)、'full' (全展开),默认为 'auto' */
toolbarMode?: 'auto' | 'more' | 'full'
/** 是否启用 AI PPT 智能生成与一键美化功能。未配置或为 false 时不显示一键美化功能 */
enableAi?: boolean
/** AI 一键美化请求接口地址。若未配置或未开启,则不显示一键美化功能 */
aiApiUrl?: string
/** AI 请求接口的额外 Headers(如鉴权 Token) */
aiHeaders?: Record<string, string>
/** 是否启用素材库功能。未配置或为 false 时不显示素材功能 */
enableMaterial?: boolean
/** 素材库获取所有内容的 API 地址(配置后自动激活并显示素材按钮) */
materialApiUrl?: string
/** 素材库获取所有内容接口地址的别名(与 materialApiUrl 等价) */
materialListApiUrl?: string
/** 素材库按类型获取单独分类素材的接口地址 */
materialTypeApiUrl?: string
/** 素材库保存当前页面为素材的接口地址 */
materialSaveApiUrl?: string
/** 素材库请求接口的额外 Headers(如鉴权 Token) */
materialHeaders?: Record<string, string>
/** 是否启用 LaTeX 公式编辑 */
enableFormula?: boolean
/** 是否启用 Unsplash 在线图库 */
enableUnsplash?: boolean
/** 自定义资源上传函数(若配载则导入文件或上传图片/音视频时自动走此函数上传并返回服务器文件地址) */
customUpload?: (fileOrBlob: File | Blob, context: UploadResourceContext) => Promise<string> | string
/** 自定义资源上传函数别名(等价于 customUpload) */
uploadHandler?: (fileOrBlob: File | Blob, context: UploadResourceContext) => Promise<string> | string
/** 是否启用左侧“更多布局”中的【模板】库功能,默认为 true(设为 false 时隐藏模板 Tab 及加载本地模板按钮) */
enableTemplate?: boolean
/** enableTemplate 的别名 */
enableTemplates?: boolean
/** 外部自定义传入的模板列表(支持传入完整 Deck JSON 对象、按需加载索引项或 SlideTemplateItem) */
templates?: SlideTemplateInput[]
/** 模板静态资源的自定义基础路径(如 'https://cdn.example.com/ppt-templates') */
templateBaseUrl?: string
/** 传入自定义 templates 时是否替换默认内置模板,默认为 true(设为 false 则与内置模板合并) */
replaceBuiltinTemplates?: boolean
}五、组件 Events 事件清单
| 事件名 | 参数 | 说明 |
| :--- | :--- | :--- |
| update:modelValue | (deck: DeckData) | 数据变化时触发,供 v-model 自动双向同步 |
| change | ({ slides, activeIndex, title }) | 幻灯片内容、顺序或数量变化时触发 |
| save | ({ format, data, deck }) | 外部调用 api.save() 时触发,附带产物与工程数据 |
| slideChange | ({ index, slide }) | 当前选中的幻灯片切换时触发 |
| export | ({ format, title, data }) | 导出 PPTX/JSON/图片完成后触发 |
| ready | (api: PptEditorApi) | 编辑器初始化挂载完成时触发,返回 API 实例 |
六、组件 Expose API(编程式接口)
通过给组件绑定 ref="editorRef",可以调用以下所有对外暴露的方法:
const editorRef = ref<PptEditorApi | null>(null)核心保存 API(获取文件或 JSON 数据)
save(options?: SaveOptions): Promise<any>保存通用核心入口。使用方可直接指定想要的格式(返回 JSON、JSON 文件、真实 PPTX 二进制文件或图片 Zip 包):// 1. 获取 JSON 格式工程数据(默认): const deck = await editorRef.value.save() // 或:await editorRef.value.save({ format: 'json' }) // 2. 获取真实的 PPTX 二进制 Blob 对象(可直接 FormData 上传 OSS / 后端存储): const pptxBlob = await editorRef.value.save({ format: 'pptx' }) const formData = new FormData() formData.append('file', pptxBlob, `${deck.title}.pptx`) await uploadToServer(formData) // 3. 获取带文件名的 PPTX File 对象: const pptxFile = await editorRef.value.save({ format: 'pptx-file' }) // 4. 获取 JSON 格式的 Blob 对象: const jsonBlob = await editorRef.value.save({ format: 'json-blob' }) // 5. 获取全部页面渲染图片的 ZIP 压缩包 Blob: const zipBlob = await editorRef.value.save({ format: 'images-zip' }) // 6. 获取内容的同时,自动触发浏览器下载: const blob = await editorRef.value.save({ format: 'pptx', autoDownload: true })getSaveData(): DeckData直接同步获取当前完整的工程 JSON 对象(含 title、slides、slideMasters、themeClrScheme 等)。getSaveJsonBlob(): Blob直接获取 JSON 格式的 Blob。getSaveJsonFile(filename?: string): File直接获取包含文件名的 JSON File 对象。getSavePptxBlob(): Promise<Blob | null>直接异步生成并返回标准 PPTX 二进制 Blob 对象。getSavePptxFile(filename?: string): Promise<File | null>直接异步生成并返回标准 PPTX File 对象。
其它工程与页面操作
loadFile(source: File | Blob | ArrayBuffer | string | any, options?: LoadSourceOptions): Promise<LoadSourceResult>【核心万能加载与渲染 API】:支持直接传入 本地 File 对象、网络下载的 Blob 二进制流、ArrayBuffer、JSON 字符串或对象、XML 字符串/Blob。插件会自动智能探测数据格式,并一键完成解析与整屏渲染! 别名:importFile同步可用。// 场景 1:用户在宿主页面上传本地 PPTX / JSON 文件 const handleFileUpload = async (e: Event) => { const file = (e.target as HTMLInputElement).files?.[0] if (file) { const res = await editorRef.value.loadFile(file) console.log(`成功加载 ${res.title},共 ${res.slidesCount} 页`) } } // 场景 2:直接加载后端接口下发的 PPTX 二进制流 Blob const res = await fetch('/api/ppt/download?id=123') const blob = await res.blob() await editorRef.value.loadFile(blob, { title: '业务汇报.pptx' }) // 场景 3:直接加载 JSON 工程字符串或 Deck 对象 await editorRef.value.loadFile(jsonString) // 场景 4:加载 XML 字符串 await editorRef.value.loadFile(xmlString, { type: 'xml' }) // 场景 5:直接传入远程或本地文件 URL 地址(自动从网络下载并渲染) await editorRef.value.loadFile('https://example.com/files/2024季度汇报.pptx') // 支持传递鉴权 Headers await editorRef.value.loadFile('/api/ppt/files/demo.pptx', { fetchOptions: { headers: { Authorization: 'Bearer token_xxx' } } })getSlides(): Slide[]获取当前所有幻灯片数组副本。getDeck(): DeckData获取当前完整工程数据。setSlides(slides: Slide[]): void直接设置/替换幻灯片列表。loadDeck(deck: DeckData): void加载完整的演示文稿工程数据。
页面导航与管理
getActiveSlide(): Slide | null获取当前选中的幻灯片数据。getActiveIndex(): number获取当前选中幻灯片的索引(从 0 开始)。selectSlide(index: number): void切换到指定索引的幻灯片。addSlide(index?: number): void在指定位置(或末尾)新增空白幻灯片。deleteSlide(index?: number): void删除指定索引(或当前选中)的幻灯片。
导出功能
exportPptx(): Promise<void>触发导出标准.pptx演示文稿文件并下载。exportJson(): void触发导出标准.json工程文件并下载。exportImages(): Promise<void>触发将全部幻灯片导出为高清晰度图片并打包为.zip下载。
历史与播放
undo(): void撤销一步操作。redo(): void重做一步操作。canUndo: Ref<boolean>当前是否可以撤销。canRedo: Ref<boolean>当前是否可以重做。startPresentation(): void进入全屏放映演示模式。stopPresentation(): void退出放映模式。
画布控制与通知
zoomIn(): void放大画布。zoomOut(): void缩小画布。resetZoom(): void重置画布缩放比例以适合当前屏幕。notify(msg: string, type?: 'success' | 'warning' | 'error' | 'info'): void弹出编辑器内部的 Toast 提示。
素材库与 AI 美化编程式配置
isMaterialEnabled: Ref<boolean>当前素材库功能是否可用/展示(只要配置了获取所有内容的接口即为true)。setMaterialConfig(config): void编程式动态更新素材库的各项 API 地址与 Headers:editorRef.value.setMaterialConfig({ listApiUrl: '/jvs-dam/api/ppt/material/list', typeApiUrl: '/jvs-dam/api/ppt/material/by-type', saveApiUrl: '/jvs-dam/api/ppt/material/save', headers: { Authorization: 'Bearer token_xxx' }, enable: true })saveMaterial(meta?: { title?: string; category?: string; slide?: Slide }): Promise<any>编程式将指定或当前幻灯片保存到素材库接口中。isAiBeautifyEnabled: Ref<boolean>当前一键美化功能是否可用/展示。setAiConfig(config): void编程式动态更新 AI 一键美化接口地址与 Token。
资源上传配置
setCustomUploadHandler(handler: CustomUploadHandler | null): void动态配载或重置自定义资源上传函数。配载后,导入文件、JSON 或手动上传图片/音视频时将自动走该函数并返回服务器 URL。getCustomUploadHandler(): CustomUploadHandler | null获取当前已配载的上传函数。
模板库编程式配置
isTemplateEnabled: Ref<boolean>当前左侧“更多布局”中的【模板】功能是否启用。setTemplateConfig(config?: { enable?: boolean; templates?: SlideTemplateInput[]; baseUrl?: string; replaceBuiltin?: boolean }): void动态更新模板库开关、自定义模板列表及静态资源基础路径。setTemplates(templates: SlideTemplateInput[], options?: { replaceBuiltin?: boolean; baseUrl?: string }): void直接替换或合并当前模板库列表。addTemplate(template: SlideTemplateInput): SlideTemplateItem动态追加单个自定义模板到模板库顶部。getTemplates(): SlideTemplateItem[]获取当前已加载的模板库列表。
底层深度控制
slidesComposable: any直接暴露内部useSlides()composable 实例,供需要进行极深度定制的开发者直接访问内部所有的元素修改、主题色、母版版式等底层状态。
七、顶部导航与工具栏插槽(Slots)
当作为插件嵌入宿主系统时,PptEditor 支持高度自由的插槽体系,既可以整体替换 Header,也可以针对工具栏的前后或右侧扩展业务按钮。
1. 插槽列表
| 插槽名 (Slot Name) | 作用域参数 (Slot Props) | 说明 |
| :--- | :--- | :--- |
| header | (context) | 完全自定义替换整个顶部栏。提供后内置 Header 不再渲染 |
| header-left-prefix | (context) | 顶部左侧最前置区域(常用作系统 Logo、返回上一级页面按钮等) |
| header-left | (context) | 顶部左侧主体(默认渲染汉堡菜单与文稿标题,提供该插槽可完全替换) |
| header-left-suffix | (context) | 顶部左侧后置区域(例如副标题、协作状态标签等) |
| toolbar / header-center | (context) | 中间工具栏主体。提供后完全替换内置的撤销/加页/图形等工具栏 |
| toolbar-prefix | (context) | 工具栏按钮组前置。在内置“撤销”按钮前插入自定义操作按钮 |
| toolbar-suffix | (context) | 工具栏按钮组后置。在内置“一键美化”按钮后插入自定义操作按钮(如企业素材库、智能组件等) |
| header-right-prefix | (context) | 顶部右侧前置区域(位于缩放比例下拉框之前) |
| header-right | (context) | 顶部右侧主体(放映按钮左侧)。最常用的业务插槽,如放置自定义“保存草稿”、“发布”、“历史版本”等 |
| header-right-suffix | (context) | 顶部右侧后置区域(位于全屏放映按钮之后,适合放置用户头像、帮助中心等) |
2. 插槽作用域参数 (context) 说明
所有插槽均可通过 v-slot="{ api, save, exportPptx, activeSlide, ... }" 解构出编辑器的上下文能力:
{
api: PptEditorApi, // 完整的编程式 API 实例
deck: DeckData, // 当前完整演示文稿数据
slides: Slide[], // 幻灯片数组
activeSlide: Slide | null, // 当前正在编辑的幻灯片
activeIndex: number, // 当前正在编辑的页码索引 (从 0 开始)
title: string, // 演示文稿标题
setTitle: (t: string) => void, // 修改标题
save: (options?: SaveOptions) => Promise<any>, // 快速保存
exportPptx: () => Promise<void>, // 快速导出 PPTX
exportJson: () => void, // 快速导出 JSON
undo: () => void, // 撤销
redo: () => void, // 重做
canUndo: boolean, // 是否可撤销
canRedo: boolean, // 是否可重做
zoomRatio: number, // 当前缩放比
resetZoom: () => void, // 重置缩放比
isMasterEditingView: boolean // 当前是否处于母版编辑模式
}3. 工具栏与右侧插槽实战范例
<template>
<PptEditor v-model="deck">
<!-- 场景 1:在左侧汉堡菜单前添加返回按钮 -->
<template #header-left-prefix>
<el-button link @click="router.back()">
<el-icon><ArrowLeft /></el-icon> 返回工作台
</el-button>
</template>
<!-- 场景 2:在顶部工具栏尾部追加企业专属工具 -->
<template #toolbar-suffix="{ api }">
<div
class="toolbar-btn-stacked"
@click="openAssetGallery(api)"
title="企业素材库"
>
<svg class="toolbar-btn-icon" viewBox="0 0 24 24">
<path d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" stroke="currentColor" fill="none" />
</svg>
<span class="btn-label">素材库</span>
</div>
</template>
<!-- 场景 3:在顶部右侧添加自定义保存与发布按钮 -->
<template #header-right="{ api, save }">
<el-button size="small" @click="handleDraftSave(save)">
保存草稿
</el-button>
<el-button type="primary" size="small" @click="handlePublish(api)">
发布
</el-button>
</template>
<!-- 场景 4:在顶部最右侧放置当前协作用户头像 -->
<template #header-right-suffix>
<el-avatar :size="28" src="https://example.com/avatar.png" />
</template>
</PptEditor>
</template>八、完整实战示例
<template>
<div class="custom-ppt-container">
<!-- 外部自定义业务工具栏 -->
<div class="external-bar">
<el-button type="primary" @click="handleExternalSave">外部保存</el-button>
<el-button @click="handleExportPptx">下载 PPTX</el-button>
<el-button @click="handleAddNewSlide">插入一页</el-button>
<el-button @click="handlePlay">全屏放映</el-button>
</div>
<!-- PPT 编辑器核心组件 -->
<div class="editor-box">
<PptEditor
ref="editorRef"
v-model="deck"
:show-header="true"
:config="{
title: '企业季度经营汇报',
showSaveButton: true
}"
@save="onSave"
@slideChange="onSlideChange"
@ready="onReady"
>
<!-- 可选:在顶部导航右侧插入自定义按钮 -->
<template #header-right>
<button class="custom-header-btn" @click="shareLink">分享</button>
</template>
</PptEditor>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { PptEditor, type PptEditorApi, type DeckData } from 'jvs-ppt-ui'
import 'jvs-ppt-ui/dist/lib/style.css'
const editorRef = ref<PptEditorApi | null>(null)
const deck = ref<DeckData | null>(null)
// 编辑器就绪
const onReady = (api: PptEditorApi) => {
console.log('编辑器已就绪,可随时调用 API:', api)
}
// 外部调用保存
const handleExternalSave = () => {
if (!editorRef.value) return
const currentDeck = editorRef.value.save()
console.log('外部获取到的保存数据:', currentDeck)
}
// 外部调用导出
const handleExportPptx = async () => {
await editorRef.value?.exportPptx()
}
// 外部插入页
const handleAddNewSlide = () => {
editorRef.value?.addSlide()
}
// 外部触发全屏放映
const handlePlay = () => {
editorRef.value?.startPresentation()
}
// 幻灯片切换
const onSlideChange = ({ index, slide }: any) => {
console.log(`当前切到第 ${index + 1} 页:`, slide)
}
// 保存回调
const onSave = (savedDeck: DeckData) => {
// 发送给服务器
// axios.post('/api/ppt/save', savedDeck)
}
const shareLink = () => {
alert('分享链接已生成!')
}
</script>
<style scoped>
.custom-ppt-container {
display: flex;
flex-direction: column;
width: 100vw;
height: 100vh;
}
.external-bar {
height: 48px;
background: #1e293b;
display: flex;
align-items: center;
padding: 0 16px;
gap: 12px;
}
.editor-box {
flex: 1;
overflow: hidden;
}
.custom-header-btn {
height: 32px;
padding: 0 12px;
background: #10b981;
color: #fff;
border: none;
border-radius: 6px;
cursor: pointer;
}
</style>八、素材库(Material Library)三层 API 架构与使用指南
jvs-ppt-ui 内置了完整的素材中心能力,支持查看全部、按单独类型拉取以及保存当前页到素材库三种 API 联动体系。
1. 按钮显示策略
💡 只要配置了获取所有内容的 API (
materialApiUrl或materialListApiUrl),顶部工具栏便会自动显示【素材】按钮。
未配置接口且未设置环境变量时,素材按钮将自动隐藏,完全不干扰原界面。
2. 核心配置项与环境变量对照
| API 功能 | 组件 Props / Config | 对应环境变量 | 说明 |
| :--- | :--- | :--- | :--- |
| 获取所有内容 | materialApiUrl 或 materialListApiUrl | VITE_MATERIAL_API_URL 或 VITE_MATERIAL_LIST_API_URL | 核心配置项,加载素材总览列表;配置后按钮自动激活 |
| 获取单独类型 | materialTypeApiUrl | VITE_MATERIAL_TYPE_API_URL | 可选项,点击分类标签(封面/目录/正文等)时调用 |
| 保存素材 | materialSaveApiUrl | VITE_MATERIAL_SAVE_API_URL | 可选项,配置后素材面板自动展示【保存当前页】按钮 |
| 统一请求头 | materialHeaders | - | 支持注入鉴权 Token 等 Headers |
3. 组件引入与配置示例
<template>
<PptEditor
ref="pptEditorRef"
:enable-material="true"
material-api-url="/jvs-dam/api/ppt/material/list"
material-type-api-url="/jvs-dam/api/ppt/material/by-type"
material-save-api-url="/jvs-dam/api/ppt/material/save"
:material-headers="{ Authorization: 'Bearer token_xxx' }"
/>
</template>4. 接口协议示例
① 获取所有内容 / 单独类型素材接口响应格式(GET)
{
"code": 0,
"msg": "success",
"data": [
{
"id": "tpl_01",
"title": "商业计划书通用封面",
"category": "封面",
"pptSource": "商务风模板",
"url": "/jvs-dam/api/ppt/material/detail/tpl_01",
"elementsCount": 8,
"originalWidth": 1280,
"originalHeight": 720
}
]
}② 保存素材接口请求格式(POST)
当用户在素材面板点击【保存当前页】并确认后,系统将自动打包当前幻灯片结构 POST 到 materialSaveApiUrl:
{
"title": "自定义正文排版素材",
"category": "正文",
"slide": {
"id": "slide_1",
"title": "核心产品架构",
"background": "#ffffff",
"elements": [ /* 幻灯片内所有图元元素数据 */ ]
},
"elementsCount": 5,
"originalSize": { "width": 1280, "height": 720 },
"createdAt": "2026-09-22T09:45:00.000Z"
}十、自定义资源上传(图片/视频/音频与文件导入)指南
jvs-ppt-ui 作为插件和 API 嵌入宿主系统时,提供了统一的自定义资源上传机制。当导入文件、导入 JSON 或用户上传媒体资源时,编辑器会智能判定是否配置了上传函数:
- 若配置了上传函数:所有本地生成的媒体资源(图片、视频、音频、背景)都会自动传入上传函数上传到服务器,编辑器最终使用上传函数返回的远程文件 URL 字符串。
- 若未配置上传函数:保持开箱即用的默认本地行为。
1. 未配置上传函数时的默认行为说明
| 场景 | 未配置上传函数时的默认行为 | 是否为 Base64 | 持久化特性 |
| :--- | :--- | :---: | :--- |
| 工具栏上传本地图片 | 使用 FileReader.readAsDataURL 读取 | 是 (Base64) | 存入 JSON 后离线持久有效 |
| 画布直接粘贴剪贴板图片 | 使用 readAsDataURL 读取图片流 | 是 (Base64) | 存入 JSON 后离线持久有效 |
| 占位符点击上传图片 | 使用 readAsDataURL 读取 | 是 (Base64) | 存入 JSON 后离线持久有效 |
| 侧边栏上传幻灯片背景图片 | 使用 readAsDataURL 读取 | 是 (Base64) | 存入 JSON 后离线持久有效 |
| 本地音视频文件选择 | 使用 readAsDataURL 读取 | 是 (Base64) | 存入 JSON 后离线持久有效 |
| 导入 PPTX 文件 | 底层解析器默认产出临时 Blob URL (blob:http://...),部分母版装饰图为 Base64 | 否 (主要是 Blob URL) | blob: URL 与当前页面会话绑定,刷新后失效,建议配置上传函数持久化为远程 URL |
💡 为什么推荐配置上传函数?
- PPTX 导入的图片默认为临时
blob:URL,刷新页面后会失效;配置上传函数后会自动持久化到对象存储/服务器。- 纯 Base64 会使保存的 JSON 体积增大 33% 左右;配置上传函数后,JSON 内仅存储几十字节的远程 URL,工程数据极度轻量。
2. 上传函数签名与上下文定义
import type { UploadResourceContext, CustomUploadHandler } from 'jvs-ppt-ui'
/**
* 上传上下文参数
*/
export interface UploadResourceContext {
/** 资源媒体类型 */
type: 'image' | 'video' | 'audio'
/** 触发来源 */
source: 'file-upload' | 'pptx-import' | 'json-import' | 'paste' | 'drop' | 'background' | 'placeholder'
/** 原始文件名(若有) */
filename?: string
/** 其它扩展字段 */
[key: string]: any
}
/**
* 自定义上传函数类型
* 接收 File 或 Blob 对象与上下文信息,同步或异步返回远程文件地址字符串
*/
export type CustomUploadHandler = (
fileOrBlob: File | Blob,
context: UploadResourceContext
) => Promise<string> | string3. 使用方式
方式 A:通过组件 Props / Config 传入
<template>
<PptEditor
v-model="deck"
:custom-upload="handleCustomUpload"
/>
<!-- 或在 config 对象中传入 -->
<!-- :config="{ customUpload: handleCustomUpload }" -->
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { PptEditor, type UploadResourceContext } from 'jvs-ppt-ui'
import 'jvs-ppt-ui/dist/lib/style.css'
const deck = ref(null)
/**
* 自定义资源上传处理器
* @param fileOrBlob 要上传的文件(File 或 Blob)
* @param context 上传来源与资源类型上下文
* @returns 最终文件的远程访问地址 URL
*/
const handleCustomUpload = async (fileOrBlob: File | Blob, context: UploadResourceContext): Promise<string> => {
const formData = new FormData()
const ext = fileOrBlob.type?.split('/')[1] || (context.type === 'video' ? 'mp4' : context.type === 'audio' ? 'mp3' : 'png')
const fileName = context.filename || `upload_${Date.now()}.${ext}`
formData.append('file', fileOrBlob, fileName)
formData.append('type', context.type)
formData.append('source', context.source)
// 调用您系统自有的文件上传 API(例如 OSS、S3、MinIO 或业务文件网关)
const response = await fetch('/api/common/upload', {
method: 'POST',
body: formData,
headers: {
Authorization: 'Bearer token_xxx'
}
}).then(res => res.json())
// 返回最终文件的远程访问 URL 字符串即可
return response.data.url
}
</script>方式 B:通过编程式 API 动态配载
若编辑器由外部动态控制,或上传逻辑依赖某些异步状态(如登录后动态获取的上传凭证),可直接调用 API 动态配载:
const editorRef = ref<PptEditorApi | null>(null)
// 动态配载上传处理器
editorRef.value.setCustomUploadHandler(async (fileOrBlob, context) => {
const url = await uploadToOss(fileOrBlob, context)
return url
})
// 获取当前配载的上传函数
const currentHandler = editorRef.value.getCustomUploadHandler()
// 卸载自定义上传处理器,恢复默认本地行为
editorRef.value.setCustomUploadHandler(null)4. 自动化去重优化策略
在导入大型 PPTX 文件时,同一张图或装饰元素往往在多页幻灯片中被多次引用。jvs-ppt-ui 内置了内存 Promise 缓存与去重机制:
- 相同的图片/媒体在单次导入中仅会触发一次真实的服务器上传请求;
- 其余引用处会自动复用首个上传请求返回的远程 URL,避免重复浪费网络流量与服务器存储空间。
十、常见接入场景与最佳实践
1. 弹窗 / 抽屉模式嵌入(小屏响应式自适应)
在将 PptEditor 嵌入到 Dialog 模态弹窗或 Drawer 抽屉时(容器宽度在 800px ~ 1200px 左右),推荐以下配置:
<template>
<el-dialog v-model="visible" fullscreen :show-close="false">
<PptEditor
ref="editorRef"
v-model="deckData"
:show-menu="false"
@save="handleSave"
>
<!-- 在右侧添加弹窗关闭或自定义业务按钮 -->
<template #header-right-suffix>
<el-button size="small" type="primary" @click="handleSave">保存</el-button>
<el-button size="small" @click="visible = false">关闭</el-button>
</template>
</PptEditor>
</el-dialog>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { PptEditor } from 'jvs-ppt-ui'
import 'jvs-ppt-ui/dist/lib/style.css'
const visible = ref(true)
const deckData = ref(null)
const editorRef = ref(null)
const handleSave = async () => {
const data = await editorRef.value?.save({ format: 'json' })
// 保存到服务端...
}
</script>小屏自适应机制说明:
- 菜单控制 (
:show-menu="false"):关闭左上角内置的测试/导入弹层菜单,左上角仅保留文档标题与重命名,更符合宿主系统定制界面的规范。 - 标题防挤压折行:标题容器内置单行截断(
white-space: nowrap; text-overflow: ellipsis)和鼠标悬浮:title提示,不再被中间工具栏挤成纵向乱序文字。 - 工具栏弹性间距:工具栏按钮间距自适应缩小(
gap: clamp(...)),宽度受限时末尾的“动画与切换”、“一键美化”等按钮依然紧凑展示;极窄尺寸下支持平滑横向滚动。 - 右侧功能区恒定展示:全屏放映、母版模式与缩放下拉框恒定单行展示(
flex-shrink: 0),绝不被压扁变形。
2. 图标库免配置说明(Zero Configuration)
- 宿主系统(如 Vue CLI / Vite 项目)引入
jvs-ppt-ui时,无需在宿主系统的index.html中手动添加任何<script>或<link>图标标签。 - 只需在入口
import 'jvs-ppt-ui/dist/lib/style.css'(或import 'jvs-ppt-ui/style.css'),组件库内部已自执行注入阿里巴巴 Iconfont SVG Symbol 与样式,实现真正的“开箱即用”。
十一、非编辑/预览模式(Readonly Mode)使用与行为规范
在许多业务场景(如课件预览、报告审阅、大屏展示、审批流中查看幻灯片)中,需要将 PPT 编辑器作为纯预览/浏览播放器使用。
通过在 <PptEditor> 上设置 :readonly="true" 即可一键启用非编辑/只读预览模式。
1. 基础用法
<template>
<PptEditor
ref="editorRef"
v-model="deckData"
:readonly="true"
:show-menu="false"
:show-sidebar-right="false"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { PptEditor } from 'jvs-ppt-ui'
import 'jvs-ppt-ui/dist/lib/style.css'
const editorRef = ref(null)
const deckData = ref(null)
</script>2. 非编辑/预览模式下的严格限制(防止意外篡改)
| 交互类型 | 只读模式下的行为表现 |
| :--- | :--- |
| 画布空白处双击 | 彻底禁用。不会创建任何新文本框或元素,画布保持原样。 |
| 图元双击编辑 | 彻底禁用。双击文本、形状、表格单元格均不会进入可编辑(ContentEditable)状态,双击公式不弹窗,双击图片占位符不弹上传框。 |
| 图元拖拽移动 | 彻底禁用。无法通过鼠标拖动改变任何文字、图形、图片、表格、组合子元素或线条控制点的位置与尺寸。 |
| 选中状态与边框 | 彻底隐藏。点击图元不再显示编辑选中边框、旋转手柄与拖动手柄(cursor: move),统一保持默认箭头光标(cursor: default)。 |
| 画布拉框框选 | 彻底禁用。在画布上拖拽鼠标不会拉出蓝色橡皮筋选区框,不会批量选中图元。 |
| 表格编辑标识 | 彻底隐藏。表格和公式上不再显示“双击编辑”浮层提示;禁用表格列宽与行高拖拽把手。 |
| 编辑工具栏 | 彻底隐藏。顶部文本、形状、线条、图片、AI美化等插入与格式工具栏自动隐藏。 |
| 幻灯片管理 | 左侧缩略图列表隐藏“新建幻灯片”按钮,并禁用幻灯片上下拖拽排序;汉堡菜单中**屏蔽“重置幻灯片”**清空操作。 |
3. 非编辑/预览模式下保留的完整功能
即使在 :readonly="true" 下,以下关键浏览、演示及导入导出能力依然完全可用:
- 幻灯片全屏放映(F5 / Shift+F5):
- 顶部常驻【幻灯片放映 ▾】按钮,支持从头放映或从当前页放映;
- 快捷键
F5(从第一页放映)、Shift + F5(从当前页放映)依然生效; - 放映模式支持键盘空格、左右方向键切页,按
Esc键退出放映。
- 导入 PPT(PPTX / JSON):
- 界面操作:顶部导航栏直接提供【导入 ▾】下拉菜单,支持选择本地
.pptx文件或.json配置文件一键导入解析并即时渲染预览; - 编程式调用:支持通过组件 API 调用
editorRef.value.loadFile(file)或editorRef.value.importFile(file)载入并呈现文件; - 导入后自动适配屏幕并继续保持只读模式。
- 界面操作:顶部导航栏直接提供【导入 ▾】下拉菜单,支持选择本地
- 导出 PPT(PPTX / JSON / ZIP / PNG / JPEG):
- 界面操作:顶部导航栏直接提供【导出 ▾】下拉菜单,支持快速下载为 PPTX、导出完整工程 JSON、打包全部页面为图片 ZIP、或下载当前页高清 PNG/JPEG 图片;
- 编程式调用:可直接通过组件 API 调用
editorRef.value.exportPptx()、editorRef.value.exportJson()、editorRef.value.exportImages()或editorRef.value.getSavePptxBlob()。
- 画布多尺度缩放与平移浏览:
- 顶部提供比例切换下拉框(50%、75%、100%、125%、150%、200% 及“适合画布”);
- 按住
Space(空格键)或鼠标中键拖动画布,可自由平移浏览大尺寸幻灯片。
- 缩略图导航与翻页:
- 点击左侧缩略图可自由跳转指定幻灯片;支持键盘
PageUp/PageDown、ArrowUp/ArrowDown快速切页。
- 点击左侧缩略图可自由跳转指定幻灯片;支持键盘
- 文字原生划词复制(Copy):
- 允许鼠标划选幻灯片内的文本内容并按
Ctrl + C(或右键复制)拷贝到剪贴板,方便审阅者摘录内容。
- 允许鼠标划选幻灯片内的文本内容并按
十二、模板库(Template Gallery)开关与自定义模板接入指南
jvs-ppt-ui 在左侧「新建幻灯片 ▾」下拉面板中内置了**【模板】与【版式】**双视图(注意:为保证 NPM 插件包轻量,npm publish 默认不会将数十 MB 的本地演示模板资源打入 NPM 包中)。宿主系统可以自由控制是否启用模板功能,并按标准格式注入自有模板。
1. 控制是否开启模板功能 (enableTemplate)
当设置 :enable-template="false" 时,左侧「更多布局」弹窗会自动隐藏 【模板】 Tab 与 【加载本地模板】 按钮,直接展示 【版式】 列表:
<template>
<!-- 方式 A:直接通过 Prop 关闭模板功能 -->
<PptEditor v-model="deck" :enable-template="false" />
<!-- 方式 B:通过 config 对象关闭 -->
<PptEditor v-model="deck" :config="{ enableTemplate: false }" />
</template>2. 传入自定义模板 (templates / templateBaseUrl)
插件使用者可以通过 :templates 传入自定义模板数组(默认替换内置模板;若希望与内置模板合并展示,可设置 :replace-builtin-templates="false"),支持以下 3 种标准格式:
格式一:直接传入完整模板 Deck 对象(适合模板数量较少或已获取完整 JSON)
<template>
<PptEditor v-model="deck" :templates="myTemplates" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { PptEditor, type SlideTemplateInput } from 'jvs-ppt-ui'
const deck = ref(null)
const myTemplates: SlideTemplateInput[] = [
{
id: 'corp-tpl-annual',
name: '企业年度述职报告', // 也可使用 title 字段
category: 'Business', // 可选:分类名称
slides: [ // 完整幻灯片页面数组(即导出 JSON 中的 slides)
{
id: 'slide-1',
background: '#0F172A',
bgType: 'color',
elements: [/* 第一页图元列表,自动作为一级列表封面渲染 */]
},
{
id: 'slide-2',
background: '#FFFFFF',
bgType: 'color',
elements: [/* 第二页图元列表 */]
}
],
styleRecipes: [/* 可选:配套主题色板 */]
}
]
</script>格式二:按需懒加载索引格式(推荐多套大图模板使用,首屏只渲染第一页,点击时才按需拉取完整 JSON)
const myLazyTemplates: SlideTemplateInput[] = [
{
id: 'tpl-product-launch',
name: '产品发布会',
category: 'Product',
slidesCount: 35, // 一级列表右下角展示的总页数
firstSlide: { /* 第一页 slide 对象,用于一级列表实时渲染封面 */ },
jsonUrl: 'https://cdn.example.com/ppt/Launch.json' // 用户点击该模板卡片时按需 fetch 的完整模板 JSON 地址
}
]格式三:配置静态模板目录地址 (templateBaseUrl)
既可以写宿主项目本地的静态目录路径(public/ 下),也可以写远程 CDN / Nginx 地址(组件会自动将模板 JSON 内的 /templates/image/...、/templates/mp4/... 替换为 templateBaseUrl 前缀):
- 写法 A:使用宿主项目本地路径(放在宿主项目的
public/目录下)- 如果直接放到宿主项目的
public/templates/目录下:甚至无需传template-base-url,默认就会请求/templates/...。 - 如果放到宿主项目的自定义目录(如
public/ppt-templates/或public/static/ppt-templates/):
<template> <!-- 对应宿主项目本地的 public/ppt-templates/ 目录 --> <PptEditor v-model="deck" template-base-url="/ppt-templates" /> </template> - 如果直接放到宿主项目的
- 写法 B:使用远程 CDN 或独立静态服务器地址
<template> <PptEditor v-model="deck" template-base-url="https://cdn.example.com/ppt-templates" /> </template>
3. 通过编程式 API 动态管理模板
通过组件 ref 实例可以在运行时随时开关模板库、替换模板列表或追加新模板:
const editorRef = ref<PptEditorApi | null>(null)
// 1. 动态替换全部模板(replaceBuiltin 设为 false 则与默认模板合并)
editorRef.value?.setTemplates(myTemplates, { replaceBuiltin: true })
// 2. 动态追加单个新模板到模板库顶部
editorRef.value?.addTemplate(newTemplateDeckJson)
// 3. 获取当前模板库中的所有模板
const currentTemplates = editorRef.value?.getTemplates()
// 4. 动态关闭或开启模板功能
editorRef.value?.setTemplateConfig({ enable: false })