feat: 导入PPTX文件DEMO

This commit is contained in:
pipipi-pikachu 2023-03-14 21:04:33 +08:00
parent c7c5de1de2
commit 0025a12f43
6 changed files with 394 additions and 85 deletions

99
package-lock.json generated
View File

@ -24,6 +24,7 @@
"nanoid": "^4.0.0",
"pinia": "^2.0.32",
"pptxgenjs": "^3.11.0",
"pptxtojson": "^0.0.8",
"prosemirror-commands": "^1.3.0",
"prosemirror-dropcursor": "^1.6.0",
"prosemirror-gapcursor": "^1.3.1",
@ -40,8 +41,7 @@
"svg-arc-to-cubic-bezier": "^3.2.0",
"svg-pathdata": "^6.0.0",
"tinycolor2": "^1.6.0",
"vue": "^3.2.47",
"vuedraggable": "^4.1.0"
"vue": "^3.2.47"
},
"devDependencies": {
"@commitlint/cli": "^17.4.4",
@ -11734,6 +11734,16 @@
"node": ">=14.0.0"
}
},
"node_modules/pptxtojson": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/pptxtojson/-/pptxtojson-0.0.8.tgz",
"integrity": "sha512-9RDoPhTF9Nq7xlJbzmi05lfLV4TVa7sATX5uUmzWEj5mZyQ1SymIU8e6E1/h86/4BUVB0DbM9blwjCBXjiVlpw==",
"dependencies": {
"jszip": "^3.10.1",
"tinycolor2": "1.6.0",
"txml": "^5.1.1"
}
},
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@ -12278,7 +12288,6 @@
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.1.tgz",
"integrity": "sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==",
"dev": true,
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
@ -12689,7 +12698,6 @@
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true,
"funding": [
{
"type": "github",
@ -13338,7 +13346,6 @@
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dev": true,
"dependencies": {
"safe-buffer": "~5.2.0"
}
@ -14395,6 +14402,23 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
},
"node_modules/txml": {
"version": "5.1.1",
"resolved": "https://registry.npmmirror.com/txml/-/txml-5.1.1.tgz",
"integrity": "sha512-TwMDLnXQ09enNaxybLVvKZU7rqog8LgnuAs4ZYXM0nV0eu10iLsSFwlX3AEknAXXtH1wT3CYfoiXAjyBexcmuw==",
"dependencies": {
"through2": "^3.0.1"
}
},
"node_modules/txml/node_modules/through2": {
"version": "3.0.2",
"resolved": "https://registry.npmmirror.com/through2/-/through2-3.0.2.tgz",
"integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==",
"dependencies": {
"inherits": "^2.0.4",
"readable-stream": "2 || 3"
}
},
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@ -14887,22 +14911,6 @@
"vue": "^3.0.0"
}
},
"node_modules/vuedraggable": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-4.1.0.tgz",
"integrity": "sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==",
"dependencies": {
"sortablejs": "1.14.0"
},
"peerDependencies": {
"vue": "^3.0.1"
}
},
"node_modules/vuedraggable/node_modules/sortablejs": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.14.0.tgz",
"integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w=="
},
"node_modules/w3c-keyname": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.6.tgz",
@ -24667,6 +24675,16 @@
}
}
},
"pptxtojson": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/pptxtojson/-/pptxtojson-0.0.8.tgz",
"integrity": "sha512-9RDoPhTF9Nq7xlJbzmi05lfLV4TVa7sATX5uUmzWEj5mZyQ1SymIU8e6E1/h86/4BUVB0DbM9blwjCBXjiVlpw==",
"requires": {
"jszip": "^3.10.1",
"tinycolor2": "1.6.0",
"txml": "^5.1.1"
}
},
"prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@ -25104,7 +25122,6 @@
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.1.tgz",
"integrity": "sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==",
"dev": true,
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
@ -25406,8 +25423,7 @@
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
},
"safe-regex-test": {
"version": "1.0.0",
@ -25928,7 +25944,6 @@
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dev": true,
"requires": {
"safe-buffer": "~5.2.0"
}
@ -26708,6 +26723,25 @@
}
}
},
"txml": {
"version": "5.1.1",
"resolved": "https://registry.npmmirror.com/txml/-/txml-5.1.1.tgz",
"integrity": "sha512-TwMDLnXQ09enNaxybLVvKZU7rqog8LgnuAs4ZYXM0nV0eu10iLsSFwlX3AEknAXXtH1wT3CYfoiXAjyBexcmuw==",
"requires": {
"through2": "^3.0.1"
},
"dependencies": {
"through2": {
"version": "3.0.2",
"resolved": "https://registry.npmmirror.com/through2/-/through2-3.0.2.tgz",
"integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==",
"requires": {
"inherits": "^2.0.4",
"readable-stream": "2 || 3"
}
}
}
},
"type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@ -27071,21 +27105,6 @@
"is-plain-object": "3.0.1"
}
},
"vuedraggable": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-4.1.0.tgz",
"integrity": "sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==",
"requires": {
"sortablejs": "1.14.0"
},
"dependencies": {
"sortablejs": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.14.0.tgz",
"integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w=="
}
}
},
"w3c-keyname": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.6.tgz",

View File

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

View File

@ -10,6 +10,7 @@ export interface ShapePoolItem {
special?: boolean
pathFormula?: ShapePathFormulasKeys
outlined?: boolean
pptxShapeType?: string
}
interface ShapeListItem {
@ -228,47 +229,56 @@ export const SHAPE_LIST: ShapeListItem[] = [
children: [
{
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',
pptxShapeType: 'rect',
},
{
viewBox: [200, 200],
path: 'M 50 0 L 150 0 Q 200 0 200 50 L 200 150 Q 200 200 150 200 L 50 200 Q 0 200 0 150 L 0 50 Q 0 0 50 0 Z',
pathFormula: ShapePathFormulasKeys.ROUND_RECT,
pptxShapeType: 'roundRect',
},
{
viewBox: [200, 200],
path: 'M 0 200 L 0 0 L 150 0 L 200 50 L 200 200 Z',
pathFormula: ShapePathFormulasKeys.CUT_RECT_SINGLE,
pptxShapeType: 'snip1Rect',
},
{
viewBox: [200, 200],
path: 'M 0 50 L 50 0 L 150 0 L 200 50 L 200 200 L 0 200 Z',
pathFormula: ShapePathFormulasKeys.CUT_RECT_SAMESIDE,
pptxShapeType: 'snip2SameRect',
},
{
viewBox: [200, 200],
path: 'M 0 150 L 0 0 L 150 0 L 200 50 L 200 200 L 50 200 Z',
pathFormula: ShapePathFormulasKeys.CUT_RECT_DIAGONAL,
pptxShapeType: 'snip2DiagRect',
},
{
viewBox: [200, 200],
path: 'M 50 0 L 150 0 L 200 50 L 200 200 L 0 200 L 0 50 Q 0 0 50 0 Z',
pathFormula: ShapePathFormulasKeys.CUT_ROUND_RECT,
pptxShapeType: 'snipRoundRect',
},
{
viewBox: [200, 200],
path: 'M 0 0 L 150 0 Q 200 0 200 50 L 200 200 L 0 200 L 0 0 Z',
pathFormula: ShapePathFormulasKeys.ROUND_RECT_SINGLE,
pptxShapeType: 'round1Rect',
},
{
viewBox: [200, 200],
path: 'M 0 50 Q 0 0 50 0 L 150 0 Q 200 0 200 50 L 200 200 L 0 200 Z',
pathFormula: ShapePathFormulasKeys.ROUND_RECT_SAMESIDE,
pptxShapeType: 'round2SameRect',
},
{
viewBox: [200, 200],
path: 'M 50 0 L 200 0 L 200 150 Q 200 200 150 200 L 0 200 L 0 50 Q 0 0 50 0 Z',
pathFormula: ShapePathFormulasKeys.ROUND_RECT_DIAGONAL,
pptxShapeType: 'round2DiagRect',
},
]
},
@ -278,12 +288,14 @@ export const SHAPE_LIST: ShapeListItem[] = [
children: [
{
viewBox: [200, 200],
path: 'M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z'
path: 'M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z',
pptxShapeType: 'ellipse',
},
{
viewBox: [200, 200],
path: 'M 100 0 L 0 200 L 200 200 L 100 0 Z',
pathFormula: ShapePathFormulasKeys.TRIANGLE,
pptxShapeType: 'triangle',
},
{
viewBox: [200, 200],
@ -297,6 +309,7 @@ export const SHAPE_LIST: ShapeListItem[] = [
viewBox: [200, 200],
path: 'M 50 0 L 200 0 L 150 200 L 0 200 L 50 0 Z',
pathFormula: ShapePathFormulasKeys.PARALLELOGRAM_LEFT,
pptxShapeType: 'parallelogram',
},
{
viewBox: [200, 200],
@ -307,10 +320,12 @@ export const SHAPE_LIST: ShapeListItem[] = [
viewBox: [200, 200],
path: 'M 50 0 L 150 0 L 200 200 L 0 200 L 50 0 Z',
pathFormula: ShapePathFormulasKeys.TRAPEZOID,
pptxShapeType: 'trapezoid',
},
{
viewBox: [200, 200],
path: 'M 100 0 L 0 100 L 100 200 L 200 100 L 100 0 Z'
path: 'M 100 0 L 0 100 L 100 200 L 200 100 L 100 0 Z',
pptxShapeType: 'diamond',
},
{
viewBox: [200, 200],
@ -340,7 +355,8 @@ export const SHAPE_LIST: ShapeListItem[] = [
},
{
viewBox: [200, 200],
path: 'M 100 0 A 100 100 102 1 0 200 100 L 100 100 L 100 0 Z'
path: 'M 100 0 A 100 100 102 1 0 200 100 L 100 100 L 100 0 Z',
pptxShapeType: 'pie',
},
{
viewBox: [200, 200],
@ -348,11 +364,13 @@ export const SHAPE_LIST: ShapeListItem[] = [
},
{
viewBox: [200, 200],
path: 'M 100 0 A 100 100 102 1 0 200 100 L 100 0 Z'
path: 'M 100 0 A 100 100 102 1 0 200 100 L 100 0 Z',
pptxShapeType: 'chord',
},
{
viewBox: [200, 200],
path: 'M 100 0 A 100 100 102 1 0 200 100 L 200 0 L 100 0 Z'
path: 'M 100 0 A 100 100 102 1 0 200 100 L 200 0 L 100 0 Z',
pptxShapeType: 'teardrop',
},
{
viewBox: [200, 200],
@ -360,7 +378,13 @@ export const SHAPE_LIST: ShapeListItem[] = [
},
{
viewBox: [200, 200],
path: 'M 100 0 L 0 90 L 50 200 L 150 200 L 200 90 L 100 0 Z'
path: 'M 100 0 L 0 90 L 50 200 L 150 200 L 200 90 L 100 0 Z',
pptxShapeType: 'pentagon',
},
{
viewBox: [200, 200],
path: 'M 40 0 L 160 0 L 200 100 L 160 200 L 40 200 L 0 100 Z',
pptxShapeType: 'hexagon',
},
{
viewBox: [200, 200],
@ -368,7 +392,8 @@ export const SHAPE_LIST: ShapeListItem[] = [
},
{
viewBox: [200, 200],
path: 'M 60 0 L 140 0 L 200 60 L 200 140 L 140 200 L 60 200 L 0 140 L 0 60 L 60 0 Z'
path: 'M 60 0 L 140 0 L 200 60 L 200 140 L 140 200 L 60 200 L 0 140 L 0 60 L 60 0 Z',
pptxShapeType: 'octagon',
},
{
viewBox: [200, 200],
@ -462,6 +487,13 @@ export const SHAPE_LIST: ShapeListItem[] = [
{
viewBox: [200, 200],
path: 'M 100 0 L 120 80 L 200 100 L 120 120 L 100 200 L 80 120 L 0 100 L 80 80 L 100 0 Z',
pptxShapeType: 'star4',
},
{
viewBox: [1024, 1024],
path: 'M1018.67652554 400.05983681l-382.95318779-5.89158658L512 34.78141155 388.27666225 394.16825023l-382.95318779 5.89158658L311.68602415 629.83174977l-117.83174978 365.27842665 312.25413766-223.88032637 312.25413904 223.88032637-117.83175116-365.27842665 318.14572563-229.77191296z',
pptxShapeType: 'star5',
special: true,
},
{
viewBox: [200, 200],
@ -470,6 +502,7 @@ export const SHAPE_LIST: ShapeListItem[] = [
{
viewBox: [200, 200],
path: 'M 100 0 L 140 60 L 200 60 L 160 100 L 200 140 L 140 140 L 100 200 L 60 140 L 0 140 L 40 100 L 0 60 L 60 60 L 100 0 Z',
pptxShapeType: 'star6',
},
{
viewBox: [200, 200],
@ -487,27 +520,33 @@ export const SHAPE_LIST: ShapeListItem[] = [
children: [
{
viewBox: [200, 200],
path: 'M 100 0 L 0 100 L 50 100 L 50 200 L 150 200 L 150 100 L 200 100 L 100 0 Z'
path: 'M 100 0 L 0 100 L 50 100 L 50 200 L 150 200 L 150 100 L 200 100 L 100 0 Z',
pptxShapeType: 'upArrow',
},
{
viewBox: [200, 200],
path: 'M 100 200 L 200 100 L 150 100 L 150 0 L 50 0 L 50 100 L 0 100 L 100 200 Z'
path: 'M 100 200 L 200 100 L 150 100 L 150 0 L 50 0 L 50 100 L 0 100 L 100 200 Z',
pptxShapeType: 'downArrow',
},
{
viewBox: [200, 200],
path: 'M 0 100 L 100 0 L 100 50 L 200 50 L 200 150 L 100 150 L 100 200 L 0 100 Z'
path: 'M 0 100 L 100 0 L 100 50 L 200 50 L 200 150 L 100 150 L 100 200 L 0 100 Z',
pptxShapeType: 'leftArrow',
},
{
viewBox: [200, 200],
path: 'M 200 100 L 100 0 L 100 50 L 0 50 L 0 150 L 100 150 L 100 200 L 200 100 Z'
path: 'M 200 100 L 100 0 L 100 50 L 0 50 L 0 150 L 100 150 L 100 200 L 200 100 Z',
pptxShapeType: 'rightArrow',
},
{
viewBox: [200, 200],
path: 'M 100 0 L 0 60 L 60 60 L 60 140 L 0 140 L 100 200 L 200 140 L 140 140 L 140 60 L 200 60 L 100 0 Z'
path: 'M 100 0 L 0 60 L 60 60 L 60 140 L 0 140 L 100 200 L 200 140 L 140 140 L 140 60 L 200 60 L 100 0 Z',
pptxShapeType: 'upDownArrow',
},
{
viewBox: [200, 200],
path: 'M 0 100 L 60 0 L 60 60 L 140 60 L 140 0 L 200 100 L 140 200 L 140 140 L 60 140 L 60 200 L 0 100 Z'
path: 'M 0 100 L 60 0 L 60 60 L 140 60 L 140 0 L 200 100 L 140 200 L 140 140 L 60 140 L 60 200 L 0 100 Z',
pptxShapeType: 'leftRightArrow',
},
{
viewBox: [200, 200],
@ -515,11 +554,12 @@ export const SHAPE_LIST: ShapeListItem[] = [
},
{
viewBox: [200, 200],
path: 'M 0 100 L 100 0 L 100 50 L 200 50 L 150 100 L 200 150 L 100 150 L 100 200 L 0 100 Z'
path: 'M 0 100 L 100 0 L 100 50 L 200 50 L 150 100 L 200 150 L 100 150 L 100 200 L 0 100 Z',
},
{
viewBox: [200, 200],
path: 'M 200 100 L 100 0 L 100 50 L 0 50 L 50 100 L 0 150 L 100 150 L 100 200 L 200 100 Z'
path: 'M 200 100 L 100 0 L 100 50 L 0 50 L 50 100 L 0 150 L 100 150 L 100 200 L 200 100 Z',
pptxShapeType: 'notchedRightArrow',
},
{
viewBox: [200, 200],
@ -527,19 +567,21 @@ export const SHAPE_LIST: ShapeListItem[] = [
},
{
viewBox: [200, 200],
path: 'M 200 100 L 120 20 L 120 80 L 80 80 L 80 0 L 0 0 L 0 200 L 80 200 L 80 120 L 120 120 L 120 180 L 200 100 Z'
path: 'M 200 100 L 120 20 L 120 80 L 80 80 L 80 0 L 0 0 L 0 200 L 80 200 L 80 120 L 120 120 L 120 180 L 200 100 Z',
},
{
viewBox: [200, 200],
path: 'M 0 0 L 120 0 L 200 100 L 120 200 L 0 200 L 80 100 L 0 0 Z'
path: 'M 0 0 L 120 0 L 200 100 L 120 200 L 0 200 L 80 100 L 0 0 Z',
pptxShapeType: 'chevron',
},
{
viewBox: [200, 200],
path: 'M 80 0 L 200 0 L 120 100 L 200 200 L 80 200 L 0 100 L 80 0 Z'
path: 'M 80 0 L 200 0 L 120 100 L 200 200 L 80 200 L 0 100 L 80 0 Z',
},
{
viewBox: [200, 200],
path: 'M 0 0 L 140 0 L 200 100 L 140 200 L 0 200 L 0 100 L 0 0 Z'
path: 'M 0 0 L 140 0 L 200 100 L 140 200 L 0 200 L 0 100 L 0 0 Z',
pptxShapeType: 'homePlate',
},
{
viewBox: [200, 200],

View File

@ -10,10 +10,9 @@ import { PPTElementOutline, PPTElementShadow, PPTElementLink, Slide } from '@/ty
import { getElementRange, getLineElementPath, getTableSubThemeColor } from '@/utils/element'
import { AST, toAST } from '@/utils/htmlParser'
import { SvgPoints, toPoints } from '@/utils/svgPathParser'
import { decrypt, encrypt } from '@/utils/crypto'
import { encrypt } from '@/utils/crypto'
import { svg2Base64 } from '@/utils/svg2Base64'
import { message } from 'ant-design-vue'
import useAddSlidesOrElements from '@/hooks/useAddSlidesOrElements'
const INCH_PX_RATIO = 100
const PT_PX_RATIO = 0.75
@ -28,8 +27,6 @@ export default () => {
const slidesStore = useSlidesStore()
const { slides, theme, viewportRatio } = storeToRefs(slidesStore)
const { addSlidesFromData } = useAddSlidesOrElements()
const exporting = ref(false)
// 导出图片
@ -64,24 +61,6 @@ export default () => {
saveAs(blob, 'pptist_slides.pptist')
}
// 导入pptist文件
const importSpecificFile = (files: FileList, cover = false) => {
const file = files[0]
const reader = new FileReader()
reader.addEventListener('load', () => {
try {
const slides = JSON.parse(decrypt(reader.result as string))
if (cover) slidesStore.setSlides(slides)
else addSlidesFromData(slides)
}
catch {
message.error('无法正确读取 / 解析该文件')
}
})
reader.readAsText(file)
}
// 导出JSON文件
const exportJSON = () => {
const blob = new Blob([JSON.stringify(slides.value)], { type: '' })
@ -778,7 +757,6 @@ export default () => {
exporting,
exportImage,
exportJSON,
importSpecificFile,
exportSpecificFile,
exportPPTX,
}

266
src/hooks/useImport.ts Normal file
View File

@ -0,0 +1,266 @@
import { storeToRefs } from 'pinia'
import { parse } from 'pptxtojson'
import { nanoid } from 'nanoid'
import { Slide, TableCellStyle, TableCell, ChartType, ChartOptions, SlideBackground, PPTShapeElement } from '@/types/slides'
import { useSlidesStore } from '@/store'
import { decrypt } from '@/utils/crypto'
import { ShapePoolItem, SHAPE_LIST, SHAPE_PATH_FORMULAS } from '@/configs/shapes'
import useAddSlidesOrElements from '@/hooks/useAddSlidesOrElements'
import { message } from 'ant-design-vue'
export default () => {
const slidesStore = useSlidesStore()
const { theme } = storeToRefs(useSlidesStore())
const { addSlidesFromData } = useAddSlidesOrElements()
// 导入pptist文件
const importSpecificFile = (files: FileList, cover = false) => {
const file = files[0]
const reader = new FileReader()
reader.addEventListener('load', () => {
try {
const slides = JSON.parse(decrypt(reader.result as string))
if (cover) slidesStore.setSlides(slides)
else addSlidesFromData(slides)
}
catch {
message.error('无法正确读取 / 解析该文件')
}
})
reader.readAsText(file)
}
// 导入PPTX文件
const importPPTXFile = (files: FileList) => {
const file = files[0]
if (!file) return
const shapeList: ShapePoolItem[] = []
for (const item of SHAPE_LIST) {
shapeList.push(...item.children)
}
const reader = new FileReader()
reader.onload = async e => {
const json = await parse(e.target!.result as ArrayBuffer)
const slides: Slide[] = []
for (const item of json.slides) {
const { type, value } = item.fill
let background: SlideBackground
if (type === 'image') {
background = {
type: 'image',
image: value.picBase64,
imageSize: 'cover',
}
}
else {
background = {
type: 'solid',
color: value,
}
}
const slide: Slide = {
id: nanoid(10),
elements: [],
background,
}
for (const el of item.elements) {
if (el.type === 'text') {
slide.elements.push({
type: 'text',
id: nanoid(10),
width: el.width,
height: el.height,
left: el.left,
top: el.top,
rotate: el.rotate,
defaultFontName: theme.value.fontName,
defaultColor: theme.value.fontColor,
content: el.content,
lineHeight: 1,
outline: {
color: el.borderColor,
width: el.borderWidth,
style: el.borderType,
},
fill: el.fillColor,
})
}
else if (el.type === 'image') {
slide.elements.push({
type: 'image',
id: nanoid(10),
src: el.src,
width: el.width,
height: el.height,
left: el.left,
top: el.top,
fixedRatio: true,
rotate: el.rotate,
})
}
else if (el.type === 'shape') {
const shape = shapeList.find(item => item.pptxShapeType === el.shapType)
const element: PPTShapeElement = {
type: 'shape',
id: nanoid(10),
width: el.width,
height: el.height,
left: el.left,
top: el.top,
viewBox: [200, 200],
path: 'M 0 0 L 200 0 L 200 200 L 0 200 Z',
fill: el.fillColor,
fixedRatio: false,
rotate: el.rotate,
outline: {
color: el.borderColor,
width: el.borderWidth,
style: el.borderType,
},
text: {
content: el.content,
defaultFontName: theme.value.fontName,
defaultColor: theme.value.fontColor,
align: 'middle',
}
}
if (shape) {
element.path = shape.path
element.viewBox = shape.viewBox
if (shape.pathFormula) {
element.pathFormula = shape.pathFormula
element.viewBox = [el.width, el.height]
const pathFormula = SHAPE_PATH_FORMULAS[shape.pathFormula]
if ('editable' in pathFormula) {
element.path = pathFormula.formula(el.width, el.height, pathFormula.defaultValue)
element.keypoint = pathFormula.defaultValue
}
else element.path = pathFormula.formula(el.width, el.height)
}
}
slide.elements.push(element)
}
else if (el.type === 'table') {
const row = el.data.length
const col = el.data[0].length
const style: TableCellStyle = {
fontname: theme.value.fontName,
color: theme.value.fontColor,
}
const data: TableCell[][] = []
for (let i = 0; i < row; i++) {
const rowCells: TableCell[] = []
for (let j = 0; j < col; j++) {
const cellData = el.data[i][j]
rowCells.push({
id: nanoid(10),
colspan: 1,
rowspan: cellData.rowSpan || 1,
text: cellData.text,
style,
})
}
data.push(rowCells)
}
const colWidths: number[] = new Array(col).fill(1 / col)
slide.elements.push({
type: 'table',
id: nanoid(10),
width: el.width,
height: el.height,
left: el.left,
top: el.top,
colWidths,
rotate: 0,
data,
outline: {
width: 2,
style: 'solid',
color: '#eeece1',
},
theme: {
color: theme.value.themeColor,
rowHeader: true,
rowFooter: false,
colHeader: false,
colFooter: false,
},
cellMinHeight: 36,
})
}
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 options: ChartOptions = {}
let chartType: ChartType = 'bar'
if (el.chartType === 'barChart') {
chartType = 'bar'
}
if (el.chartType === 'stackedBarChart') {
chartType = 'bar'
options = { stackBars: true }
}
else if (el.chartType === 'lineChart') {
chartType = 'line'
}
else if (el.chartType === 'areaChart') {
chartType = 'line'
options = { showArea: true }
}
else if (el.chartType === 'scatterChart') {
chartType = 'line'
options = { showLine: false }
}
else if (el.chartType === 'pieChart' || el.chartType === 'pie3DChart') {
chartType = 'pie'
}
slide.elements.push({
type: 'chart',
id: nanoid(10),
chartType: chartType,
width: el.width,
height: el.height,
left: el.left,
top: el.top,
rotate: 0,
themeColor: [theme.value.themeColor],
gridColor: theme.value.fontColor,
data: {
labels,
legends,
series,
},
options,
})
}
// else if (el.type === 'group') {}
}
slides.push(slide)
}
addSlidesFromData(slides)
}
reader.readAsArrayBuffer(file)
}
return {
importSpecificFile,
importPPTXFile,
}
}

View File

@ -8,6 +8,9 @@
<FileInput accept=".pptist" @change="files => importSpecificFile(files)">
<MenuItem>导入 pptist 文件</MenuItem>
</FileInput>
<FileInput accept="application/vnd.openxmlformats-officedocument.presentationml.presentation" @change="files => importPPTXFile(files)">
<MenuItem>导入 pptx 文件demo</MenuItem>
</FileInput>
<MenuItem @click="setDialogForExport('pptx')">导出文件</MenuItem>
</Menu>
</template>
@ -83,7 +86,7 @@ import { useMainStore } from '@/store'
import useScreening from '@/hooks/useScreening'
import useSlideHandler from '@/hooks/useSlideHandler'
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
import useExport from '@/hooks/useExport'
import useImport from '@/hooks/useImport'
import HotkeyDoc from './HotkeyDoc.vue'
import FileInput from '@/components/FileInput.vue'
@ -101,7 +104,7 @@ const { gridLineSize, showRuler, showSelectPanel } = storeToRefs(mainStore)
const { enterScreening, enterScreeningFromStart } = useScreening()
const { createSlide, deleteSlide, resetSlides } = useSlideHandler()
const { redo, undo } = useHistorySnapshot()
const { importSpecificFile } = useExport()
const { importSpecificFile, importPPTXFile } = useImport()
const setDialogForExport = mainStore.setDialogForExport