mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
update
This commit is contained in:
parent
fdbb6dde16
commit
5f20da11b3
348
package-lock.json
generated
348
package-lock.json
generated
@ -2258,6 +2258,122 @@
|
||||
"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": {
|
||||
@ -2421,6 +2537,17 @@
|
||||
"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",
|
||||
@ -2483,6 +2610,18 @@
|
||||
"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",
|
||||
@ -2531,6 +2670,18 @@
|
||||
"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",
|
||||
@ -7322,122 +7473,6 @@
|
||||
"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",
|
||||
@ -15883,87 +15918,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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",
|
||||
|
@ -1,23 +1,24 @@
|
||||
import { Ref, computed } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { State, MutationTypes } from '@/store'
|
||||
import { PPTElement } from '@/types/slides'
|
||||
import { PPTElement, Slide } from '@/types/slides'
|
||||
import { ElementAlignCommand, ElementAlignCommands } from '@/types/edit'
|
||||
import { getElementListRange } from '../utils/elementRange'
|
||||
import { getElementListRange } from '@/utils/element'
|
||||
import { VIEWPORT_SIZE, VIEWPORT_ASPECT_RATIO } from '@/configs/canvas'
|
||||
|
||||
export default (elementList: Ref<PPTElement[]>) => {
|
||||
export default () => {
|
||||
const store = useStore<State>()
|
||||
|
||||
const activeElementIdList = computed(() => store.state.activeElementIdList)
|
||||
const activeElementList: Ref<PPTElement[]> = computed(() => store.getters.activeElementList)
|
||||
const currentSlide: Ref<Slide> = computed(() => store.getters.currentSlide)
|
||||
|
||||
const alignElementToCanvas = (command: ElementAlignCommand) => {
|
||||
const viewportWidth = VIEWPORT_SIZE
|
||||
const viewportHeight = VIEWPORT_SIZE * VIEWPORT_ASPECT_RATIO
|
||||
const { minX, maxX, minY, maxY } = getElementListRange(activeElementList.value)
|
||||
|
||||
const newElementList: PPTElement[] = JSON.parse(JSON.stringify(elementList.value))
|
||||
const newElementList: PPTElement[] = JSON.parse(JSON.stringify(currentSlide.value.elements))
|
||||
for(const element of newElementList) {
|
||||
if(!activeElementIdList.value.includes(element.elId)) continue
|
||||
|
@ -1,19 +1,20 @@
|
||||
import { Ref, computed } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { State, MutationTypes } from '@/store'
|
||||
import { PPTElement } from '@/types/slides'
|
||||
import { PPTElement, Slide } from '@/types/slides'
|
||||
import { createRandomCode } from '@/utils/common'
|
||||
|
||||
export default (elementList: Ref<PPTElement[]>) => {
|
||||
export default () => {
|
||||
const store = useStore<State>()
|
||||
const activeElementIdList = computed(() => store.state.activeElementIdList)
|
||||
const activeElementList: Ref<PPTElement[]> = computed(() => store.getters.activeElementList)
|
||||
const currentSlide: Ref<Slide> = computed(() => store.getters.currentSlide)
|
||||
|
||||
// 组合元素(为当前所有激活元素添加一个相同的groupId)
|
||||
const combineElements = () => {
|
||||
if(!activeElementList.value.length) return
|
||||
|
||||
let newElementList: PPTElement[] = JSON.parse(JSON.stringify(elementList))
|
||||
let newElementList: PPTElement[] = JSON.parse(JSON.stringify(currentSlide.value.elements))
|
||||
const groupId = createRandomCode()
|
||||
|
||||
const combineElementList: PPTElement[] = []
|
||||
@ -41,7 +42,7 @@ export default (elementList: Ref<PPTElement[]>) => {
|
||||
const hasElementInGroup = activeElementList.value.some(item => item.groupId)
|
||||
if(!hasElementInGroup) return
|
||||
|
||||
const newElementList: PPTElement[] = JSON.parse(JSON.stringify(elementList))
|
||||
const newElementList: PPTElement[] = JSON.parse(JSON.stringify(currentSlide.value.elements))
|
||||
for(const element of newElementList) {
|
||||
if(activeElementIdList.value.includes(element.elId) && element.groupId) delete element.groupId
|
||||
}
|
@ -6,13 +6,15 @@ import { copyText, readClipboard } from '@/utils/clipboard'
|
||||
import { encrypt } from '@/utils/crypto'
|
||||
import { message } from 'ant-design-vue'
|
||||
import usePasteTextClipboardData from '@/hooks/usePasteTextClipboardData'
|
||||
import useDeleteElement from './useDeleteElement'
|
||||
|
||||
export default (deleteElement: () => void) => {
|
||||
export default () => {
|
||||
const store = useStore<State>()
|
||||
const activeElementIdList = computed(() => store.state.activeElementIdList)
|
||||
const activeElementList: Ref<PPTElement[]> = computed(() => store.getters.activeElementList)
|
||||
|
||||
const { pasteTextClipboardData } = usePasteTextClipboardData()
|
||||
const { deleteElement } = useDeleteElement()
|
||||
|
||||
const copyElement = () => {
|
||||
if(!activeElementIdList.value.length) return
|
||||
@ -24,7 +26,6 @@ export default (deleteElement: () => void) => {
|
||||
|
||||
copyText(text).then(() => {
|
||||
store.commit(MutationTypes.SET_EDITORAREA_FOCUS, true)
|
||||
message.success('元素已复制到剪贴板', 0.8)
|
||||
})
|
||||
}
|
||||
|
@ -1,21 +1,22 @@
|
||||
import { Ref, computed } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { State, MutationTypes } from '@/store'
|
||||
import { PPTElement } from '@/types/slides'
|
||||
import { Slide } from '@/types/slides'
|
||||
|
||||
export default (elementList: Ref<PPTElement[]>) => {
|
||||
export default () => {
|
||||
const store = useStore<State>()
|
||||
const activeElementIdList = computed(() => store.state.activeElementIdList)
|
||||
const currentSlide: Ref<Slide> = computed(() => store.getters.currentSlide)
|
||||
|
||||
const deleteElement = () => {
|
||||
if(!activeElementIdList.value.length) return
|
||||
const newElementList = elementList.value.filter(el => !activeElementIdList.value.includes(el.elId))
|
||||
const newElementList = currentSlide.value.elements.filter(el => !activeElementIdList.value.includes(el.elId))
|
||||
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, [])
|
||||
store.commit(MutationTypes.UPDATE_SLIDE, { elements: newElementList })
|
||||
}
|
||||
|
||||
const deleteAllElements = () => {
|
||||
if(!elementList.value.length) return
|
||||
if(!currentSlide.value.elements.length) return
|
||||
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, [])
|
||||
store.commit(MutationTypes.UPDATE_SLIDE, { elements: [] })
|
||||
}
|
@ -1,23 +1,24 @@
|
||||
import { useStore } from 'vuex'
|
||||
import { Ref, computed } from 'vue'
|
||||
import { State, MutationTypes } from '@/store'
|
||||
import { PPTElement } from '@/types/slides'
|
||||
import { PPTElement, Slide } from '@/types/slides'
|
||||
|
||||
export default (elementList: Ref<PPTElement[]>) => {
|
||||
export default () => {
|
||||
const store = useStore<State>()
|
||||
const activeElementIdList = computed(() => store.state.activeElementIdList)
|
||||
const currentSlide: Ref<Slide> = computed(() => store.getters.currentSlide)
|
||||
|
||||
const lockElement = (handleElement: PPTElement) => {
|
||||
const newElementList: PPTElement[] = JSON.parse(JSON.stringify(elementList.value))
|
||||
const lockElement = () => {
|
||||
const newElementList: PPTElement[] = JSON.parse(JSON.stringify(currentSlide.value.elements))
|
||||
|
||||
for(const element of newElementList) {
|
||||
if(activeElementIdList.value.includes(handleElement.elId)) element.isLock = true
|
||||
if(activeElementIdList.value.includes(element.elId)) element.isLock = true
|
||||
}
|
||||
store.commit(MutationTypes.UPDATE_SLIDE, { elements: newElementList })
|
||||
}
|
||||
|
||||
const unlockElement = (handleElement: PPTElement) => {
|
||||
const newElementList: PPTElement[] = JSON.parse(JSON.stringify(elementList.value))
|
||||
const newElementList: PPTElement[] = JSON.parse(JSON.stringify(currentSlide.value.elements))
|
||||
|
||||
if(handleElement.groupId) {
|
||||
for(const element of newElementList) {
|
@ -1,15 +1,16 @@
|
||||
import { Ref, computed } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { State, MutationTypes } from '@/store'
|
||||
import { PPTElement } from '@/types/slides'
|
||||
import { Slide } from '@/types/slides'
|
||||
import { KEYS } from '@/configs/hotkey'
|
||||
|
||||
export default (elementList: Ref<PPTElement[]>) => {
|
||||
export default () => {
|
||||
const store = useStore<State>()
|
||||
const activeElementIdList = computed(() => store.state.activeElementIdList)
|
||||
const currentSlide: Ref<Slide> = computed(() => store.getters.currentSlide)
|
||||
|
||||
const moveElement = (command: string) => {
|
||||
const newElementList = elementList.value.map(el => {
|
||||
const newElementList = currentSlide.value.elements.map(el => {
|
||||
if(activeElementIdList.value.includes(el.elId)) {
|
||||
let { left, top } = el
|
||||
switch(command) {
|
@ -1,11 +1,12 @@
|
||||
import { Ref, computed } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { Ref } from 'vue'
|
||||
import { State, MutationTypes } from '@/store'
|
||||
import { PPTElement } from '@/types/slides'
|
||||
import { PPTElement, Slide } from '@/types/slides'
|
||||
import { ElementOrderCommand, ElementOrderCommands } from '@/types/edit'
|
||||
|
||||
export default (elementList: Ref<PPTElement[]>) => {
|
||||
export default () => {
|
||||
const store = useStore<State>()
|
||||
const currentSlide: Ref<Slide> = computed(() => store.getters.currentSlide)
|
||||
|
||||
// 获取组合元素层级范围(组合成员中的最大层级和最小层级)
|
||||
const getCombineElementIndexRange = (elementList: PPTElement[], combineElementList: PPTElement[]) => {
|
||||
@ -167,10 +168,10 @@ export default (elementList: Ref<PPTElement[]>) => {
|
||||
const orderElement = (element: PPTElement, command: ElementOrderCommand) => {
|
||||
let newElementList = null
|
||||
|
||||
if(command === ElementOrderCommands.UP) newElementList = moveUpElement(elementList.value, element)
|
||||
else if(command === ElementOrderCommands.DOWN) newElementList = moveDownElement(elementList.value, element)
|
||||
else if(command === ElementOrderCommands.TOP) newElementList = moveTopElement(elementList.value, element)
|
||||
else if(command === ElementOrderCommands.BOTTOM) newElementList = moveBottomElement(elementList.value, element)
|
||||
if(command === ElementOrderCommands.UP) newElementList = moveUpElement(currentSlide.value.elements, element)
|
||||
else if(command === ElementOrderCommands.DOWN) newElementList = moveDownElement(currentSlide.value.elements, element)
|
||||
else if(command === ElementOrderCommands.TOP) newElementList = moveTopElement(currentSlide.value.elements, element)
|
||||
else if(command === ElementOrderCommands.BOTTOM) newElementList = moveBottomElement(currentSlide.value.elements, element)
|
||||
|
||||
store.commit(MutationTypes.UPDATE_SLIDE, { elements: newElementList })
|
||||
}
|
@ -5,6 +5,11 @@ import { decrypt } from '@/utils/crypto'
|
||||
import { PPTElement, Slide } from '@/types/slides'
|
||||
import { createRandomCode } from '@/utils/common'
|
||||
|
||||
interface PasteTextClipboardDataOptions {
|
||||
onlySlide?: boolean;
|
||||
onlyElements?: boolean;
|
||||
}
|
||||
|
||||
export default () => {
|
||||
const store = useStore<State>()
|
||||
const currentSlide: Ref<Slide> = computed(() => store.getters.currentSlide)
|
||||
@ -37,15 +42,18 @@ export default () => {
|
||||
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, Object.values(elIdMap))
|
||||
}
|
||||
|
||||
const pasteSlide = (slides: Slide[]) => {
|
||||
console.log(slides)
|
||||
const pasteSlide = (slide: Slide) => {
|
||||
store.commit(MutationTypes.ADD_SLIDE, slide)
|
||||
}
|
||||
|
||||
const pasteText = (text: string) => {
|
||||
console.log(text)
|
||||
}
|
||||
|
||||
const pasteTextClipboardData = (text: string) => {
|
||||
const pasteTextClipboardData = (text: string, options?: PasteTextClipboardDataOptions) => {
|
||||
const onlySlide = options?.onlySlide || false
|
||||
const onlyElements = options?.onlyElements || false
|
||||
|
||||
let clipboardData
|
||||
try {
|
||||
clipboardData = JSON.parse(decrypt(text))
|
||||
@ -58,12 +66,12 @@ export default () => {
|
||||
if(typeof clipboardData === 'object') {
|
||||
const { type, data } = clipboardData
|
||||
|
||||
if(type === 'elements') pasteElement(data)
|
||||
else if(type === 'slide') pasteSlide(data)
|
||||
if(type === 'elements' && !onlySlide) pasteElement(data)
|
||||
else if(type === 'slide' && !onlyElements) pasteSlide(data)
|
||||
}
|
||||
|
||||
// 粘贴普通文本
|
||||
else pasteText(clipboardData)
|
||||
else if(!onlyElements && !onlySlide) pasteText(clipboardData)
|
||||
}
|
||||
|
||||
return {
|
||||
|
19
src/hooks/useSelectAllElement.ts
Normal file
19
src/hooks/useSelectAllElement.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { Ref, computed } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { State, MutationTypes } from '@/store'
|
||||
import { Slide } from '@/types/slides'
|
||||
|
||||
export default () => {
|
||||
const store = useStore<State>()
|
||||
const currentSlide: Ref<Slide> = computed(() => store.getters.currentSlide)
|
||||
|
||||
const selectAllElement = () => {
|
||||
const unlockedElements = currentSlide.value.elements.filter(el => !el.isLock)
|
||||
const newActiveElementIdList = unlockedElements.map(el => el.elId)
|
||||
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, newActiveElementIdList)
|
||||
}
|
||||
|
||||
return {
|
||||
selectAllElement,
|
||||
}
|
||||
}
|
78
src/hooks/useSlideHandler.ts
Normal file
78
src/hooks/useSlideHandler.ts
Normal file
@ -0,0 +1,78 @@
|
||||
import { Ref, computed } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { State, MutationTypes } from '@/store'
|
||||
import { Slide } from '@/types/slides'
|
||||
import { createRandomCode } from '@/utils/common'
|
||||
import { copyText, readClipboard } from '@/utils/clipboard'
|
||||
import { encrypt } from '@/utils/crypto'
|
||||
import { KEYS } from '@/configs/hotkey'
|
||||
import { message } from 'ant-design-vue'
|
||||
import usePasteTextClipboardData from '@/hooks/usePasteTextClipboardData'
|
||||
|
||||
export default () => {
|
||||
const store = useStore<State>()
|
||||
const slideIndex = computed(() => store.state.slideIndex)
|
||||
const slidesLength = computed(() => store.state.slides.length)
|
||||
const currentSlide: Ref<Slide> = computed(() => store.getters.currentSlide)
|
||||
|
||||
const { pasteTextClipboardData } = usePasteTextClipboardData()
|
||||
|
||||
const updateSlideIndex = (command: string) => {
|
||||
let targetIndex = 0
|
||||
if(command === KEYS.UP && slideIndex.value > 0) {
|
||||
targetIndex = slideIndex.value - 1
|
||||
}
|
||||
else if(command === KEYS.DOWN && slideIndex.value < slidesLength.value - 1) {
|
||||
targetIndex = slideIndex.value + 1
|
||||
}
|
||||
store.commit(MutationTypes.UPDATE_SLIDE_INDEX, targetIndex)
|
||||
}
|
||||
|
||||
const copySlide = () => {
|
||||
const text = encrypt(JSON.stringify({
|
||||
type: 'slide',
|
||||
data: currentSlide.value,
|
||||
}))
|
||||
|
||||
copyText(text).then(() => {
|
||||
store.commit(MutationTypes.SET_THUMBNAILS_FOCUS, true)
|
||||
})
|
||||
}
|
||||
|
||||
const pasteSlide = () => {
|
||||
readClipboard().then(text => {
|
||||
pasteTextClipboardData(text, { onlySlide: true })
|
||||
}).catch(err => message.warning(err))
|
||||
}
|
||||
|
||||
const createSlide = () => {
|
||||
const emptySlide = {
|
||||
id: createRandomCode(8),
|
||||
elements: [],
|
||||
}
|
||||
store.commit(MutationTypes.ADD_SLIDE, emptySlide)
|
||||
}
|
||||
|
||||
const copyAndPasteSlide = () => {
|
||||
store.commit(MutationTypes.ADD_SLIDE, currentSlide.value)
|
||||
}
|
||||
|
||||
const deleteSlide = () => {
|
||||
store.commit(MutationTypes.DELETE_SLIDE, currentSlide.value.id)
|
||||
}
|
||||
|
||||
const cutSlide = () => {
|
||||
copySlide()
|
||||
deleteSlide()
|
||||
}
|
||||
|
||||
return {
|
||||
updateSlideIndex,
|
||||
copySlide,
|
||||
pasteSlide,
|
||||
createSlide,
|
||||
copyAndPasteSlide,
|
||||
deleteSlide,
|
||||
cutSlide,
|
||||
}
|
||||
}
|
@ -29,7 +29,7 @@ export interface State {
|
||||
const state: State = {
|
||||
activeElementIdList: [],
|
||||
handleElementId: '',
|
||||
editorAreaShowScale: 85,
|
||||
editorAreaShowScale: 90,
|
||||
thumbnailsFocus: false,
|
||||
editorAreaFocus: false,
|
||||
disableHotkeys: false,
|
||||
|
@ -56,11 +56,9 @@ export const mutations: MutationTree<State> = {
|
||||
state.slides = slides
|
||||
},
|
||||
|
||||
[MutationTypes.ADD_SLIDE](state, data: AddSlideData) {
|
||||
const { index, slide } = data
|
||||
const slides = Array.isArray(slide) ? slide : [slide]
|
||||
const addIndex = index !== undefined ? index : (state.slideIndex + 1)
|
||||
state.slides.splice(addIndex, 0, ...slides)
|
||||
[MutationTypes.ADD_SLIDE](state, slide: Slide) {
|
||||
const addIndex = state.slideIndex + 1
|
||||
state.slides.splice(addIndex, 0, slide)
|
||||
state.slideIndex = addIndex
|
||||
},
|
||||
|
||||
|
@ -52,4 +52,22 @@ export enum OPERATE_KEYS {
|
||||
LEFT_BOTTOM = 6,
|
||||
BOTTOM = 7,
|
||||
RIGHT_BOTTOM = 8,
|
||||
}
|
||||
|
||||
export interface AlignmentLineAxis {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export interface AlignmentLineProps {
|
||||
type: 'vertical' | 'horizontal';
|
||||
axis: AlignmentLineAxis;
|
||||
length: number;
|
||||
}
|
||||
|
||||
export interface MultiSelectRange {
|
||||
minX: number;
|
||||
maxX: number;
|
||||
minY: number;
|
||||
maxY: number;
|
||||
}
|
@ -1,93 +1,116 @@
|
||||
import { PPTElement } from '@/types/slides'
|
||||
|
||||
// 获取矩形旋转后在画布中的位置范围
|
||||
interface RotatedElementData {
|
||||
left: number;
|
||||
top: number;
|
||||
width: number;
|
||||
height: number;
|
||||
rotate: number;
|
||||
}
|
||||
export const getRectRotatedRange = (element: RotatedElementData) => {
|
||||
const { left, top, width, height, rotate = 0 } = element
|
||||
|
||||
const radius = Math.sqrt( Math.pow(width, 2) + Math.pow(height, 2) ) / 2
|
||||
const auxiliaryAngle = Math.atan(height / width) * 180 / Math.PI
|
||||
|
||||
const tlbraRadian = (180 - rotate - auxiliaryAngle) * Math.PI / 180
|
||||
const trblaRadian = (auxiliaryAngle - rotate) * Math.PI / 180
|
||||
|
||||
const halfWidth = width / 2
|
||||
const halfHeight = height / 2
|
||||
|
||||
const middleLeft = left + halfWidth
|
||||
const middleTop = top + halfHeight
|
||||
|
||||
const xAxis = [
|
||||
middleLeft + radius * Math.cos(tlbraRadian),
|
||||
middleLeft + radius * Math.cos(trblaRadian),
|
||||
middleLeft - radius * Math.cos(tlbraRadian),
|
||||
middleLeft - radius * Math.cos(trblaRadian),
|
||||
]
|
||||
const yAxis = [
|
||||
middleTop - radius * Math.sin(tlbraRadian),
|
||||
middleTop - radius * Math.sin(trblaRadian),
|
||||
middleTop + radius * Math.sin(tlbraRadian),
|
||||
middleTop + radius * Math.sin(trblaRadian),
|
||||
]
|
||||
|
||||
return {
|
||||
xRange: [Math.min(...xAxis), Math.max(...xAxis)],
|
||||
yRange: [Math.min(...yAxis), Math.max(...yAxis)],
|
||||
}
|
||||
}
|
||||
|
||||
// 获取元素在画布中的位置范围
|
||||
export const getElementRange = (element: PPTElement) => {
|
||||
let minX, maxX, minY, maxY
|
||||
|
||||
if(element.type === 'line') {
|
||||
minX = element.left
|
||||
maxX = element.left + Math.max(element.start[0], element.end[0])
|
||||
minY = element.top
|
||||
maxY = element.top + Math.max(element.start[1], element.end[1])
|
||||
}
|
||||
else if('rotate' in element && element.rotate) {
|
||||
const { left, top, width, height, rotate } = element
|
||||
const { xRange, yRange } = getRectRotatedRange({ left, top, width, height, rotate })
|
||||
minX = xRange[0]
|
||||
maxX = xRange[1]
|
||||
minY = yRange[0]
|
||||
maxY = yRange[1]
|
||||
}
|
||||
else {
|
||||
minX = element.left
|
||||
maxX = element.left + element.width
|
||||
minY = element.top
|
||||
maxY = element.top + element.height
|
||||
}
|
||||
return { minX, maxX, minY, maxY }
|
||||
}
|
||||
|
||||
// 获取元素集合在画布中的位置范围
|
||||
export const getElementListRange = (elementList: PPTElement[]) => {
|
||||
const leftValues: number[] = []
|
||||
const topValues: number[] = []
|
||||
const rightValues: number[] = []
|
||||
const bottomValues: number[] = []
|
||||
|
||||
elementList.forEach(element => {
|
||||
const { minX, maxX, minY, maxY } = getElementRange(element)
|
||||
leftValues.push(minX)
|
||||
topValues.push(minY)
|
||||
rightValues.push(maxX)
|
||||
bottomValues.push(maxY)
|
||||
})
|
||||
|
||||
const minX = Math.min(...leftValues)
|
||||
const maxX = Math.max(...rightValues)
|
||||
const minY = Math.min(...topValues)
|
||||
const maxY = Math.max(...bottomValues)
|
||||
|
||||
return { minX, maxX, minY, maxY }
|
||||
import { PPTElement } from '@/types/slides'
|
||||
|
||||
// 获取矩形旋转后在画布中的位置范围
|
||||
interface RotatedElementData {
|
||||
left: number;
|
||||
top: number;
|
||||
width: number;
|
||||
height: number;
|
||||
rotate: number;
|
||||
}
|
||||
export const getRectRotatedRange = (element: RotatedElementData) => {
|
||||
const { left, top, width, height, rotate = 0 } = element
|
||||
|
||||
const radius = Math.sqrt( Math.pow(width, 2) + Math.pow(height, 2) ) / 2
|
||||
const auxiliaryAngle = Math.atan(height / width) * 180 / Math.PI
|
||||
|
||||
const tlbraRadian = (180 - rotate - auxiliaryAngle) * Math.PI / 180
|
||||
const trblaRadian = (auxiliaryAngle - rotate) * Math.PI / 180
|
||||
|
||||
const halfWidth = width / 2
|
||||
const halfHeight = height / 2
|
||||
|
||||
const middleLeft = left + halfWidth
|
||||
const middleTop = top + halfHeight
|
||||
|
||||
const xAxis = [
|
||||
middleLeft + radius * Math.cos(tlbraRadian),
|
||||
middleLeft + radius * Math.cos(trblaRadian),
|
||||
middleLeft - radius * Math.cos(tlbraRadian),
|
||||
middleLeft - radius * Math.cos(trblaRadian),
|
||||
]
|
||||
const yAxis = [
|
||||
middleTop - radius * Math.sin(tlbraRadian),
|
||||
middleTop - radius * Math.sin(trblaRadian),
|
||||
middleTop + radius * Math.sin(tlbraRadian),
|
||||
middleTop + radius * Math.sin(trblaRadian),
|
||||
]
|
||||
|
||||
return {
|
||||
xRange: [Math.min(...xAxis), Math.max(...xAxis)],
|
||||
yRange: [Math.min(...yAxis), Math.max(...yAxis)],
|
||||
}
|
||||
}
|
||||
|
||||
// 获取元素在画布中的位置范围
|
||||
export const getElementRange = (element: PPTElement) => {
|
||||
let minX, maxX, minY, maxY
|
||||
|
||||
if(element.type === 'line') {
|
||||
minX = element.left
|
||||
maxX = element.left + Math.max(element.start[0], element.end[0])
|
||||
minY = element.top
|
||||
maxY = element.top + Math.max(element.start[1], element.end[1])
|
||||
}
|
||||
else if('rotate' in element && element.rotate) {
|
||||
const { left, top, width, height, rotate } = element
|
||||
const { xRange, yRange } = getRectRotatedRange({ left, top, width, height, rotate })
|
||||
minX = xRange[0]
|
||||
maxX = xRange[1]
|
||||
minY = yRange[0]
|
||||
maxY = yRange[1]
|
||||
}
|
||||
else {
|
||||
minX = element.left
|
||||
maxX = element.left + element.width
|
||||
minY = element.top
|
||||
maxY = element.top + element.height
|
||||
}
|
||||
return { minX, maxX, minY, maxY }
|
||||
}
|
||||
|
||||
// 获取元素集合在画布中的位置范围
|
||||
export const getElementListRange = (elementList: PPTElement[]) => {
|
||||
const leftValues: number[] = []
|
||||
const topValues: number[] = []
|
||||
const rightValues: number[] = []
|
||||
const bottomValues: number[] = []
|
||||
|
||||
elementList.forEach(element => {
|
||||
const { minX, maxX, minY, maxY } = getElementRange(element)
|
||||
leftValues.push(minX)
|
||||
topValues.push(minY)
|
||||
rightValues.push(maxX)
|
||||
bottomValues.push(maxY)
|
||||
})
|
||||
|
||||
const minX = Math.min(...leftValues)
|
||||
const maxX = Math.max(...rightValues)
|
||||
const minY = Math.min(...topValues)
|
||||
const maxY = Math.max(...bottomValues)
|
||||
|
||||
return { minX, maxX, minY, maxY }
|
||||
}
|
||||
|
||||
export interface AlignLine {
|
||||
value: number;
|
||||
range: [number, number];
|
||||
}
|
||||
|
||||
// 对齐参考线去重,对于相同位置的多条参考线,取长度范围的最小值和最大值,并基于此范围将多条参考线合并为一条
|
||||
export const uniqAlignLines = (lines: AlignLine[]) => {
|
||||
const uniqLines: AlignLine[] = []
|
||||
lines.forEach(line => {
|
||||
const index = uniqLines.findIndex(_line => _line.value === line.value)
|
||||
if(index === -1) uniqLines.push(line)
|
||||
else {
|
||||
const uniqLine = uniqLines[index]
|
||||
const rangeMin = Math.min(uniqLine.range[0], line.range[0])
|
||||
const rangeMax = Math.max(uniqLine.range[1], line.range[1])
|
||||
const range: [number, number] = [rangeMin, rangeMax]
|
||||
const _line = { value: line.value, range }
|
||||
uniqLines[index] = _line
|
||||
}
|
||||
})
|
||||
return uniqLines
|
||||
}
|
@ -1,5 +1,9 @@
|
||||
import mitt, { Emitter } from 'mitt'
|
||||
|
||||
export enum EMITTER_EVENTS {
|
||||
|
||||
}
|
||||
|
||||
const emitter: Emitter = mitt()
|
||||
|
||||
export default emitter
|
@ -15,7 +15,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { PropType } from 'vue'
|
||||
import { AlignmentLineAxis } from './types/index'
|
||||
import { AlignmentLineAxis } from '@/types/edit'
|
||||
|
||||
export default {
|
||||
name: 'alignment-line',
|
||||
|
@ -26,14 +26,12 @@ import { computed, defineComponent, reactive, PropType, watchEffect, toRefs } fr
|
||||
import { useStore } from 'vuex'
|
||||
import { State } from '@/store'
|
||||
import { PPTElement, ElementTypes } from '@/types/slides'
|
||||
import { getElementListRange } from './utils/elementRange'
|
||||
import { ElementScaleHandler, OperateResizablePointTypes, OperateBorderLineTypes, OPERATE_KEYS } from '@/types/edit'
|
||||
import { getElementListRange } from '@/utils/element'
|
||||
import { ElementScaleHandler, OperateResizablePointTypes, OperateBorderLineTypes, MultiSelectRange, OPERATE_KEYS } from '@/types/edit'
|
||||
|
||||
import ResizablePoint from '@/views/_common/_operate/ResizablePoint.vue'
|
||||
import BorderLine from '@/views/_common/_operate/BorderLine.vue'
|
||||
|
||||
import { MultiSelectRange } from './types/index'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'multi-select-operate',
|
||||
components: {
|
||||
|
@ -2,10 +2,9 @@ import { Ref, computed } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { State, MutationTypes } from '@/store'
|
||||
import { ElementTypes, PPTElement } from '@/types/slides'
|
||||
import { AlignmentLineProps } from '../types/index'
|
||||
import { AlignmentLineProps } from '@/types/edit'
|
||||
import { VIEWPORT_SIZE, VIEWPORT_ASPECT_RATIO } from '@/configs/canvas'
|
||||
import { getRectRotatedRange } from '../utils/elementRange'
|
||||
import { AlignLine, uniqAlignLines } from '../utils/alignLines'
|
||||
import { getRectRotatedRange, AlignLine, uniqAlignLines } from '@/utils/element'
|
||||
|
||||
export default (
|
||||
elementList: Ref<PPTElement[]>,
|
||||
|
@ -2,7 +2,7 @@ import { Ref, reactive } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { State, MutationTypes } from '@/store'
|
||||
import { PPTElement } from '@/types/slides'
|
||||
import { getElementRange } from '../utils/elementRange'
|
||||
import { getElementRange } from '@/utils/element'
|
||||
|
||||
export default (elementList: Ref<PPTElement[]>, viewportRef: Ref<HTMLElement | null>, canvasScale: Ref<number>) => {
|
||||
const store = useStore<State>()
|
||||
|
@ -2,10 +2,9 @@ import { computed, Ref } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { State, MutationTypes } from '@/store'
|
||||
import { ElementTypes, PPTElement, PPTImageElement, PPTLineElement, PPTShapeElement } from '@/types/slides'
|
||||
import { OPERATE_KEYS, ElementScaleHandler } from '@/types/edit'
|
||||
import { OPERATE_KEYS, ElementScaleHandler, AlignmentLineProps, MultiSelectRange } from '@/types/edit'
|
||||
import { VIEWPORT_SIZE, VIEWPORT_ASPECT_RATIO } from '@/configs/canvas'
|
||||
import { AlignLine, uniqAlignLines } from '../utils/alignLines'
|
||||
import { AlignmentLineProps, MultiSelectRange } from '../types/index'
|
||||
import { AlignLine, uniqAlignLines } from '@/utils/element'
|
||||
|
||||
// 计算元素被旋转一定角度后,八个操作点的新坐标
|
||||
interface RotateElementData {
|
||||
|
@ -56,15 +56,6 @@
|
||||
:selectElement="selectElement"
|
||||
:rotateElement="rotateElement"
|
||||
:scaleElement="scaleElement"
|
||||
:orderElement="orderElement"
|
||||
:combineElements="combineElements"
|
||||
:uncombineElements="uncombineElements"
|
||||
:alignElementToCanvas="alignElementToCanvas"
|
||||
:deleteElement="deleteElement"
|
||||
:lockElement="lockElement"
|
||||
:unlockElement="unlockElement"
|
||||
:copyElement="copyElement"
|
||||
:cutElement="cutElement"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -76,21 +67,19 @@ import { useStore } from 'vuex'
|
||||
import { State, MutationTypes } from '@/store'
|
||||
import { ContextmenuItem } from '@/components/Contextmenu/types'
|
||||
import { PPTElement, Slide } from '@/types/slides'
|
||||
import { AlignmentLineProps } from './types/index'
|
||||
import { AlignmentLineProps } from '@/types/edit'
|
||||
|
||||
import useViewportSize from './hooks/useViewportSize'
|
||||
import useLockElement from './hooks/useLockElement'
|
||||
import useDeleteElement from './hooks/useDeleteElement'
|
||||
import useCombineElement from './hooks/useCombineElement'
|
||||
import useOrderElement from './hooks/useOrderElement'
|
||||
import useAlignElementToCanvas from './hooks/useAlignElementToCanvas'
|
||||
import useCopyAndPasteElement from './hooks/useCopyAndPasteElement'
|
||||
import useMouseSelection from './hooks/useMouseSelection'
|
||||
import useDropImageElement from './hooks/useDropImageElement'
|
||||
import useRotateElement from './hooks/useRotateElement'
|
||||
import useScaleElement from './hooks/useScaleElement'
|
||||
import useSelectElement from './hooks/useSelectElement'
|
||||
import useDragElement from './hooks/useDragElement'
|
||||
import useMouseSelection from './hooks/useMouseSelection'
|
||||
import useDropImageElement from './hooks/useDropImageElement'
|
||||
|
||||
import useDeleteElement from '@/hooks/useDeleteElement'
|
||||
import useCopyAndPasteElement from '@/hooks/useCopyAndPasteElement'
|
||||
import useSelectAllElement from '@/hooks/useSelectAllElement'
|
||||
|
||||
import EditableElement from '@/views/_common/_element/EditableElement.vue'
|
||||
import MouseSelection from './MouseSelection.vue'
|
||||
@ -137,15 +126,13 @@ export default defineComponent({
|
||||
const { mouseSelectionState, updateMouseSelection } = useMouseSelection(elementList, viewportRef, canvasScale)
|
||||
|
||||
const { dragElement } = useDragElement(elementList, activeGroupElementId, canvasScale, alignmentLines)
|
||||
const { selectElement, selectAllElement } = useSelectElement(elementList, activeGroupElementId, dragElement)
|
||||
const { selectElement } = useSelectElement(elementList, activeGroupElementId, dragElement)
|
||||
const { scaleElement, scaleMultiElement } = useScaleElement(elementList, canvasScale, activeGroupElementId, alignmentLines)
|
||||
const { rotateElement } = useRotateElement(elementList, viewportRef, canvasScale)
|
||||
const { orderElement } = useOrderElement(elementList)
|
||||
const { alignElementToCanvas } = useAlignElementToCanvas(elementList)
|
||||
const { combineElements, uncombineElements } = useCombineElement(elementList)
|
||||
const { deleteElement, deleteAllElements } = useDeleteElement(elementList)
|
||||
const { lockElement, unlockElement } = useLockElement(elementList)
|
||||
const { copyElement, cutElement, pasteElement } = useCopyAndPasteElement(deleteElement)
|
||||
const { rotateElement } = useRotateElement(elementList, viewportRef, canvasScale)
|
||||
|
||||
const { selectAllElement } = useSelectAllElement()
|
||||
const { deleteAllElements } = useDeleteElement()
|
||||
const { pasteElement } = useCopyAndPasteElement()
|
||||
|
||||
const handleClickBlankArea = (e: MouseEvent) => {
|
||||
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, [])
|
||||
@ -195,15 +182,6 @@ export default defineComponent({
|
||||
rotateElement,
|
||||
scaleElement,
|
||||
scaleMultiElement,
|
||||
orderElement,
|
||||
combineElements,
|
||||
uncombineElements,
|
||||
alignElementToCanvas,
|
||||
deleteElement,
|
||||
lockElement,
|
||||
unlockElement,
|
||||
copyElement,
|
||||
cutElement,
|
||||
contextmenus,
|
||||
}
|
||||
},
|
||||
|
@ -1,17 +0,0 @@
|
||||
export interface AlignmentLineAxis {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export interface AlignmentLineProps {
|
||||
type: 'vertical' | 'horizontal';
|
||||
axis: AlignmentLineAxis;
|
||||
length: number;
|
||||
}
|
||||
|
||||
export interface MultiSelectRange {
|
||||
minX: number;
|
||||
maxX: number;
|
||||
minY: number;
|
||||
maxY: number;
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
export interface AlignLine {
|
||||
value: number;
|
||||
range: [number, number];
|
||||
}
|
||||
|
||||
// 对齐参考线去重,对于相同位置的多条参考线,取长度范围的最小值和最大值,并基于此范围将多条参考线合并为一条
|
||||
export const uniqAlignLines = (lines: AlignLine[]) => {
|
||||
const uniqLines: AlignLine[] = []
|
||||
lines.forEach(line => {
|
||||
const index = uniqLines.findIndex(_line => _line.value === line.value)
|
||||
if(index === -1) uniqLines.push(line)
|
||||
else {
|
||||
const uniqLine = uniqLines[index]
|
||||
const rangeMin = Math.min(uniqLine.range[0], line.range[0])
|
||||
const rangeMax = Math.max(uniqLine.range[1], line.range[1])
|
||||
const range: [number, number] = [rangeMin, rangeMax]
|
||||
const _line = { value: line.value, range }
|
||||
uniqLines[index] = _line
|
||||
}
|
||||
})
|
||||
return uniqLines
|
||||
}
|
@ -23,7 +23,7 @@
|
||||
@mousedown="changSlideIndex(index)"
|
||||
v-contextmenu="contextmenus"
|
||||
>
|
||||
<div class="slide-index">{{ fillDigit(index + 1) }}</div>
|
||||
<div class="slide-index">{{ fillDigit(index + 1, 2) }}</div>
|
||||
<div class="thumbnail"></div>
|
||||
</div>
|
||||
</template>
|
||||
@ -38,6 +38,7 @@ import { useStore } from 'vuex'
|
||||
import { State, MutationTypes } from '@/store'
|
||||
import { fillDigit } from '@/utils/common'
|
||||
import { ContextmenuItem } from '@/components/Contextmenu/types'
|
||||
import useSlideHandler from '@/hooks/useSlideHandler'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'thumbnails',
|
||||
@ -49,6 +50,15 @@ export default defineComponent({
|
||||
const slides = computed(() => store.state.slides)
|
||||
const slideIndex = computed(() => store.state.slideIndex)
|
||||
|
||||
const {
|
||||
copySlide,
|
||||
pasteSlide,
|
||||
createSlide,
|
||||
copyAndPasteSlide,
|
||||
deleteSlide,
|
||||
cutSlide,
|
||||
} = useSlideHandler()
|
||||
|
||||
const changSlideIndex = (index: number) => {
|
||||
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, [])
|
||||
|
||||
@ -75,25 +85,6 @@ export default defineComponent({
|
||||
store.commit(MutationTypes.UPDATE_SLIDE_INDEX, newIndex)
|
||||
}
|
||||
|
||||
const cutSlide = () => {
|
||||
console.log('cutSlide')
|
||||
}
|
||||
const copySlide = () => {
|
||||
console.log('copySlide')
|
||||
}
|
||||
const pasteSlide = () => {
|
||||
console.log('pasteSlide')
|
||||
}
|
||||
const createSlide = () => {
|
||||
console.log('createSlide')
|
||||
}
|
||||
const copyAndPasteSlide = () => {
|
||||
console.log('copyAndPasteSlide')
|
||||
}
|
||||
const deleteSlide = () => {
|
||||
console.log('deleteSlide')
|
||||
}
|
||||
|
||||
const contextmenus = (): ContextmenuItem[] => {
|
||||
return [
|
||||
{
|
||||
|
@ -2,50 +2,98 @@ import { computed, onMounted, onUnmounted } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { State, MutationTypes } from '@/store'
|
||||
import { KEYS } from '@/configs/hotkey'
|
||||
|
||||
import { message } from 'ant-design-vue'
|
||||
|
||||
import useSlideHandler from '@/hooks/useSlideHandler'
|
||||
import useLockElement from '@/hooks/useLockElement'
|
||||
import useDeleteElement from '@/hooks/useDeleteElement'
|
||||
import useCombineElement from '@/hooks/useCombineElement'
|
||||
import useCopyAndPasteElement from '@/hooks/useCopyAndPasteElement'
|
||||
import useSelectAllElement from '@/hooks/useSelectAllElement'
|
||||
import useMoveElement from '@/hooks/useMoveElement'
|
||||
|
||||
export default () => {
|
||||
const store = useStore<State>()
|
||||
|
||||
const ctrlKeyActive = computed(() => store.state.ctrlKeyState)
|
||||
const shiftKeyActive = computed(() => store.state.shiftKeyState)
|
||||
const disableHotkeys = computed(() => store.state.disableHotkeys)
|
||||
const activeElementIdList = computed(() => store.state.activeElementIdList)
|
||||
|
||||
const editorAreaFocus = computed(() => store.state.editorAreaFocus)
|
||||
const thumbnailsFocus = computed(() => store.state.thumbnailsFocus)
|
||||
|
||||
const {
|
||||
updateSlideIndex,
|
||||
copySlide,
|
||||
createSlide,
|
||||
deleteSlide,
|
||||
cutSlide,
|
||||
} = useSlideHandler()
|
||||
|
||||
const { combineElements, uncombineElements } = useCombineElement()
|
||||
const { deleteElement } = useDeleteElement()
|
||||
const { lockElement } = useLockElement()
|
||||
const { copyElement, cutElement } = useCopyAndPasteElement()
|
||||
const { selectAllElement } = useSelectAllElement()
|
||||
const { moveElement } = useMoveElement()
|
||||
|
||||
const copy = () => {
|
||||
message.success('copy')
|
||||
if(disableHotkeys.value) return
|
||||
if(thumbnailsFocus.value) copySlide()
|
||||
else if(activeElementIdList.value.length) copyElement()
|
||||
}
|
||||
|
||||
const cut = () => {
|
||||
message.success('cut')
|
||||
if(disableHotkeys.value) return
|
||||
if(thumbnailsFocus.value) cutSlide()
|
||||
else if(activeElementIdList.value.length) cutElement()
|
||||
}
|
||||
|
||||
const undo = () => {
|
||||
message.success('undo')
|
||||
}
|
||||
|
||||
const redo = () => {
|
||||
message.success('redo')
|
||||
}
|
||||
|
||||
const selectAll = () => {
|
||||
message.success('selectAll')
|
||||
if(!editorAreaFocus.value && disableHotkeys.value) return
|
||||
selectAllElement()
|
||||
}
|
||||
|
||||
const lock = () => {
|
||||
message.success('lock')
|
||||
if(!editorAreaFocus.value && disableHotkeys.value) return
|
||||
lockElement()
|
||||
}
|
||||
const combine = () => {
|
||||
message.success('combine')
|
||||
if(!editorAreaFocus.value && disableHotkeys.value) return
|
||||
combineElements()
|
||||
}
|
||||
|
||||
const uncombine = () => {
|
||||
message.success('uncombine')
|
||||
if(!editorAreaFocus.value && disableHotkeys.value) return
|
||||
uncombineElements()
|
||||
}
|
||||
|
||||
const remove = () => {
|
||||
message.success('remove')
|
||||
if(disableHotkeys.value) return
|
||||
if(thumbnailsFocus.value) deleteSlide()
|
||||
else if(activeElementIdList.value.length) deleteElement()
|
||||
}
|
||||
|
||||
const move = (key: string) => {
|
||||
message.success(`move: ${key}`)
|
||||
if(disableHotkeys.value) return
|
||||
if(thumbnailsFocus.value && (key === KEYS.UP || key === KEYS.DOWN)) {
|
||||
updateSlideIndex(key)
|
||||
}
|
||||
else if(activeElementIdList.value.length) moveElement(key)
|
||||
}
|
||||
|
||||
const create = () => {
|
||||
message.success('create')
|
||||
if(!thumbnailsFocus.value || disableHotkeys.value) return
|
||||
createSlide()
|
||||
}
|
||||
|
||||
const keydownListener = (e: KeyboardEvent) => {
|
||||
|
@ -27,13 +27,14 @@ import { computed, defineComponent, PropType } from 'vue'
|
||||
import { PPTElement, PPTTextElement, PPTImageElement, PPTShapeElement, PPTLineElement } from '@/types/slides'
|
||||
import { ContextmenuItem } from '@/components/Contextmenu/types'
|
||||
|
||||
import {
|
||||
ElementOrderCommand,
|
||||
ElementOrderCommands,
|
||||
ElementAlignCommand,
|
||||
ElementAlignCommands,
|
||||
ElementScaleHandler,
|
||||
} from '@/types/edit'
|
||||
import useLockElement from '@/hooks/useLockElement'
|
||||
import useDeleteElement from '@/hooks/useDeleteElement'
|
||||
import useCombineElement from '@/hooks/useCombineElement'
|
||||
import useOrderElement from '@/hooks/useOrderElement'
|
||||
import useAlignElementToCanvas from '@/hooks/useAlignElementToCanvas'
|
||||
import useCopyAndPasteElement from '@/hooks/useCopyAndPasteElement'
|
||||
|
||||
import { ElementOrderCommands, ElementAlignCommands, ElementScaleHandler } from '@/types/edit'
|
||||
|
||||
import ImageElement from './ImageElement/index.vue'
|
||||
import TextElement from './TextElement/index.vue'
|
||||
@ -85,42 +86,6 @@ export default defineComponent({
|
||||
type: Function as PropType<(e: MouseEvent, element: Exclude<PPTElement, PPTLineElement>, command: ElementScaleHandler) => void>,
|
||||
required: true,
|
||||
},
|
||||
orderElement: {
|
||||
type: Function as PropType<(element: PPTElement, command: ElementOrderCommand) => void>,
|
||||
required: true,
|
||||
},
|
||||
combineElements: {
|
||||
type: Function as PropType<() => void>,
|
||||
required: true,
|
||||
},
|
||||
uncombineElements: {
|
||||
type: Function as PropType<() => void>,
|
||||
required: true,
|
||||
},
|
||||
alignElementToCanvas: {
|
||||
type: Function as PropType<(command: ElementAlignCommand) => void>,
|
||||
required: true,
|
||||
},
|
||||
deleteElement: {
|
||||
type: Function as PropType<() => void>,
|
||||
required: true,
|
||||
},
|
||||
lockElement: {
|
||||
type: Function as PropType<(element: PPTElement) => void>,
|
||||
required: true,
|
||||
},
|
||||
unlockElement: {
|
||||
type: Function as PropType<(element: PPTElement) => void>,
|
||||
required: true,
|
||||
},
|
||||
copyElement: {
|
||||
type: Function as PropType<() => void>,
|
||||
required: true,
|
||||
},
|
||||
cutElement: {
|
||||
type: Function as PropType<() => void>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const currentElementComponent = computed(() => {
|
||||
@ -131,12 +96,19 @@ export default defineComponent({
|
||||
return elementTypeMap[props.elementInfo.type] || null
|
||||
})
|
||||
|
||||
const { orderElement } = useOrderElement()
|
||||
const { alignElementToCanvas } = useAlignElementToCanvas()
|
||||
const { combineElements, uncombineElements } = useCombineElement()
|
||||
const { deleteElement } = useDeleteElement()
|
||||
const { lockElement, unlockElement } = useLockElement()
|
||||
const { copyElement, cutElement } = useCopyAndPasteElement()
|
||||
|
||||
const contextmenus = (): ContextmenuItem[] => {
|
||||
if(props.elementInfo.isLock) {
|
||||
return [{
|
||||
text: '解锁',
|
||||
icon: 'icon-unlock',
|
||||
handler: () => props.unlockElement(props.elementInfo),
|
||||
handler: () => unlockElement(props.elementInfo),
|
||||
}]
|
||||
}
|
||||
|
||||
@ -145,13 +117,13 @@ export default defineComponent({
|
||||
text: '剪切',
|
||||
subText: 'Ctrl + X',
|
||||
icon: 'icon-scissor',
|
||||
handler: props.cutElement,
|
||||
handler: cutElement,
|
||||
},
|
||||
{
|
||||
text: '复制',
|
||||
subText: 'Ctrl + C',
|
||||
icon: 'icon-copy',
|
||||
handler: props.copyElement,
|
||||
handler: copyElement,
|
||||
},
|
||||
{ divider: true },
|
||||
{
|
||||
@ -159,29 +131,29 @@ export default defineComponent({
|
||||
icon: 'icon-top-layer',
|
||||
disable: props.isMultiSelect && !props.elementInfo.groupId,
|
||||
children: [
|
||||
{ text: '置顶层', handler: () => props.orderElement(props.elementInfo, ElementOrderCommands.TOP) },
|
||||
{ text: '置底层', handler: () => props.orderElement(props.elementInfo, ElementOrderCommands.BOTTOM) },
|
||||
{ text: '置顶层', handler: () => orderElement(props.elementInfo, ElementOrderCommands.TOP) },
|
||||
{ text: '置底层', handler: () => orderElement(props.elementInfo, ElementOrderCommands.BOTTOM) },
|
||||
{ divider: true },
|
||||
{ text: '上移一层', handler: () => props.orderElement(props.elementInfo, ElementOrderCommands.UP) },
|
||||
{ text: '下移一层', handler: () => props.orderElement(props.elementInfo, ElementOrderCommands.DOWN) },
|
||||
{ text: '上移一层', handler: () => orderElement(props.elementInfo, ElementOrderCommands.UP) },
|
||||
{ text: '下移一层', handler: () => orderElement(props.elementInfo, ElementOrderCommands.DOWN) },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '水平对齐',
|
||||
icon: 'icon-align-left',
|
||||
children: [
|
||||
{ text: '水平居中', handler: () => props.alignElementToCanvas(ElementAlignCommands.HORIZONTAL) },
|
||||
{ text: '左对齐', handler: () => props.alignElementToCanvas(ElementAlignCommands.LEFT) },
|
||||
{ text: '右对齐', handler: () => props.alignElementToCanvas(ElementAlignCommands.RIGHT) },
|
||||
{ text: '水平居中', handler: () => alignElementToCanvas(ElementAlignCommands.HORIZONTAL) },
|
||||
{ text: '左对齐', handler: () => alignElementToCanvas(ElementAlignCommands.LEFT) },
|
||||
{ text: '右对齐', handler: () => alignElementToCanvas(ElementAlignCommands.RIGHT) },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '垂直对齐',
|
||||
icon: 'icon-align-bottom',
|
||||
children: [
|
||||
{ text: '垂直居中', handler: () => props.alignElementToCanvas(ElementAlignCommands.VERTICAL) },
|
||||
{ text: '上对齐', handler: () => props.alignElementToCanvas(ElementAlignCommands.TOP) },
|
||||
{ text: '下对齐', handler: () => props.alignElementToCanvas(ElementAlignCommands.BOTTOM) },
|
||||
{ text: '垂直居中', handler: () => alignElementToCanvas(ElementAlignCommands.VERTICAL) },
|
||||
{ text: '上对齐', handler: () => alignElementToCanvas(ElementAlignCommands.TOP) },
|
||||
{ text: '下对齐', handler: () => alignElementToCanvas(ElementAlignCommands.BOTTOM) },
|
||||
],
|
||||
},
|
||||
{ divider: true },
|
||||
@ -189,20 +161,20 @@ export default defineComponent({
|
||||
text: props.elementInfo.groupId ? '取消组合' : '组合',
|
||||
subText: 'Ctrl + G',
|
||||
icon: 'icon-block',
|
||||
handler: props.elementInfo.groupId ? props.uncombineElements : props.combineElements,
|
||||
handler: props.elementInfo.groupId ? uncombineElements : combineElements,
|
||||
hide: !props.isMultiSelect,
|
||||
},
|
||||
{
|
||||
text: '锁定',
|
||||
subText: 'Ctrl + L',
|
||||
icon: 'icon-lock',
|
||||
handler: () => props.lockElement(props.elementInfo),
|
||||
handler: lockElement,
|
||||
},
|
||||
{
|
||||
text: '删除',
|
||||
subText: 'Delete',
|
||||
icon: 'icon-delete',
|
||||
handler: props.deleteElement,
|
||||
handler: deleteElement,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user