mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
feat: 添加图片导出
This commit is contained in:
parent
692ce11244
commit
24b00fbb20
@ -4,6 +4,7 @@ import { trim } from 'lodash'
|
|||||||
import { saveAs } from 'file-saver'
|
import { saveAs } from 'file-saver'
|
||||||
import pptxgen from 'pptxgenjs'
|
import pptxgen from 'pptxgenjs'
|
||||||
import tinycolor from 'tinycolor2'
|
import tinycolor from 'tinycolor2'
|
||||||
|
import { toPng, toJpeg } from 'html-to-image'
|
||||||
import { useSlidesStore } from '@/store'
|
import { useSlidesStore } from '@/store'
|
||||||
import { getElementRange, getLineElementPath, getTableSubThemeColor } from '@/utils/element'
|
import { getElementRange, getLineElementPath, getTableSubThemeColor } from '@/utils/element'
|
||||||
import { AST, toAST } from '@/utils/htmlParser'
|
import { AST, toAST } from '@/utils/htmlParser'
|
||||||
@ -11,10 +12,41 @@ import { SvgPoints, toPoints } from '@/utils/svgPathParser'
|
|||||||
import { svg2Base64 } from '@/utils/svg2Base64'
|
import { svg2Base64 } from '@/utils/svg2Base64'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
|
|
||||||
|
interface ExportImageConfig {
|
||||||
|
quality: number;
|
||||||
|
width: number;
|
||||||
|
fontEmbedCSS?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const { slides } = storeToRefs(useSlidesStore())
|
const { slides } = storeToRefs(useSlidesStore())
|
||||||
|
|
||||||
const exporting = ref(false)
|
const exporting = ref(false)
|
||||||
|
|
||||||
|
// 导出图片
|
||||||
|
const exportImage = (domRef: HTMLElement, format: string, quality: number, ignoreWebfont = true) => {
|
||||||
|
exporting.value = true
|
||||||
|
const toImage = format === 'png' ? toPng : toJpeg
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!domRef) return
|
||||||
|
|
||||||
|
const config: ExportImageConfig = {
|
||||||
|
quality,
|
||||||
|
width: 1600,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ignoreWebfont) config.fontEmbedCSS = ''
|
||||||
|
|
||||||
|
toImage(domRef, config).then(dataUrl => {
|
||||||
|
exporting.value = false
|
||||||
|
saveAs(dataUrl, `pptist_slides.${format}`)
|
||||||
|
}).catch(() => {
|
||||||
|
exporting.value = false
|
||||||
|
message.error('导出图片失败')
|
||||||
|
})
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
|
|
||||||
// 导出JSON文件
|
// 导出JSON文件
|
||||||
const exportJSON = () => {
|
const exportJSON = () => {
|
||||||
@ -576,6 +608,7 @@ export default () => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
exporting,
|
exporting,
|
||||||
|
exportImage,
|
||||||
exportJSON,
|
exportJSON,
|
||||||
exportPPTX,
|
exportPPTX,
|
||||||
}
|
}
|
||||||
|
169
src/views/Editor/EditorHeader/ExportImgDialog.vue
Normal file
169
src/views/Editor/EditorHeader/ExportImgDialog.vue
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
<template>
|
||||||
|
<div class="export-img-dialog">
|
||||||
|
<div class="thumbnails-view">
|
||||||
|
<div class="thumbnails" ref="imageThumbnailsRef">
|
||||||
|
<ThumbnailSlide
|
||||||
|
class="thumbnail"
|
||||||
|
v-for="slide in slides"
|
||||||
|
:key="slide.id"
|
||||||
|
:slide="slide"
|
||||||
|
:size="1600"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="configs">
|
||||||
|
<div class="row">
|
||||||
|
<div class="title">导出格式:</div>
|
||||||
|
<RadioGroup
|
||||||
|
class="config-item"
|
||||||
|
v-model:value="format"
|
||||||
|
>
|
||||||
|
<RadioButton value="jpeg">JPEG</RadioButton>
|
||||||
|
<RadioButton value="png">PNG</RadioButton>
|
||||||
|
</RadioGroup>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="title">图片质量:</div>
|
||||||
|
<Slider
|
||||||
|
class="config-item"
|
||||||
|
:min="0"
|
||||||
|
:max="1"
|
||||||
|
:step="0.1"
|
||||||
|
v-model:value="quality"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="title">忽略在线字体:</div>
|
||||||
|
<div class="config-item">
|
||||||
|
<Switch v-model:checked="ignoreWebfont" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tip">
|
||||||
|
提示:导出时默认会忽略在线字体,若您在幻灯片中使用了在线字体,且不希望导出图片中丢失相关样式,可以选择关闭【忽略在线字体】选项,但要注意,这将会导致导出用时大幅度增加。
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btns">
|
||||||
|
<Button class="btn export" type="primary" @click="expImage()">导出图片</Button>
|
||||||
|
<Button class="btn close" @click="close()">关闭</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FullscreenSpin :loading="exporting" tip="正在导出..." />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, ref } from 'vue'
|
||||||
|
import { storeToRefs } from 'pinia'
|
||||||
|
import { useSlidesStore } from '@/store'
|
||||||
|
import useExport from '@/hooks/useExport'
|
||||||
|
|
||||||
|
import ThumbnailSlide from '@/views/components/ThumbnailSlide/index.vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'export-img-dialog',
|
||||||
|
components: {
|
||||||
|
ThumbnailSlide,
|
||||||
|
},
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const { slides } = storeToRefs(useSlidesStore())
|
||||||
|
|
||||||
|
const imageThumbnailsRef = ref<HTMLElement>()
|
||||||
|
const format = ref<'jpeg' | 'png'>('jpeg')
|
||||||
|
const quality = ref(1)
|
||||||
|
const ignoreWebfont = ref(true)
|
||||||
|
|
||||||
|
const close = () => emit('close')
|
||||||
|
|
||||||
|
const { exportImage, exporting } = useExport()
|
||||||
|
|
||||||
|
const expImage = () => {
|
||||||
|
if (!imageThumbnailsRef.value) return
|
||||||
|
exportImage(imageThumbnailsRef.value, format.value, quality.value, ignoreWebfont.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
imageThumbnailsRef,
|
||||||
|
slides,
|
||||||
|
format,
|
||||||
|
quality,
|
||||||
|
ignoreWebfont,
|
||||||
|
exporting,
|
||||||
|
expImage,
|
||||||
|
close,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.export-img-dialog {
|
||||||
|
height: 500px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.thumbnails-view {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.configs {
|
||||||
|
width: 300px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
.config-item {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #aaa;
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btns {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 40px;
|
||||||
|
|
||||||
|
.export {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.close {
|
||||||
|
width: 100px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -7,6 +7,7 @@
|
|||||||
<Menu>
|
<Menu>
|
||||||
<MenuItem @click="exportJSON()">导出 JSON</MenuItem>
|
<MenuItem @click="exportJSON()">导出 JSON</MenuItem>
|
||||||
<MenuItem @click="exportPPTX()">导出 PPTX</MenuItem>
|
<MenuItem @click="exportPPTX()">导出 PPTX</MenuItem>
|
||||||
|
<MenuItem @click="exportImgDialogVisible = true">导出图片</MenuItem>
|
||||||
</Menu>
|
</Menu>
|
||||||
</template>
|
</template>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
@ -63,6 +64,17 @@
|
|||||||
<HotkeyDoc />
|
<HotkeyDoc />
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
v-model:visible="exportImgDialogVisible"
|
||||||
|
:footer="null"
|
||||||
|
centered
|
||||||
|
:closable="false"
|
||||||
|
:width="680"
|
||||||
|
destroyOnClose
|
||||||
|
>
|
||||||
|
<ExportImgDialog @close="exportImgDialogVisible = false"/>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
<FullscreenSpin :loading="exporting" tip="正在导出..." />
|
<FullscreenSpin :loading="exporting" tip="正在导出..." />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -77,11 +89,13 @@ import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
|||||||
import useExport from '@/hooks/useExport'
|
import useExport from '@/hooks/useExport'
|
||||||
|
|
||||||
import HotkeyDoc from './HotkeyDoc.vue'
|
import HotkeyDoc from './HotkeyDoc.vue'
|
||||||
|
import ExportImgDialog from './ExportImgDialog.vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'editor-header',
|
name: 'editor-header',
|
||||||
components: {
|
components: {
|
||||||
HotkeyDoc,
|
HotkeyDoc,
|
||||||
|
ExportImgDialog,
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const mainStore = useMainStore()
|
const mainStore = useMainStore()
|
||||||
@ -97,6 +111,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hotkeyDrawerVisible = ref(false)
|
const hotkeyDrawerVisible = ref(false)
|
||||||
|
const exportImgDialogVisible = ref(false)
|
||||||
|
|
||||||
const goIssues = () => {
|
const goIssues = () => {
|
||||||
window.open('https://github.com/pipipi-pikachu/PPTist/issues')
|
window.open('https://github.com/pipipi-pikachu/PPTist/issues')
|
||||||
@ -107,6 +122,7 @@ export default defineComponent({
|
|||||||
undo,
|
undo,
|
||||||
showGridLines,
|
showGridLines,
|
||||||
hotkeyDrawerVisible,
|
hotkeyDrawerVisible,
|
||||||
|
exportImgDialogVisible,
|
||||||
exporting,
|
exporting,
|
||||||
enterScreening,
|
enterScreening,
|
||||||
enterScreeningFromStart,
|
enterScreeningFromStart,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user