This commit is contained in:
pipipi-pikachu 2020-12-21 22:58:22 +08:00
parent 76f74505c5
commit fdbb6dde16
12 changed files with 449 additions and 324 deletions

357
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "ppt_online_editor", "name": "hamster_ppt",
"version": "0.1.0", "version": "0.0.1",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -2258,122 +2258,6 @@
"tslint": "^5.20.1", "tslint": "^5.20.1",
"webpack": "^4.0.0", "webpack": "^4.0.0",
"yorkie": "^2.0.0" "yorkie": "^2.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-4.3.0.tgz?cache=0&sync_timestamp=1606792369066&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fansi-styles%2Fdownload%2Fansi-styles-4.3.0.tgz",
"integrity": "sha1-7dgDYornHATIWuegkG7a00tkiTc=",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npm.taobao.org/chalk/download/chalk-4.1.0.tgz?cache=0&sync_timestamp=1591687000046&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fchalk%2Fdownload%2Fchalk-4.1.0.tgz",
"integrity": "sha1-ThSHCmGNni7dl92DRf2dncMVZGo=",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npm.taobao.org/color-convert/download/color-convert-2.0.1.tgz?cache=0&sync_timestamp=1566248870121&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcolor-convert%2Fdownload%2Fcolor-convert-2.0.1.tgz",
"integrity": "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npm.taobao.org/color-name/download/color-name-1.1.4.tgz",
"integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=",
"dev": true,
"optional": true
},
"fork-ts-checker-webpack-plugin-v5": {
"version": "npm:fork-ts-checker-webpack-plugin@5.2.1",
"resolved": "https://registry.npm.taobao.org/fork-ts-checker-webpack-plugin/download/fork-ts-checker-webpack-plugin-5.2.1.tgz",
"integrity": "sha1-eTJthpeXkG+osk4qvPlCH8gFRQ0=",
"dev": true,
"optional": true,
"requires": {
"@babel/code-frame": "^7.8.3",
"@types/json-schema": "^7.0.5",
"chalk": "^4.1.0",
"cosmiconfig": "^6.0.0",
"deepmerge": "^4.2.2",
"fs-extra": "^9.0.0",
"memfs": "^3.1.2",
"minimatch": "^3.0.4",
"schema-utils": "2.7.0",
"semver": "^7.3.2",
"tapable": "^1.0.0"
}
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-4.0.0.tgz",
"integrity": "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=",
"dev": true,
"optional": true
},
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npm.taobao.org/lru-cache/download/lru-cache-6.0.0.tgz?cache=0&sync_timestamp=1594427484405&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flru-cache%2Fdownload%2Flru-cache-6.0.0.tgz",
"integrity": "sha1-bW/mVw69lqr5D8rR2vo7JWbbOpQ=",
"dev": true,
"optional": true,
"requires": {
"yallist": "^4.0.0"
}
},
"schema-utils": {
"version": "2.7.0",
"resolved": "https://registry.npm.taobao.org/schema-utils/download/schema-utils-2.7.0.tgz",
"integrity": "sha1-FxUfdtjq5n+793lgwzxnatn078c=",
"dev": true,
"optional": true,
"requires": {
"@types/json-schema": "^7.0.4",
"ajv": "^6.12.2",
"ajv-keywords": "^3.4.1"
}
},
"semver": {
"version": "7.3.4",
"resolved": "https://registry.npm.taobao.org/semver/download/semver-7.3.4.tgz?cache=0&sync_timestamp=1606852064928&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsemver%2Fdownload%2Fsemver-7.3.4.tgz",
"integrity": "sha1-J6qn0uTKdkUvmNOt0JOnLJQ+3Jc=",
"dev": true,
"optional": true,
"requires": {
"lru-cache": "^6.0.0"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-7.2.0.tgz?cache=0&sync_timestamp=1608033330722&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-7.2.0.tgz",
"integrity": "sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npm.taobao.org/yallist/download/yallist-4.0.0.tgz",
"integrity": "sha1-m7knkNnA7/7GO+c1GeEaNQGaOnI=",
"dev": true,
"optional": true
}
} }
}, },
"@vue/cli-plugin-unit-jest": { "@vue/cli-plugin-unit-jest": {
@ -2537,17 +2421,6 @@
"unique-filename": "^1.1.1" "unique-filename": "^1.1.1"
} }
}, },
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npm.taobao.org/chalk/download/chalk-4.1.0.tgz?cache=0&sync_timestamp=1591687000046&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fchalk%2Fdownload%2Fchalk-4.1.0.tgz",
"integrity": "sha1-ThSHCmGNni7dl92DRf2dncMVZGo=",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"cliui": { "cliui": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npm.taobao.org/cliui/download/cliui-6.0.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcliui%2Fdownload%2Fcliui-6.0.0.tgz", "resolved": "https://registry.npm.taobao.org/cliui/download/cliui-6.0.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcliui%2Fdownload%2Fcliui-6.0.0.tgz",
@ -2610,18 +2483,6 @@
"graceful-fs": "^4.1.6" "graceful-fs": "^4.1.6"
} }
}, },
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/loader-utils/download/loader-utils-2.0.0.tgz",
"integrity": "sha1-5MrOW4FtQloWa18JfhDNErNgZLA=",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"source-map": { "source-map": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz", "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz",
@ -2670,18 +2531,6 @@
"integrity": "sha1-tkb2m+OULavOzJ1mOcgNwQXvqmY=", "integrity": "sha1-tkb2m+OULavOzJ1mOcgNwQXvqmY=",
"dev": true "dev": true
}, },
"vue-loader-v16": {
"version": "npm:vue-loader@16.1.2",
"resolved": "https://registry.npm.taobao.org/vue-loader/download/vue-loader-16.1.2.tgz?cache=0&sync_timestamp=1608187974157&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-loader%2Fdownload%2Fvue-loader-16.1.2.tgz",
"integrity": "sha1-XAO2xQ0qX5g8fOuhXFDXjKKymPQ=",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
}
},
"wrap-ansi": { "wrap-ansi": {
"version": "6.2.0", "version": "6.2.0",
"resolved": "https://registry.npm.taobao.org/wrap-ansi/download/wrap-ansi-6.2.0.tgz", "resolved": "https://registry.npm.taobao.org/wrap-ansi/download/wrap-ansi-6.2.0.tgz",
@ -7473,6 +7322,122 @@
"worker-rpc": "^0.1.0" "worker-rpc": "^0.1.0"
} }
}, },
"fork-ts-checker-webpack-plugin-v5": {
"version": "npm:fork-ts-checker-webpack-plugin@5.2.1",
"resolved": "https://registry.npm.taobao.org/fork-ts-checker-webpack-plugin/download/fork-ts-checker-webpack-plugin-5.2.1.tgz",
"integrity": "sha1-eTJthpeXkG+osk4qvPlCH8gFRQ0=",
"dev": true,
"optional": true,
"requires": {
"@babel/code-frame": "^7.8.3",
"@types/json-schema": "^7.0.5",
"chalk": "^4.1.0",
"cosmiconfig": "^6.0.0",
"deepmerge": "^4.2.2",
"fs-extra": "^9.0.0",
"memfs": "^3.1.2",
"minimatch": "^3.0.4",
"schema-utils": "2.7.0",
"semver": "^7.3.2",
"tapable": "^1.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-4.3.0.tgz?cache=0&sync_timestamp=1606792302448&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fansi-styles%2Fdownload%2Fansi-styles-4.3.0.tgz",
"integrity": "sha1-7dgDYornHATIWuegkG7a00tkiTc=",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npm.taobao.org/chalk/download/chalk-4.1.0.tgz",
"integrity": "sha1-ThSHCmGNni7dl92DRf2dncMVZGo=",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npm.taobao.org/color-convert/download/color-convert-2.0.1.tgz",
"integrity": "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npm.taobao.org/color-name/download/color-name-1.1.4.tgz",
"integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=",
"dev": true,
"optional": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-4.0.0.tgz?cache=0&sync_timestamp=1596294337050&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhas-flag%2Fdownload%2Fhas-flag-4.0.0.tgz",
"integrity": "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=",
"dev": true,
"optional": true
},
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npm.taobao.org/lru-cache/download/lru-cache-6.0.0.tgz",
"integrity": "sha1-bW/mVw69lqr5D8rR2vo7JWbbOpQ=",
"dev": true,
"optional": true,
"requires": {
"yallist": "^4.0.0"
}
},
"schema-utils": {
"version": "2.7.0",
"resolved": "https://registry.npm.taobao.org/schema-utils/download/schema-utils-2.7.0.tgz",
"integrity": "sha1-FxUfdtjq5n+793lgwzxnatn078c=",
"dev": true,
"optional": true,
"requires": {
"@types/json-schema": "^7.0.4",
"ajv": "^6.12.2",
"ajv-keywords": "^3.4.1"
}
},
"semver": {
"version": "7.3.4",
"resolved": "https://registry.npm.taobao.org/semver/download/semver-7.3.4.tgz?cache=0&sync_timestamp=1606852122426&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsemver%2Fdownload%2Fsemver-7.3.4.tgz",
"integrity": "sha1-J6qn0uTKdkUvmNOt0JOnLJQ+3Jc=",
"dev": true,
"optional": true,
"requires": {
"lru-cache": "^6.0.0"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-7.2.0.tgz",
"integrity": "sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npm.taobao.org/yallist/download/yallist-4.0.0.tgz",
"integrity": "sha1-m7knkNnA7/7GO+c1GeEaNQGaOnI=",
"dev": true,
"optional": true
}
}
},
"form-data": { "form-data": {
"version": "2.3.3", "version": "2.3.3",
"resolved": "https://registry.npm.taobao.org/form-data/download/form-data-2.3.3.tgz", "resolved": "https://registry.npm.taobao.org/form-data/download/form-data-2.3.3.tgz",
@ -10796,6 +10761,11 @@
"through2": "^2.0.0" "through2": "^2.0.0"
} }
}, },
"mitt": {
"version": "2.1.0",
"resolved": "https://registry.npm.taobao.org/mitt/download/mitt-2.1.0.tgz?cache=0&sync_timestamp=1594823587749&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmitt%2Fdownload%2Fmitt-2.1.0.tgz",
"integrity": "sha1-90BXfCMXbGIFsSGylzUU6t4bIjA="
},
"mixin-deep": { "mixin-deep": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npm.taobao.org/mixin-deep/download/mixin-deep-1.3.2.tgz", "resolved": "https://registry.npm.taobao.org/mixin-deep/download/mixin-deep-1.3.2.tgz",
@ -15913,6 +15883,87 @@
} }
} }
}, },
"vue-loader-v16": {
"version": "npm:vue-loader@16.1.2",
"resolved": "https://registry.npm.taobao.org/vue-loader/download/vue-loader-16.1.2.tgz?cache=0&sync_timestamp=1608187974157&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-loader%2Fdownload%2Fvue-loader-16.1.2.tgz",
"integrity": "sha1-XAO2xQ0qX5g8fOuhXFDXjKKymPQ=",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-4.3.0.tgz?cache=0&sync_timestamp=1606792302448&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fansi-styles%2Fdownload%2Fansi-styles-4.3.0.tgz",
"integrity": "sha1-7dgDYornHATIWuegkG7a00tkiTc=",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npm.taobao.org/chalk/download/chalk-4.1.0.tgz",
"integrity": "sha1-ThSHCmGNni7dl92DRf2dncMVZGo=",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npm.taobao.org/color-convert/download/color-convert-2.0.1.tgz",
"integrity": "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npm.taobao.org/color-name/download/color-name-1.1.4.tgz",
"integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=",
"dev": true,
"optional": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-4.0.0.tgz?cache=0&sync_timestamp=1596294337050&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhas-flag%2Fdownload%2Fhas-flag-4.0.0.tgz",
"integrity": "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=",
"dev": true,
"optional": true
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/loader-utils/download/loader-utils-2.0.0.tgz",
"integrity": "sha1-5MrOW4FtQloWa18JfhDNErNgZLA=",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-7.2.0.tgz",
"integrity": "sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"vue-router": { "vue-router": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npm.taobao.org/vue-router/download/vue-router-4.0.1.tgz?cache=0&sync_timestamp=1607347245114&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-router%2Fdownload%2Fvue-router-4.0.1.tgz", "resolved": "https://registry.npm.taobao.org/vue-router/download/vue-router-4.0.1.tgz?cache=0&sync_timestamp=1607347245114&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-router%2Fdownload%2Fvue-router-4.0.1.tgz",

View File

@ -15,6 +15,7 @@
"crypto-js": "^4.0.0", "crypto-js": "^4.0.0",
"dexie": "^3.0.3", "dexie": "^3.0.3",
"lodash": "^4.17.20", "lodash": "^4.17.20",
"mitt": "^2.1.0",
"store2": "^2.12.0", "store2": "^2.12.0",
"vue": "^3.0.0", "vue": "^3.0.0",
"vue-router": "^4.0.0-0", "vue-router": "^4.0.0-0",

View File

@ -10,7 +10,6 @@ export enum ElementTypes {
} }
export const DEFAULT_TEXT = { export const DEFAULT_TEXT = {
type: 'text',
left: 0, left: 0,
top: 0, top: 0,
width: 300, width: 300,
@ -22,20 +21,17 @@ export const DEFAULT_TEXT = {
} }
export const DEFAULT_IMAGE = { export const DEFAULT_IMAGE = {
type: 'image',
left: 0, left: 0,
top: 0, top: 0,
lockRatio: true, lockRatio: true,
} }
export const DEFAULT_SHAPE = { export const DEFAULT_SHAPE = {
type: 'shape',
fill: DEFAULT_COLOR, fill: DEFAULT_COLOR,
lockRatio: false, lockRatio: false,
} }
export const DEFAULT_LINE = { export const DEFAULT_LINE = {
type: 'line',
style: 'solid', style: 'solid',
marker: ['', ''], marker: ['', ''],
width: 4, width: 4,
@ -43,7 +39,6 @@ export const DEFAULT_LINE = {
} }
export const DEFAULT_CHART = { export const DEFAULT_CHART = {
type: 'chart',
left: 0, left: 0,
top: 0, top: 0,
width: 500, width: 500,
@ -51,7 +46,6 @@ export const DEFAULT_CHART = {
} }
export const DEFAULT_TABLE = { export const DEFAULT_TABLE = {
type: 'table',
left: 0, left: 0,
top: 0, top: 0,
isLock: false, isLock: false,

View File

@ -1,131 +1,145 @@
import { createRandomCode } from '@/utils/common' import { useStore } from 'vuex'
import { getImageSize } from '@/utils/image' import { MutationTypes } from '@/store'
import { VIEWPORT_SIZE, VIEWPORT_ASPECT_RATIO } from '@/configs/canvas' import { createRandomCode } from '@/utils/common'
import { TableElementCell } from '@/types/slides' import { getImageSize } from '@/utils/image'
import { import { VIEWPORT_SIZE, VIEWPORT_ASPECT_RATIO } from '@/configs/canvas'
DEFAULT_IMAGE, import { PPTElement, TableElementCell } from '@/types/slides'
DEFAULT_TEXT, import {
DEFAULT_SHAPE, DEFAULT_IMAGE,
DEFAULT_LINE, DEFAULT_TEXT,
DEFAULT_CHART, DEFAULT_SHAPE,
DEFAULT_TABLE, DEFAULT_LINE,
} from '@/configs/element' DEFAULT_CHART,
DEFAULT_TABLE,
interface CommonElementPosition { } from '@/configs/element'
top: number;
left: number; interface CommonElementPosition {
width: number; top: number;
height: number; left: number;
} width: number;
height: number;
interface LineElementPosition { }
top: number;
left: number; interface LineElementPosition {
start: [number, number]; top: number;
end: [number, number]; left: number;
} start: [number, number];
end: [number, number];
export default () => { }
const insertImage = (imgUrl: string) => {
getImageSize(imgUrl).then(({ width, height }) => { export default () => {
const scale = width / height const store = useStore()
if(scale < VIEWPORT_ASPECT_RATIO && width > VIEWPORT_SIZE) { const createElement = (element: PPTElement) => {
width = VIEWPORT_SIZE store.commit(MutationTypes.ADD_ELEMENT, element)
height = width * scale store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, [element.elId])
} }
else if(height > VIEWPORT_SIZE * VIEWPORT_ASPECT_RATIO) {
height = VIEWPORT_SIZE * VIEWPORT_ASPECT_RATIO const createImageElement = (imgUrl: string) => {
width = height / scale getImageSize(imgUrl).then(({ width, height }) => {
} const scale = width / height
return { if(scale < VIEWPORT_ASPECT_RATIO && width > VIEWPORT_SIZE) {
...DEFAULT_IMAGE, width = VIEWPORT_SIZE
elId: createRandomCode(), height = width * scale
imgUrl, }
width, else if(height > VIEWPORT_SIZE * VIEWPORT_ASPECT_RATIO) {
height, height = VIEWPORT_SIZE * VIEWPORT_ASPECT_RATIO
} width = height / scale
}) }
}
createElement({
const insertChart = (chartType: string, data: Object) => { ...DEFAULT_IMAGE,
return { type: 'image',
...DEFAULT_CHART, elId: createRandomCode(),
elId: createRandomCode(), imgUrl,
chartType, width,
data, height,
} })
} })
}
const insertTable = (rowCount: number, colCount: number) => {
const row: TableElementCell[] = new Array(colCount).fill({ colspan: 1, rowspan: 1, content: '' }) const createChartElement = (chartType: string, data: string) => {
const data: TableElementCell[][] = new Array(rowCount).fill(row) createElement({
...DEFAULT_CHART,
const DEFAULT_CELL_WIDTH = 80 type: 'chart',
const DEFAULT_CELL_HEIGHT = 35 elId: createRandomCode(),
const DEFAULT_BORDER_WIDTH = 2 chartType,
data,
const colSizes: number[] = new Array(colCount).fill(DEFAULT_CELL_WIDTH) })
const rowSizes: number[] = new Array(rowCount).fill(DEFAULT_CELL_HEIGHT) }
return { const createTableElement = (rowCount: number, colCount: number) => {
...DEFAULT_TABLE, const row: TableElementCell[] = new Array(colCount).fill({ colspan: 1, rowspan: 1, content: '' })
elId: createRandomCode(), const data: TableElementCell[][] = new Array(rowCount).fill(row)
width: colCount * DEFAULT_CELL_WIDTH + DEFAULT_BORDER_WIDTH,
height: rowCount * DEFAULT_CELL_HEIGHT + DEFAULT_BORDER_WIDTH, const DEFAULT_CELL_WIDTH = 80
colSizes, const DEFAULT_CELL_HEIGHT = 35
rowSizes, const DEFAULT_BORDER_WIDTH = 2
data,
} const colSizes: number[] = new Array(colCount).fill(DEFAULT_CELL_WIDTH)
} const rowSizes: number[] = new Array(rowCount).fill(DEFAULT_CELL_HEIGHT)
const insertText = (position: CommonElementPosition) => { createElement({
const { left, top, width, height } = position ...DEFAULT_TABLE,
return { type: 'table',
...DEFAULT_TEXT, elId: createRandomCode(),
elId: createRandomCode(), width: colCount * DEFAULT_CELL_WIDTH + DEFAULT_BORDER_WIDTH,
left, height: rowCount * DEFAULT_CELL_HEIGHT + DEFAULT_BORDER_WIDTH,
top, colSizes,
width, rowSizes,
height, data,
} })
} }
const insertShape = (position: CommonElementPosition, svgCode: string) => { const createTextElement = (position: CommonElementPosition) => {
const { left, top, width, height } = position const { left, top, width, height } = position
return { createElement({
...DEFAULT_SHAPE, ...DEFAULT_TEXT,
elId: createRandomCode(), type: 'text',
left, elId: createRandomCode(),
top, left,
width, top,
height, width,
svgCode, height,
} })
} }
const insertLine = (position: LineElementPosition, marker: [string, string], lineType: string) => { const createShapeElement = (position: CommonElementPosition, svgCode: string) => {
const { left, top, start, end } = position const { left, top, width, height } = position
createElement({
return { ...DEFAULT_SHAPE,
...DEFAULT_LINE, type: 'shape',
elId: createRandomCode(), elId: createRandomCode(),
left, left,
top, top,
start, width,
end, height,
marker, svgCode,
lineType, })
} }
}
const createLineElement = (position: LineElementPosition, marker: [string, string], lineType: string) => {
return { const { left, top, start, end } = position
insertImage, createElement({
insertChart, ...DEFAULT_LINE,
insertTable, type: 'line',
insertText, elId: createRandomCode(),
insertShape, left,
insertLine, top,
} start,
end,
marker,
lineType,
})
}
return {
createImageElement,
createChartElement,
createTableElement,
createTextElement,
createShapeElement,
createLineElement,
}
} }

View File

@ -0,0 +1,72 @@
import { computed, Ref } from 'vue'
import { useStore } from 'vuex'
import { MutationTypes, State } from '@/store'
import { decrypt } from '@/utils/crypto'
import { PPTElement, Slide } from '@/types/slides'
import { createRandomCode } from '@/utils/common'
export default () => {
const store = useStore<State>()
const currentSlide: Ref<Slide> = computed(() => store.getters.currentSlide)
const pasteElement = (elements: PPTElement[]) => {
const groupIdMap = {}
const elIdMap = {}
for(const element of elements) {
const groupId = element.groupId
if(groupId && !groupIdMap[groupId]) {
groupIdMap[groupId] = createRandomCode()
}
elIdMap[element.elId] = createRandomCode()
}
const currentSlideElementIdList = currentSlide.value.elements.map(el => el.elId)
for(const element of elements) {
const inCurrentSlide = currentSlideElementIdList.includes(element.elId)
element.elId = elIdMap[element.elId]
if(inCurrentSlide) {
element.left = element.left + 10
element.top = element.top + 10
}
if(element.groupId) element.groupId = groupIdMap[element.groupId]
}
store.commit(MutationTypes.ADD_ELEMENT, elements)
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, Object.values(elIdMap))
}
const pasteSlide = (slides: Slide[]) => {
console.log(slides)
}
const pasteText = (text: string) => {
console.log(text)
}
const pasteTextClipboardData = (text: string) => {
let clipboardData
try {
clipboardData = JSON.parse(decrypt(text))
}
catch {
clipboardData = text
}
// 粘贴自定义元素或页面
if(typeof clipboardData === 'object') {
const { type, data } = clipboardData
if(type === 'elements') pasteElement(data)
else if(type === 'slide') pasteSlide(data)
}
// 粘贴普通文本
else pasteText(clipboardData)
}
return {
pasteTextClipboardData,
}
}

View File

@ -11,9 +11,9 @@ export enum ElementTypes {
export interface PPTElementBaseProps { export interface PPTElementBaseProps {
elId: string; elId: string;
isLock: boolean;
left: number; left: number;
top: number; top: number;
isLock?: boolean;
groupId?: string; groupId?: string;
} }
@ -78,8 +78,8 @@ export interface PPTLineElement extends PPTElementBaseProps {
export interface PPTChartElement extends PPTElementBaseProps, PPTElementSizeProps, PPTElementBorderProps { export interface PPTChartElement extends PPTElementBaseProps, PPTElementSizeProps, PPTElementBorderProps {
type: 'chart'; type: 'chart';
chartType: string; chartType: string;
theme: string;
data: string; data: string;
theme?: string;
} }
export interface TableElementCell { export interface TableElementCell {
@ -90,8 +90,8 @@ export interface TableElementCell {
} }
export interface PPTTableElement extends PPTElementBaseProps, PPTElementSizeProps { export interface PPTTableElement extends PPTElementBaseProps, PPTElementSizeProps {
type: 'table'; type: 'table';
borderTheme: string; borderTheme?: string;
theme: string; theme?: string;
rowSizes: number[]; rowSizes: number[];
colSizes: number[]; colSizes: number[];
data: TableElementCell[][]; data: TableElementCell[][];

5
src/utils/emitter.ts Normal file
View File

@ -0,0 +1,5 @@
import mitt, { Emitter } from 'mitt'
const emitter: Emitter = mitt()
export default emitter

View File

@ -3,14 +3,17 @@ import { useStore } from 'vuex'
import { State, MutationTypes } from '@/store' import { State, MutationTypes } from '@/store'
import { PPTElement } from '@/types/slides' import { PPTElement } from '@/types/slides'
import { copyText, readClipboard } from '@/utils/clipboard' import { copyText, readClipboard } from '@/utils/clipboard'
import { encrypt, decrypt } from '@/utils/crypto' import { encrypt } from '@/utils/crypto'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import usePasteTextClipboardData from '@/hooks/usePasteTextClipboardData'
export default (deleteElement: () => void) => { export default (deleteElement: () => void) => {
const store = useStore<State>() const store = useStore<State>()
const activeElementIdList = computed(() => store.state.activeElementIdList) const activeElementIdList = computed(() => store.state.activeElementIdList)
const activeElementList: Ref<PPTElement[]> = computed(() => store.getters.activeElementList) const activeElementList: Ref<PPTElement[]> = computed(() => store.getters.activeElementList)
const { pasteTextClipboardData } = usePasteTextClipboardData()
const copyElement = () => { const copyElement = () => {
if(!activeElementIdList.value.length) return if(!activeElementIdList.value.length) return
@ -32,14 +35,7 @@ export default (deleteElement: () => void) => {
const pasteElement = () => { const pasteElement = () => {
readClipboard().then(text => { readClipboard().then(text => {
let clipboardData pasteTextClipboardData(text)
try {
clipboardData = JSON.parse(decrypt(text))
}
catch {
clipboardData = text
}
console.log(clipboardData)
}).catch(err => message.warning(err)) }).catch(err => message.warning(err))
} }

View File

@ -1,16 +1,17 @@
import { onMounted, onUnmounted, Ref } from 'vue' import { onMounted, onUnmounted, Ref } from 'vue'
import { getImageDataURL } from '@/utils/image' import { getImageDataURL } from '@/utils/image'
import useCreateElement from '@/hooks/useCreateElement'
export default (elementRef: Ref<HTMLElement | null>) => { export default (elementRef: Ref<HTMLElement | null>) => {
const { createImageElement } = useCreateElement()
const handleDrop = (e: DragEvent) => { const handleDrop = (e: DragEvent) => {
if(!e.dataTransfer) return if(!e.dataTransfer) return
const file = e.dataTransfer.items[0] const file = e.dataTransfer.items[0]
if( file.kind === 'file' && file.type.indexOf('image') !== -1 ) { if( file.kind === 'file' && file.type.indexOf('image') !== -1 ) {
const imageFile = file.getAsFile() const imageFile = file.getAsFile()
if(imageFile) { if(imageFile) {
getImageDataURL(imageFile).then(dataURL => { getImageDataURL(imageFile).then(dataURL => createImageElement(dataURL))
console.log(dataURL)
})
} }
} }
} }

View File

@ -75,7 +75,7 @@ import { computed, defineComponent, Ref, ref, watch, watchEffect } from 'vue'
import { useStore } from 'vuex' import { useStore } from 'vuex'
import { State, MutationTypes } from '@/store' import { State, MutationTypes } from '@/store'
import { ContextmenuItem } from '@/components/Contextmenu/types' import { ContextmenuItem } from '@/components/Contextmenu/types'
import { PPTElement } from '@/types/slides' import { PPTElement, Slide } from '@/types/slides'
import { AlignmentLineProps } from './types/index' import { AlignmentLineProps } from './types/index'
import useViewportSize from './hooks/useViewportSize' import useViewportSize from './hooks/useViewportSize'
@ -122,7 +122,7 @@ export default defineComponent({
const activeGroupElementId = ref('') const activeGroupElementId = ref('')
watch(handleElementId, () => activeGroupElementId.value = '') watch(handleElementId, () => activeGroupElementId.value = '')
const currentSlide = computed(() => store.getters.currentSlide) const currentSlide: Ref<Slide> = computed(() => store.getters.currentSlide)
const elementList = ref<PPTElement[]>([]) const elementList = ref<PPTElement[]>([])
const setLocalElementList = () => { const setLocalElementList = () => {
elementList.value = currentSlide.value ? JSON.parse(JSON.stringify(currentSlide.value.elements)) : [] elementList.value = currentSlide.value ? JSON.parse(JSON.stringify(currentSlide.value.elements)) : []

View File

@ -10,7 +10,7 @@
<draggable <draggable
class="thumbnail-list" class="thumbnail-list"
:modelValue="slides" :modelValue="slides"
:animation="80" :animation="300"
:scroll="true" :scroll="true"
:scrollSensitivity="50" :scrollSensitivity="50"
@end="handleDragEnd" @end="handleDragEnd"

View File

@ -1,8 +1,9 @@
import { computed, onMounted, onUnmounted } from 'vue' import { computed, onMounted, onUnmounted } from 'vue'
import { useStore } from 'vuex' import { useStore } from 'vuex'
import { State } from '@/store' import { State } from '@/store'
import { decrypt } from '@/utils/crypto'
import { getImageDataURL } from '@/utils/image' import { getImageDataURL } from '@/utils/image'
import usePasteTextClipboardData from '@/hooks/usePasteTextClipboardData'
import useCreateElement from '@/hooks/useCreateElement'
export default () => { export default () => {
const store = useStore<State>() const store = useStore<State>()
@ -10,21 +11,11 @@ export default () => {
const thumbnailsFocus = computed(() => store.state.thumbnailsFocus) const thumbnailsFocus = computed(() => store.state.thumbnailsFocus)
const disableHotkeys = computed(() => store.state.disableHotkeys) const disableHotkeys = computed(() => store.state.disableHotkeys)
const pasteImageFile = (imageFile: File) => { const { pasteTextClipboardData } = usePasteTextClipboardData()
getImageDataURL(imageFile).then(dataURL => { const { createImageElement } = useCreateElement()
console.log(dataURL)
})
}
const pasteText = (text: string) => { const pasteImageFile = (imageFile: File) => {
let content getImageDataURL(imageFile).then(dataURL => createImageElement(dataURL))
try {
content = JSON.parse(decrypt(text))
}
catch {
content = text
}
console.log(content)
} }
const pasteListener = (e: ClipboardEvent) => { const pasteListener = (e: ClipboardEvent) => {
@ -47,7 +38,7 @@ export default () => {
} }
if( clipboardDataFirstItem.kind === 'string' && clipboardDataFirstItem.type === 'text/plain' ) { if( clipboardDataFirstItem.kind === 'string' && clipboardDataFirstItem.type === 'text/plain' ) {
clipboardDataFirstItem.getAsString(text => pasteText(text)) clipboardDataFirstItem.getAsString(text => pasteTextClipboardData(text))
} }
} }