From 8c64ea798ab005a1e7e1ac97b4ef4c00602d6794 Mon Sep 17 00:00:00 2001 From: parisma Date: Wed, 3 Aug 2022 20:01:26 +0800 Subject: [PATCH] =?UTF-8?q?feat(editor):=20=E5=A4=9A=E9=80=89=E7=B2=98?= =?UTF-8?q?=E8=B4=B4=E5=90=8E=E5=90=8C=E6=AD=A5=E9=80=89=E4=B8=AD=E7=B2=98?= =?UTF-8?q?=E8=B4=B4=E7=9A=84=E5=A4=9A=E4=B8=AA=E5=85=83=E7=B4=A0,?= =?UTF-8?q?=E5=B9=B6=E6=94=AF=E6=8C=81=E6=8B=96=E6=8B=BD,=E7=B2=98?= =?UTF-8?q?=E8=B4=B4=E5=88=A0=E9=99=A4=E6=94=AF=E6=8C=81=E5=A4=9A=E4=B8=AA?= =?UTF-8?q?=E5=85=83=E7=B4=A0=E5=90=8C=E6=97=B6=E6=92=A4=E9=94=80=E5=88=B0?= =?UTF-8?q?=E4=B8=8A=E4=B8=80=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/editor/src/services/editor.ts | 111 ++++++++++++--------- packages/editor/src/utils/operator.ts | 44 ++++++-- packages/stage/src/StageCore.ts | 13 ++- packages/stage/src/StageMultiDragResize.ts | 10 +- packages/stage/src/types.ts | 1 + 5 files changed, 115 insertions(+), 64 deletions(-) diff --git a/packages/editor/src/services/editor.ts b/packages/editor/src/services/editor.ts index aae5d2e6..5c4b07c8 100644 --- a/packages/editor/src/services/editor.ts +++ b/packages/editor/src/services/editor.ts @@ -42,9 +42,7 @@ import { beforeAdd, beforePaste, beforeRemove, notifyAddToStage } from '@editor/ import BaseService from './BaseService'; class Editor extends BaseService { - private isHistoryStateChange = false; - - private state = reactive({ + public state = reactive({ root: null, page: null, parent: null, @@ -55,6 +53,7 @@ class Editor extends BaseService { modifiedNodeIds: new Map(), pageLength: 0, }); + private isHistoryStateChange = false; constructor() { super( @@ -288,7 +287,28 @@ class Editor extends BaseService { * @returns 添加后的节点 */ public async multiAdd(configs: MNode[]): Promise { - return await Promise.all(configs.map((configItem) => this.add(configItem as AddMNode))); + const stage = this.get('stage'); + const newNodes: MNode[] = await Promise.all( + configs.map(async (configItem: MNode): Promise => { + // 新增元素到配置 + const { parentNode, newNode, layout } = await beforeAdd(configItem as AddMNode); + // 将新增元素事件通知到stage以更新渲染 + await notifyAddToStage(parentNode, newNode, layout); + return newNode; + }), + ); + const newNodeIds: Id[] = newNodes.map((node) => node.id); + + // 增加历史记录 多选不可能选中page + this.addModifiedNodeId(newNodeIds.join('-')); + this.pushHistoryState(); + + // 触发选中样式 + stage?.multiSelect(newNodeIds); + + this.emit('multiAdd', newNodes); + + return newNodes; } /** @@ -299,10 +319,11 @@ class Editor extends BaseService { */ public async add(addNode: AddMNode, parent?: MContainer | null): Promise { const stage = this.get('stage'); + // 新增元素到配置 const { parentNode, newNode, layout, isPage } = await beforeAdd(addNode, parent); // 将新增元素事件通知到stage以更新渲染 await notifyAddToStage(parentNode, newNode, layout); - // 触发选中样式 + // 更新编辑器选中元素 await this.select(newNode); // 增加历史记录 this.addModifiedNodeId(newNode.id); @@ -327,9 +348,42 @@ class Editor extends BaseService { * @param {Object} node * @return {Object} 删除的组件配置 */ - public async remove(nodes: MNode | MNode[]): Promise<(MNode | void)[]> { - const removeNodes = Array.isArray(nodes) ? nodes : [nodes]; - return await Promise.all(removeNodes.map(async (node) => await this.doRemove(node))); + public async remove(nodeOrNodeList: MNode | MNode[]): Promise { + if (Array.isArray(nodeOrNodeList)) { + // 多选批量删除 + const nodes = nodeOrNodeList; + return this.multiRemove(nodes); + } + const node = nodeOrNodeList; + const removeParent = await beforeRemove(node); + // 删除的是页面 + if (!removeParent) return node; + // 更新历史记录 + this.addModifiedNodeId(removeParent.id); + this.pushHistoryState(); + + this.emit('remove', node); + + return node; + } + + /** + * 批量删除 + * @param nodes 批量删除的节点 + * @returns 批量删除的节点 + */ + public async multiRemove(nodes: MNode[]): Promise { + await Promise.all( + nodes.map(async (removeNode) => { + await beforeRemove(removeNode); + }), + ); + const nodeIds = nodes.map((node) => node.id); + this.addModifiedNodeId(nodeIds.join('-')); + this.pushHistoryState(); + + this.emit('multiRemove', nodes); + return nodes; } /** @@ -683,47 +737,6 @@ class Editor extends BaseService { page, }; } - - private async doRemove(node: MNode): Promise { - const beforeRemoveRes = beforeRemove(node); - if (!beforeRemoveRes) return; - const { parent, root } = beforeRemoveRes; - - const stage = this.get('stage'); - stage?.remove({ id: node.id, root: cloneDeep(this.get('root')) }); - - if (node.type === NodeType.PAGE) { - this.state.pageLength -= 1; - - if (root.items[0]) { - await this.select(root.items[0]); - stage?.select(root.items[0].id); - } else { - this.set('node', null); - this.set('nodes', []); - this.set('parent', null); - this.set('page', null); - this.set('stage', null); - this.set('highlightNode', null); - this.resetModifiedNodeId(); - historyService.reset(); - - this.emit('remove', node); - - return node; - } - } else { - await this.select(parent); - stage?.select(parent.id); - } - - this.addModifiedNodeId(parent.id); - this.pushHistoryState(); - - this.emit('remove', node); - - return node; - } } export type EditorService = Editor; diff --git a/packages/editor/src/utils/operator.ts b/packages/editor/src/utils/operator.ts index df58292a..420cdd4c 100644 --- a/packages/editor/src/utils/operator.ts +++ b/packages/editor/src/utils/operator.ts @@ -1,18 +1,19 @@ import { toRaw } from 'vue'; -import { cloneDeep } from 'lodash-es'; +import { cloneDeep, isEmpty } from 'lodash-es'; import { Id, MApp, MContainer, MNode, NodeType } from '@tmagic/schema'; import StageCore from '@tmagic/stage'; import { isPage } from '@tmagic/utils'; import editorService from '@editor/services/editor'; +import historyService from '@editor/services/history'; import propsService from '@editor/services/props'; import { AddMNode, Layout, PastePosition } from '@editor/type'; import { fixNodeLeft, generatePageNameByApp, getInitPositionStyle, getNodeIndex } from '@editor/utils/editor'; /** * 粘贴前置操作:返回分配了新id以及校准了坐标的配置 - * @param position 粘贴的坐标,如果为空则默认在元素坐标基础上偏移10px + * @param position 粘贴的坐标 * @param config 待粘贴的元素配置(复制时保存的那份配置) * @returns */ @@ -25,7 +26,8 @@ export const beforePaste = async (position: PastePosition, config: MNode[]) => { const pasteConfigs: MNode[] = await Promise.all( config.map(async (configItem: MNode): Promise => { let pastePosition = position; - if (curNode.items) { + if (!isEmpty(pastePosition) && curNode.items) { + // 如果没有传入粘贴坐标则可能为键盘操作,不再转换 // 如果粘贴时选中了容器,则将元素粘贴到容器内,坐标需要转换为相对于容器的坐标 pastePosition = getPositionInContainer(pastePosition, curNode.id); } @@ -146,9 +148,10 @@ export const notifyAddToStage = async (parentNode: MContainer, newNode: MNode, l * @param node 待删除的节点 * @returns 父级元素,root根元素 */ -export const beforeRemove = (node: MNode): { parent: MContainer; root: MApp } | void => { +export const beforeRemove = async (node: MNode): Promise => { if (!node?.id) return; + const stage = editorService.get('stage'); const root = editorService.get('root'); if (!root) throw new Error('没有root'); @@ -162,8 +165,33 @@ export const beforeRemove = (node: MNode): { parent: MContainer; root: MApp } | if (typeof index !== 'number' || index === -1) throw new Error('找不要删除的节点'); // 从配置中删除元素 parent.items?.splice(index, 1); - return { - parent, - root, - }; + + // 通知stage更新 + stage?.remove({ id: node.id, root: cloneDeep(root) }); + + if (node.type === NodeType.PAGE) { + editorService.state.pageLength -= 1; + + if (root.items[0]) { + await editorService.select(root.items[0]); + stage?.select(root.items[0].id); + } else { + editorService.set('node', null); + editorService.set('nodes', []); + editorService.set('parent', null); + editorService.set('page', null); + editorService.set('stage', null); + editorService.set('highlightNode', null); + editorService.resetModifiedNodeId(); + historyService.reset(); + + editorService.emit('remove', node); + + return; + } + } else { + await editorService.select(parent); + stage?.select(parent.id); + } + return parent; }; diff --git a/packages/stage/src/StageCore.ts b/packages/stage/src/StageCore.ts index b2e7cdb6..fc48be83 100644 --- a/packages/stage/src/StageCore.ts +++ b/packages/stage/src/StageCore.ts @@ -128,8 +128,7 @@ export default class StageCore extends EventEmitter { } else { this.selectedDomList.push(el); } - this.multiDr.multiSelect(this.selectedDomList); - this.emit('multiSelect', this.selectedDomList); + this.multiSelect(this.selectedDomList); }); // 要先触发select,在触发update @@ -222,6 +221,16 @@ export default class StageCore extends EventEmitter { } } + /** + * 多选 + * @param domList 多选节点 + */ + public async multiSelect(idOrElList: HTMLElement[] | Id[]): Promise { + const elList = await Promise.all(idOrElList.map(async (idOrEl) => await this.getTargetElement(idOrEl))); + this.multiDr.multiSelect(elList); + this.emit('multiSelect', elList); + } + /** * 更新选中的节点 * @param data 更新的数据 diff --git a/packages/stage/src/StageMultiDragResize.ts b/packages/stage/src/StageMultiDragResize.ts index 27540ee2..d8cfe66e 100644 --- a/packages/stage/src/StageMultiDragResize.ts +++ b/packages/stage/src/StageMultiDragResize.ts @@ -70,7 +70,6 @@ export default class StageMultiDragResize extends EventEmitter { }); this.moveableForMulti?.destroy(); this.multiMoveableHelper?.clear(); - this.moveableForMulti = new Moveable( this.container, this.getOptions({ @@ -208,11 +207,12 @@ export default class StageMultiDragResize extends EventEmitter { * @return {MoveableOptions} moveable options参数 */ private getOptions(options: MoveableOptions = {}): MoveableOptions { - let { moveableOptions = {} } = this.core.config; + let { multiMoveableOptions = {} } = this.core.config; - if (typeof moveableOptions === 'function') { - moveableOptions = moveableOptions(this.core); + if (typeof multiMoveableOptions === 'function') { + multiMoveableOptions = multiMoveableOptions(this.core); } + return { defaultGroupRotate: 0, defaultGroupOrigin: '50% 50%', @@ -225,7 +225,7 @@ export default class StageMultiDragResize extends EventEmitter { origin: true, padding: { left: 0, top: 0, right: 0, bottom: 0 }, ...options, - ...moveableOptions, + ...multiMoveableOptions, }; } } diff --git a/packages/stage/src/types.ts b/packages/stage/src/types.ts index 48a12341..24c9740e 100644 --- a/packages/stage/src/types.ts +++ b/packages/stage/src/types.ts @@ -39,6 +39,7 @@ export type StageCoreConfig = { containerHighlightClassName: string; containerHighlightDuration: number; moveableOptions?: ((core?: StageCore) => MoveableOptions) | MoveableOptions; + multiMoveableOptions?: ((core?: StageCore) => MoveableOptions) | MoveableOptions; /** runtime 的HTML地址,可以是一个HTTP地址,如果和编辑器不同域,需要设置跨域,也可以是一个相对或绝对路径 */ runtimeUrl?: string; render?: (renderer: StageCore) => Promise | HTMLElement;