From fdbb6dde169467e67caee28a6b6d13bfec1557b8 Mon Sep 17 00:00:00 2001 From: pipipi-pikachu <1171051090@qq.com> Date: Mon, 21 Dec 2020 22:58:22 +0800 Subject: [PATCH] update --- package-lock.json | 357 ++++++++++-------- package.json | 1 + src/configs/element.ts | 6 - .../Canvas => }/hooks/useCreateElement.ts | 274 +++++++------- src/hooks/usePasteTextClipboardData.ts | 72 ++++ src/types/slides.ts | 8 +- src/utils/emitter.ts | 5 + .../Canvas/hooks/useCopyAndPasteElement.ts | 14 +- .../Canvas/hooks/useDropImageElement.ts | 7 +- src/views/Editor/Canvas/index.vue | 4 +- src/views/Editor/Thumbnails/index.vue | 2 +- src/views/Editor/usePasteEvent.ts | 23 +- 12 files changed, 449 insertions(+), 324 deletions(-) rename src/{views/Editor/Canvas => }/hooks/useCreateElement.ts (65%) create mode 100644 src/hooks/usePasteTextClipboardData.ts create mode 100644 src/utils/emitter.ts diff --git a/package-lock.json b/package-lock.json index ef5c678..00957ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index fbec147..b0c5045 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/configs/element.ts b/src/configs/element.ts index 1ff6af6..690a034 100644 --- a/src/configs/element.ts +++ b/src/configs/element.ts @@ -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, diff --git a/src/views/Editor/Canvas/hooks/useCreateElement.ts b/src/hooks/useCreateElement.ts similarity index 65% rename from src/views/Editor/Canvas/hooks/useCreateElement.ts rename to src/hooks/useCreateElement.ts index 849eea4..e46eadd 100644 --- a/src/views/Editor/Canvas/hooks/useCreateElement.ts +++ b/src/hooks/useCreateElement.ts @@ -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, + } } \ No newline at end of file diff --git a/src/hooks/usePasteTextClipboardData.ts b/src/hooks/usePasteTextClipboardData.ts new file mode 100644 index 0000000..a08d595 --- /dev/null +++ b/src/hooks/usePasteTextClipboardData.ts @@ -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() + const currentSlide: Ref = 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, + } +} \ No newline at end of file diff --git a/src/types/slides.ts b/src/types/slides.ts index 20a8590..6a8ea05 100644 --- a/src/types/slides.ts +++ b/src/types/slides.ts @@ -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[][]; diff --git a/src/utils/emitter.ts b/src/utils/emitter.ts new file mode 100644 index 0000000..c552ce4 --- /dev/null +++ b/src/utils/emitter.ts @@ -0,0 +1,5 @@ +import mitt, { Emitter } from 'mitt' + +const emitter: Emitter = mitt() + +export default emitter \ No newline at end of file diff --git a/src/views/Editor/Canvas/hooks/useCopyAndPasteElement.ts b/src/views/Editor/Canvas/hooks/useCopyAndPasteElement.ts index cb63374..4d43f11 100644 --- a/src/views/Editor/Canvas/hooks/useCopyAndPasteElement.ts +++ b/src/views/Editor/Canvas/hooks/useCopyAndPasteElement.ts @@ -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() const activeElementIdList = computed(() => store.state.activeElementIdList) const activeElementList: Ref = 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)) } diff --git a/src/views/Editor/Canvas/hooks/useDropImageElement.ts b/src/views/Editor/Canvas/hooks/useDropImageElement.ts index df968ac..be2a467 100644 --- a/src/views/Editor/Canvas/hooks/useDropImageElement.ts +++ b/src/views/Editor/Canvas/hooks/useDropImageElement.ts @@ -1,16 +1,17 @@ import { onMounted, onUnmounted, Ref } from 'vue' import { getImageDataURL } from '@/utils/image' +import useCreateElement from '@/hooks/useCreateElement' export default (elementRef: Ref) => { + 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)) } } } diff --git a/src/views/Editor/Canvas/index.vue b/src/views/Editor/Canvas/index.vue index f4a9dd9..453b590 100644 --- a/src/views/Editor/Canvas/index.vue +++ b/src/views/Editor/Canvas/index.vue @@ -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 = computed(() => store.getters.currentSlide) const elementList = ref([]) const setLocalElementList = () => { elementList.value = currentSlide.value ? JSON.parse(JSON.stringify(currentSlide.value.elements)) : [] diff --git a/src/views/Editor/Thumbnails/index.vue b/src/views/Editor/Thumbnails/index.vue index e1094b7..0847a41 100644 --- a/src/views/Editor/Thumbnails/index.vue +++ b/src/views/Editor/Thumbnails/index.vue @@ -10,7 +10,7 @@ { const store = useStore() @@ -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)) } }