mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
feat: 导入 PPTX Demo 补充
This commit is contained in:
parent
05f7edf852
commit
0b6c8c04b4
@ -46,7 +46,7 @@ module.exports = {
|
|||||||
}],
|
}],
|
||||||
'default-case': 'error',
|
'default-case': 'error',
|
||||||
'consistent-this': ['error', '_this'],
|
'consistent-this': ['error', '_this'],
|
||||||
'max-depth': ['error', 6],
|
'max-depth': ['error', 8],
|
||||||
'max-lines': ['error', 800],
|
'max-lines': ['error', 800],
|
||||||
'no-multi-str': 'error',
|
'no-multi-str': 'error',
|
||||||
'space-infix-ops': 'error',
|
'space-infix-ops': 'error',
|
||||||
|
14
package-lock.json
generated
14
package-lock.json
generated
@ -24,7 +24,7 @@
|
|||||||
"nanoid": "^4.0.0",
|
"nanoid": "^4.0.0",
|
||||||
"pinia": "^2.0.32",
|
"pinia": "^2.0.32",
|
||||||
"pptxgenjs": "^3.11.0",
|
"pptxgenjs": "^3.11.0",
|
||||||
"pptxtojson": "^0.0.8",
|
"pptxtojson": "^0.0.10",
|
||||||
"prosemirror-commands": "^1.3.0",
|
"prosemirror-commands": "^1.3.0",
|
||||||
"prosemirror-dropcursor": "^1.6.0",
|
"prosemirror-dropcursor": "^1.6.0",
|
||||||
"prosemirror-gapcursor": "^1.3.1",
|
"prosemirror-gapcursor": "^1.3.1",
|
||||||
@ -11735,9 +11735,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/pptxtojson": {
|
"node_modules/pptxtojson": {
|
||||||
"version": "0.0.8",
|
"version": "0.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/pptxtojson/-/pptxtojson-0.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/pptxtojson/-/pptxtojson-0.0.10.tgz",
|
||||||
"integrity": "sha512-9RDoPhTF9Nq7xlJbzmi05lfLV4TVa7sATX5uUmzWEj5mZyQ1SymIU8e6E1/h86/4BUVB0DbM9blwjCBXjiVlpw==",
|
"integrity": "sha512-OOVbl4y5tgnGkQIlSyZ742oBRivVSBYJV7dOwbhXEcnRZvPRVGBu8KPo7PE9QP2tDwpvuXJvqFrRaG7mbnfSfA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"tinycolor2": "1.6.0",
|
"tinycolor2": "1.6.0",
|
||||||
@ -24676,9 +24676,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"pptxtojson": {
|
"pptxtojson": {
|
||||||
"version": "0.0.8",
|
"version": "0.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/pptxtojson/-/pptxtojson-0.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/pptxtojson/-/pptxtojson-0.0.10.tgz",
|
||||||
"integrity": "sha512-9RDoPhTF9Nq7xlJbzmi05lfLV4TVa7sATX5uUmzWEj5mZyQ1SymIU8e6E1/h86/4BUVB0DbM9blwjCBXjiVlpw==",
|
"integrity": "sha512-OOVbl4y5tgnGkQIlSyZ742oBRivVSBYJV7dOwbhXEcnRZvPRVGBu8KPo7PE9QP2tDwpvuXJvqFrRaG7mbnfSfA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"tinycolor2": "1.6.0",
|
"tinycolor2": "1.6.0",
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
"nanoid": "^4.0.0",
|
"nanoid": "^4.0.0",
|
||||||
"pinia": "^2.0.32",
|
"pinia": "^2.0.32",
|
||||||
"pptxgenjs": "^3.11.0",
|
"pptxgenjs": "^3.11.0",
|
||||||
"pptxtojson": "^0.0.8",
|
"pptxtojson": "^0.0.10",
|
||||||
"prosemirror-commands": "^1.3.0",
|
"prosemirror-commands": "^1.3.0",
|
||||||
"prosemirror-dropcursor": "^1.6.0",
|
"prosemirror-dropcursor": "^1.6.0",
|
||||||
"prosemirror-gapcursor": "^1.3.1",
|
"prosemirror-gapcursor": "^1.3.1",
|
||||||
|
@ -52,6 +52,7 @@
|
|||||||
// select
|
// select
|
||||||
.ant-select {
|
.ant-select {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.ant-select-item-option-active:not(.ant-select-item-option-disabled) {
|
.ant-select-item-option-active:not(.ant-select-item-option-disabled) {
|
||||||
background-color: rgba($color: $themeColor, $alpha: .2);
|
background-color: rgba($color: $themeColor, $alpha: .2);
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
|
import { ref } from 'vue'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { parse } from 'pptxtojson'
|
import { parse, Shape, Element } from 'pptxtojson'
|
||||||
import { nanoid } from 'nanoid'
|
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 { useSlidesStore } from '@/store'
|
||||||
import { decrypt } from '@/utils/crypto'
|
import { decrypt } from '@/utils/crypto'
|
||||||
import { ShapePoolItem, SHAPE_LIST, SHAPE_PATH_FORMULAS } from '@/configs/shapes'
|
import { ShapePoolItem, SHAPE_LIST, SHAPE_PATH_FORMULAS } from '@/configs/shapes'
|
||||||
|
import { VIEWPORT_SIZE } from '@/configs/canvas'
|
||||||
import useAddSlidesOrElements from '@/hooks/useAddSlidesOrElements'
|
import useAddSlidesOrElements from '@/hooks/useAddSlidesOrElements'
|
||||||
|
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
@ -15,6 +17,8 @@ export default () => {
|
|||||||
|
|
||||||
const { addSlidesFromData } = useAddSlidesOrElements()
|
const { addSlidesFromData } = useAddSlidesOrElements()
|
||||||
|
|
||||||
|
const exporting = ref(false)
|
||||||
|
|
||||||
// 导入pptist文件
|
// 导入pptist文件
|
||||||
const importSpecificFile = (files: FileList, cover = false) => {
|
const importSpecificFile = (files: FileList, cover = false) => {
|
||||||
const file = files[0]
|
const file = files[0]
|
||||||
@ -33,11 +37,47 @@ export default () => {
|
|||||||
reader.readAsText(file)
|
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文件
|
// 导入PPTX文件
|
||||||
const importPPTXFile = (files: FileList) => {
|
const importPPTXFile = (files: FileList) => {
|
||||||
const file = files[0]
|
const file = files[0]
|
||||||
if (!file) return
|
if (!file) return
|
||||||
|
|
||||||
|
exporting.value = true
|
||||||
|
|
||||||
const shapeList: ShapePoolItem[] = []
|
const shapeList: ShapePoolItem[] = []
|
||||||
for (const item of SHAPE_LIST) {
|
for (const item of SHAPE_LIST) {
|
||||||
shapeList.push(...item.children)
|
shapeList.push(...item.children)
|
||||||
@ -46,6 +86,10 @@ export default () => {
|
|||||||
const reader = new FileReader()
|
const reader = new FileReader()
|
||||||
reader.onload = async e => {
|
reader.onload = async e => {
|
||||||
const json = await parse(e.target!.result as ArrayBuffer)
|
const json = await parse(e.target!.result as ArrayBuffer)
|
||||||
|
|
||||||
|
const width = json.size.width
|
||||||
|
const scale = VIEWPORT_SIZE / width
|
||||||
|
|
||||||
const slides: Slide[] = []
|
const slides: Slide[] = []
|
||||||
for (const item of json.slides) {
|
for (const item of json.slides) {
|
||||||
const { type, value } = item.fill
|
const { type, value } = item.fill
|
||||||
@ -69,7 +113,14 @@ export default () => {
|
|||||||
elements: [],
|
elements: [],
|
||||||
background,
|
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') {
|
if (el.type === 'text') {
|
||||||
slide.elements.push({
|
slide.elements.push({
|
||||||
type: 'text',
|
type: 'text',
|
||||||
@ -105,6 +156,11 @@ export default () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
else if (el.type === 'shape') {
|
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 shape = shapeList.find(item => item.pptxShapeType === el.shapType)
|
||||||
|
|
||||||
const element: PPTShapeElement = {
|
const element: PPTShapeElement = {
|
||||||
@ -116,7 +172,7 @@ export default () => {
|
|||||||
top: el.top,
|
top: el.top,
|
||||||
viewBox: [200, 200],
|
viewBox: [200, 200],
|
||||||
path: 'M 0 0 L 200 0 L 200 200 L 0 200 Z',
|
path: 'M 0 0 L 200 0 L 200 200 L 0 200 Z',
|
||||||
fill: el.fillColor,
|
fill: el.fillColor || 'none',
|
||||||
fixedRatio: false,
|
fixedRatio: false,
|
||||||
rotate: el.rotate,
|
rotate: el.rotate,
|
||||||
outline: {
|
outline: {
|
||||||
@ -151,6 +207,7 @@ export default () => {
|
|||||||
|
|
||||||
slide.elements.push(element)
|
slide.elements.push(element)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (el.type === 'table') {
|
else if (el.type === 'table') {
|
||||||
const row = el.data.length
|
const row = el.data.length
|
||||||
const col = el.data[0].length
|
const col = el.data[0].length
|
||||||
@ -203,9 +260,21 @@ export default () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
else if (el.type === 'chart') {
|
else if (el.type === 'chart') {
|
||||||
const labels = Object.values(el.data[0].xlabels)
|
let labels: string[]
|
||||||
const legends = el.data.map(item => item.key)
|
let legends: string[]
|
||||||
const series = el.data.map(item => item.values.map(v => v.y))
|
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 options: ChartOptions = {}
|
||||||
|
|
||||||
let chartType: ChartType = 'bar'
|
let chartType: ChartType = 'bar'
|
||||||
@ -250,11 +319,21 @@ export default () => {
|
|||||||
options,
|
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)
|
slides.push(slide)
|
||||||
}
|
}
|
||||||
addSlidesFromData(slides)
|
addSlidesFromData(slides)
|
||||||
|
exporting.value = false
|
||||||
}
|
}
|
||||||
reader.readAsArrayBuffer(file)
|
reader.readAsArrayBuffer(file)
|
||||||
}
|
}
|
||||||
@ -262,5 +341,6 @@ export default () => {
|
|||||||
return {
|
return {
|
||||||
importSpecificFile,
|
importSpecificFile,
|
||||||
importPPTXFile,
|
importPPTXFile,
|
||||||
|
exporting,
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -76,6 +76,8 @@
|
|||||||
>
|
>
|
||||||
<HotkeyDoc />
|
<HotkeyDoc />
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
|
||||||
|
<FullscreenSpin :loading="exporting" tip="正在导入..." />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -90,6 +92,7 @@ import useImport from '@/hooks/useImport'
|
|||||||
|
|
||||||
import HotkeyDoc from './HotkeyDoc.vue'
|
import HotkeyDoc from './HotkeyDoc.vue'
|
||||||
import FileInput from '@/components/FileInput.vue'
|
import FileInput from '@/components/FileInput.vue'
|
||||||
|
import FullscreenSpin from '@/components/FullscreenSpin.vue'
|
||||||
import {
|
import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Dropdown,
|
Dropdown,
|
||||||
@ -104,7 +107,7 @@ const { gridLineSize, showRuler, showSelectPanel } = storeToRefs(mainStore)
|
|||||||
const { enterScreening, enterScreeningFromStart } = useScreening()
|
const { enterScreening, enterScreeningFromStart } = useScreening()
|
||||||
const { createSlide, deleteSlide, resetSlides } = useSlideHandler()
|
const { createSlide, deleteSlide, resetSlides } = useSlideHandler()
|
||||||
const { redo, undo } = useHistorySnapshot()
|
const { redo, undo } = useHistorySnapshot()
|
||||||
const { importSpecificFile, importPPTXFile } = useImport()
|
const { importSpecificFile, importPPTXFile, exporting } = useImport()
|
||||||
|
|
||||||
const setDialogForExport = mainStore.setDialogForExport
|
const setDialogForExport = mainStore.setDialogForExport
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user