feat: 导入 PPTX Demo 补充

This commit is contained in:
pipipi-pikachu 2023-03-28 21:59:34 +08:00
parent 05f7edf852
commit 0b6c8c04b4
6 changed files with 271 additions and 187 deletions

View File

@ -46,7 +46,7 @@ module.exports = {
}],
'default-case': 'error',
'consistent-this': ['error', '_this'],
'max-depth': ['error', 6],
'max-depth': ['error', 8],
'max-lines': ['error', 800],
'no-multi-str': 'error',
'space-infix-ops': 'error',

14
package-lock.json generated
View File

@ -24,7 +24,7 @@
"nanoid": "^4.0.0",
"pinia": "^2.0.32",
"pptxgenjs": "^3.11.0",
"pptxtojson": "^0.0.8",
"pptxtojson": "^0.0.10",
"prosemirror-commands": "^1.3.0",
"prosemirror-dropcursor": "^1.6.0",
"prosemirror-gapcursor": "^1.3.1",
@ -11735,9 +11735,9 @@
}
},
"node_modules/pptxtojson": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/pptxtojson/-/pptxtojson-0.0.8.tgz",
"integrity": "sha512-9RDoPhTF9Nq7xlJbzmi05lfLV4TVa7sATX5uUmzWEj5mZyQ1SymIU8e6E1/h86/4BUVB0DbM9blwjCBXjiVlpw==",
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/pptxtojson/-/pptxtojson-0.0.10.tgz",
"integrity": "sha512-OOVbl4y5tgnGkQIlSyZ742oBRivVSBYJV7dOwbhXEcnRZvPRVGBu8KPo7PE9QP2tDwpvuXJvqFrRaG7mbnfSfA==",
"dependencies": {
"jszip": "^3.10.1",
"tinycolor2": "1.6.0",
@ -24676,9 +24676,9 @@
}
},
"pptxtojson": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/pptxtojson/-/pptxtojson-0.0.8.tgz",
"integrity": "sha512-9RDoPhTF9Nq7xlJbzmi05lfLV4TVa7sATX5uUmzWEj5mZyQ1SymIU8e6E1/h86/4BUVB0DbM9blwjCBXjiVlpw==",
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/pptxtojson/-/pptxtojson-0.0.10.tgz",
"integrity": "sha512-OOVbl4y5tgnGkQIlSyZ742oBRivVSBYJV7dOwbhXEcnRZvPRVGBu8KPo7PE9QP2tDwpvuXJvqFrRaG7mbnfSfA==",
"requires": {
"jszip": "^3.10.1",
"tinycolor2": "1.6.0",

View File

@ -25,7 +25,7 @@
"nanoid": "^4.0.0",
"pinia": "^2.0.32",
"pptxgenjs": "^3.11.0",
"pptxtojson": "^0.0.8",
"pptxtojson": "^0.0.10",
"prosemirror-commands": "^1.3.0",
"prosemirror-dropcursor": "^1.6.0",
"prosemirror-gapcursor": "^1.3.1",

View File

@ -52,6 +52,7 @@
// select
.ant-select {
user-select: none;
overflow: hidden;
}
.ant-select-item-option-active:not(.ant-select-item-option-disabled) {
background-color: rgba($color: $themeColor, $alpha: .2);

View File

@ -1,10 +1,12 @@
import { ref } from 'vue'
import { storeToRefs } from 'pinia'
import { parse } from 'pptxtojson'
import { parse, Shape, Element } from 'pptxtojson'
import { nanoid } from 'nanoid'
import { Slide, TableCellStyle, TableCell, ChartType, ChartOptions, SlideBackground, PPTShapeElement } from '@/types/slides'
import { Slide, TableCellStyle, TableCell, ChartType, ChartOptions, SlideBackground, PPTShapeElement, PPTLineElement } from '@/types/slides'
import { useSlidesStore } from '@/store'
import { decrypt } from '@/utils/crypto'
import { ShapePoolItem, SHAPE_LIST, SHAPE_PATH_FORMULAS } from '@/configs/shapes'
import { VIEWPORT_SIZE } from '@/configs/canvas'
import useAddSlidesOrElements from '@/hooks/useAddSlidesOrElements'
import { message } from 'ant-design-vue'
@ -15,6 +17,8 @@ export default () => {
const { addSlidesFromData } = useAddSlidesOrElements()
const exporting = ref(false)
// 导入pptist文件
const importSpecificFile = (files: FileList, cover = false) => {
const file = files[0]
@ -33,11 +37,47 @@ export default () => {
reader.readAsText(file)
}
const parseLineElement = (el: Shape): PPTLineElement => {
let start: [number, number] = [0, 0]
let end: [number, number] = [0, 0]
if (!el.isFlipV && !el.isFlipH) { // 右下
start = [0, 0]
end = [el.width, el.height]
}
else if (el.isFlipV && el.isFlipH) { // 左上
start = [el.width, el.height]
end = [0, 0]
}
else if (el.isFlipV && !el.isFlipH) { // 右上
start = [0, el.height]
end = [el.width, 0]
}
else { // 左下
start = [el.width, 0]
end = [0, el.height]
}
return {
type: 'line',
id: nanoid(10),
width: el.borderWidth || 1,
left: el.left,
top: el.top,
start,
end,
style: el.borderType,
color: el.borderColor,
points: ['', el.shapType === 'straightConnector1' ? 'arrow' : '']
}
}
// 导入PPTX文件
const importPPTXFile = (files: FileList) => {
const file = files[0]
if (!file) return
exporting.value = true
const shapeList: ShapePoolItem[] = []
for (const item of SHAPE_LIST) {
shapeList.push(...item.children)
@ -46,6 +86,10 @@ export default () => {
const reader = new FileReader()
reader.onload = async e => {
const json = await parse(e.target!.result as ArrayBuffer)
const width = json.size.width
const scale = VIEWPORT_SIZE / width
const slides: Slide[] = []
for (const item of json.slides) {
const { type, value } = item.fill
@ -69,7 +113,14 @@ export default () => {
elements: [],
background,
}
for (const el of item.elements) {
const parseElements = (elements: Element[]) => {
for (const el of elements) {
el.width = el.width * scale
el.height = el.height * scale
el.left = el.left * scale
el.top = el.top * scale
if (el.type === 'text') {
slide.elements.push({
type: 'text',
@ -105,6 +156,11 @@ export default () => {
})
}
else if (el.type === 'shape') {
if (el.shapType === 'line' || el.shapType === 'straightConnector1') {
const lineElement = parseLineElement(el)
slide.elements.push(lineElement)
}
else {
const shape = shapeList.find(item => item.pptxShapeType === el.shapType)
const element: PPTShapeElement = {
@ -116,7 +172,7 @@ export default () => {
top: el.top,
viewBox: [200, 200],
path: 'M 0 0 L 200 0 L 200 200 L 0 200 Z',
fill: el.fillColor,
fill: el.fillColor || 'none',
fixedRatio: false,
rotate: el.rotate,
outline: {
@ -151,6 +207,7 @@ export default () => {
slide.elements.push(element)
}
}
else if (el.type === 'table') {
const row = el.data.length
const col = el.data[0].length
@ -203,9 +260,21 @@ export default () => {
})
}
else if (el.type === 'chart') {
const labels = Object.values(el.data[0].xlabels)
const legends = el.data.map(item => item.key)
const series = el.data.map(item => item.values.map(v => v.y))
let labels: string[]
let legends: string[]
let series: number[][]
if (el.chartType === 'scatterChart') {
labels = el.data[0].map(item => item + '')
legends = ['系列1']
series = [el.data[1]]
}
else {
labels = Object.values(el.data[0].xlabels)
legends = el.data.map(item => item.key)
series = el.data.map(item => item.values.map(v => v.y))
}
let options: ChartOptions = {}
let chartType: ChartType = 'bar'
@ -250,11 +319,21 @@ export default () => {
options,
})
}
// else if (el.type === 'group') {}
else if (el.type === 'group') {
const elements = el.elements.map(_el => ({
..._el,
left: _el.left + el.left,
top: _el.top + el.top,
}))
parseElements(elements)
}
}
}
parseElements(item.elements)
slides.push(slide)
}
addSlidesFromData(slides)
exporting.value = false
}
reader.readAsArrayBuffer(file)
}
@ -262,5 +341,6 @@ export default () => {
return {
importSpecificFile,
importPPTXFile,
exporting,
}
}

View File

@ -76,6 +76,8 @@
>
<HotkeyDoc />
</Drawer>
<FullscreenSpin :loading="exporting" tip="正在导入..." />
</div>
</template>
@ -90,6 +92,7 @@ import useImport from '@/hooks/useImport'
import HotkeyDoc from './HotkeyDoc.vue'
import FileInput from '@/components/FileInput.vue'
import FullscreenSpin from '@/components/FullscreenSpin.vue'
import {
Tooltip,
Dropdown,
@ -104,7 +107,7 @@ const { gridLineSize, showRuler, showSelectPanel } = storeToRefs(mainStore)
const { enterScreening, enterScreeningFromStart } = useScreening()
const { createSlide, deleteSlide, resetSlides } = useSlideHandler()
const { redo, undo } = useHistorySnapshot()
const { importSpecificFile, importPPTXFile } = useImport()
const { importSpecificFile, importPPTXFile, exporting } = useImport()
const setDialogForExport = mainStore.setDialogForExport