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",
"version": "0.1.0",
"name": "hamster_ppt",
"version": "0.0.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -2258,122 +2258,6 @@
"tslint": "^5.20.1",
"webpack": "^4.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": {
@ -2537,17 +2421,6 @@
"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": {
"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",
@ -2610,18 +2483,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": {
"version": "0.6.1",
"resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz",
@ -2670,18 +2531,6 @@
"integrity": "sha1-tkb2m+OULavOzJ1mOcgNwQXvqmY=",
"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": {
"version": "6.2.0",
"resolved": "https://registry.npm.taobao.org/wrap-ansi/download/wrap-ansi-6.2.0.tgz",
@ -7473,6 +7322,122 @@
"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": {
"version": "2.3.3",
"resolved": "https://registry.npm.taobao.org/form-data/download/form-data-2.3.3.tgz",
@ -10796,6 +10761,11 @@
"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": {
"version": "1.3.2",
"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": {
"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",

View File

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

View File

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

View File

@ -1,131 +1,145 @@
import { createRandomCode } from '@/utils/common'
import { getImageSize } from '@/utils/image'
import { VIEWPORT_SIZE, VIEWPORT_ASPECT_RATIO } from '@/configs/canvas'
import { TableElementCell } from '@/types/slides'
import {
DEFAULT_IMAGE,
DEFAULT_TEXT,
DEFAULT_SHAPE,
DEFAULT_LINE,
DEFAULT_CHART,
DEFAULT_TABLE,
} from '@/configs/element'
interface CommonElementPosition {
top: number;
left: number;
width: number;
height: number;
}
interface LineElementPosition {
top: number;
left: number;
start: [number, number];
end: [number, number];
}
export default () => {
const insertImage = (imgUrl: string) => {
getImageSize(imgUrl).then(({ width, height }) => {
const scale = width / height
if(scale < VIEWPORT_ASPECT_RATIO && width > VIEWPORT_SIZE) {
width = VIEWPORT_SIZE
height = width * scale
}
else if(height > VIEWPORT_SIZE * VIEWPORT_ASPECT_RATIO) {
height = VIEWPORT_SIZE * VIEWPORT_ASPECT_RATIO
width = height / scale
}
return {
...DEFAULT_IMAGE,
elId: createRandomCode(),
imgUrl,
width,
height,
}
})
}
const insertChart = (chartType: string, data: Object) => {
return {
...DEFAULT_CHART,
elId: createRandomCode(),
chartType,
data,
}
}
const insertTable = (rowCount: number, colCount: number) => {
const row: TableElementCell[] = new Array(colCount).fill({ colspan: 1, rowspan: 1, content: '' })
const data: TableElementCell[][] = new Array(rowCount).fill(row)
const DEFAULT_CELL_WIDTH = 80
const DEFAULT_CELL_HEIGHT = 35
const DEFAULT_BORDER_WIDTH = 2
const colSizes: number[] = new Array(colCount).fill(DEFAULT_CELL_WIDTH)
const rowSizes: number[] = new Array(rowCount).fill(DEFAULT_CELL_HEIGHT)
return {
...DEFAULT_TABLE,
elId: createRandomCode(),
width: colCount * DEFAULT_CELL_WIDTH + DEFAULT_BORDER_WIDTH,
height: rowCount * DEFAULT_CELL_HEIGHT + DEFAULT_BORDER_WIDTH,
colSizes,
rowSizes,
data,
}
}
const insertText = (position: CommonElementPosition) => {
const { left, top, width, height } = position
return {
...DEFAULT_TEXT,
elId: createRandomCode(),
left,
top,
width,
height,
}
}
const insertShape = (position: CommonElementPosition, svgCode: string) => {
const { left, top, width, height } = position
return {
...DEFAULT_SHAPE,
elId: createRandomCode(),
left,
top,
width,
height,
svgCode,
}
}
const insertLine = (position: LineElementPosition, marker: [string, string], lineType: string) => {
const { left, top, start, end } = position
return {
...DEFAULT_LINE,
elId: createRandomCode(),
left,
top,
start,
end,
marker,
lineType,
}
}
return {
insertImage,
insertChart,
insertTable,
insertText,
insertShape,
insertLine,
}
import { useStore } from 'vuex'
import { MutationTypes } from '@/store'
import { createRandomCode } from '@/utils/common'
import { getImageSize } from '@/utils/image'
import { VIEWPORT_SIZE, VIEWPORT_ASPECT_RATIO } from '@/configs/canvas'
import { PPTElement, TableElementCell } from '@/types/slides'
import {
DEFAULT_IMAGE,
DEFAULT_TEXT,
DEFAULT_SHAPE,
DEFAULT_LINE,
DEFAULT_CHART,
DEFAULT_TABLE,
} from '@/configs/element'
interface CommonElementPosition {
top: number;
left: number;
width: number;
height: number;
}
interface LineElementPosition {
top: number;
left: number;
start: [number, number];
end: [number, number];
}
export default () => {
const store = useStore()
const createElement = (element: PPTElement) => {
store.commit(MutationTypes.ADD_ELEMENT, element)
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, [element.elId])
}
const createImageElement = (imgUrl: string) => {
getImageSize(imgUrl).then(({ width, height }) => {
const scale = width / height
if(scale < VIEWPORT_ASPECT_RATIO && width > VIEWPORT_SIZE) {
width = VIEWPORT_SIZE
height = width * scale
}
else if(height > VIEWPORT_SIZE * VIEWPORT_ASPECT_RATIO) {
height = VIEWPORT_SIZE * VIEWPORT_ASPECT_RATIO
width = height / scale
}
createElement({
...DEFAULT_IMAGE,
type: 'image',
elId: createRandomCode(),
imgUrl,
width,
height,
})
})
}
const createChartElement = (chartType: string, data: string) => {
createElement({
...DEFAULT_CHART,
type: 'chart',
elId: createRandomCode(),
chartType,
data,
})
}
const createTableElement = (rowCount: number, colCount: number) => {
const row: TableElementCell[] = new Array(colCount).fill({ colspan: 1, rowspan: 1, content: '' })
const data: TableElementCell[][] = new Array(rowCount).fill(row)
const DEFAULT_CELL_WIDTH = 80
const DEFAULT_CELL_HEIGHT = 35
const DEFAULT_BORDER_WIDTH = 2
const colSizes: number[] = new Array(colCount).fill(DEFAULT_CELL_WIDTH)
const rowSizes: number[] = new Array(rowCount).fill(DEFAULT_CELL_HEIGHT)
createElement({
...DEFAULT_TABLE,
type: 'table',
elId: createRandomCode(),
width: colCount * DEFAULT_CELL_WIDTH + DEFAULT_BORDER_WIDTH,
height: rowCount * DEFAULT_CELL_HEIGHT + DEFAULT_BORDER_WIDTH,
colSizes,
rowSizes,
data,
})
}
const createTextElement = (position: CommonElementPosition) => {
const { left, top, width, height } = position
createElement({
...DEFAULT_TEXT,
type: 'text',
elId: createRandomCode(),
left,
top,
width,
height,
})
}
const createShapeElement = (position: CommonElementPosition, svgCode: string) => {
const { left, top, width, height } = position
createElement({
...DEFAULT_SHAPE,
type: 'shape',
elId: createRandomCode(),
left,
top,
width,
height,
svgCode,
})
}
const createLineElement = (position: LineElementPosition, marker: [string, string], lineType: string) => {
const { left, top, start, end } = position
createElement({
...DEFAULT_LINE,
type: 'line',
elId: createRandomCode(),
left,
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 {
elId: string;
isLock: boolean;
left: number;
top: number;
isLock?: boolean;
groupId?: string;
}
@ -78,8 +78,8 @@ export interface PPTLineElement extends PPTElementBaseProps {
export interface PPTChartElement extends PPTElementBaseProps, PPTElementSizeProps, PPTElementBorderProps {
type: 'chart';
chartType: string;
theme: string;
data: string;
theme?: string;
}
export interface TableElementCell {
@ -90,8 +90,8 @@ export interface TableElementCell {
}
export interface PPTTableElement extends PPTElementBaseProps, PPTElementSizeProps {
type: 'table';
borderTheme: string;
theme: string;
borderTheme?: string;
theme?: string;
rowSizes: number[];
colSizes: number[];
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 { PPTElement } from '@/types/slides'
import { copyText, readClipboard } from '@/utils/clipboard'
import { encrypt, decrypt } from '@/utils/crypto'
import { encrypt } from '@/utils/crypto'
import { message } from 'ant-design-vue'
import usePasteTextClipboardData from '@/hooks/usePasteTextClipboardData'
export default (deleteElement: () => void) => {
const store = useStore<State>()
const activeElementIdList = computed(() => store.state.activeElementIdList)
const activeElementList: Ref<PPTElement[]> = computed(() => store.getters.activeElementList)
const { pasteTextClipboardData } = usePasteTextClipboardData()
const copyElement = () => {
if(!activeElementIdList.value.length) return
@ -32,14 +35,7 @@ export default (deleteElement: () => void) => {
const pasteElement = () => {
readClipboard().then(text => {
let clipboardData
try {
clipboardData = JSON.parse(decrypt(text))
}
catch {
clipboardData = text
}
console.log(clipboardData)
pasteTextClipboardData(text)
}).catch(err => message.warning(err))
}

View File

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

View File

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

View File

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

View File

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