@tmagic/editor 1.2.0-beta.9 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/style.css +62 -34
- package/dist/{tmagic-editor.mjs → tmagic-editor.js} +1641 -1266
- package/dist/tmagic-editor.js.map +1 -0
- package/dist/{tmagic-editor.umd.js → tmagic-editor.umd.cjs} +1649 -1270
- package/dist/tmagic-editor.umd.cjs.map +1 -0
- package/package.json +16 -14
- package/src/Editor.vue +66 -40
- package/src/components/CodeDraftEditor.vue +137 -0
- package/src/components/ContentMenu.vue +1 -1
- package/src/components/FunctionEditor.vue +190 -0
- package/src/components/Icon.vue +1 -1
- package/src/{layouts → components}/Layout.vue +54 -15
- package/src/components/ScrollBar.vue +1 -1
- package/src/components/ScrollViewer.vue +10 -1
- package/src/components/ToolButton.vue +1 -1
- package/src/fields/Code.vue +1 -1
- package/src/fields/CodeLink.vue +1 -1
- package/src/fields/CodeSelect.vue +102 -99
- package/src/fields/UISelect.vue +3 -3
- package/src/index.ts +2 -1
- package/src/layouts/AddPageBox.vue +5 -2
- package/src/layouts/CodeEditor.vue +2 -3
- package/src/layouts/Framework.vue +12 -10
- package/src/layouts/NavMenu.vue +11 -8
- package/src/layouts/PropsPanel.vue +12 -9
- package/src/layouts/Resizer.vue +1 -1
- package/src/layouts/sidebar/ComponentListPanel.vue +2 -3
- package/src/layouts/sidebar/LayerMenu.vue +9 -7
- package/src/layouts/sidebar/LayerPanel.vue +130 -178
- package/src/layouts/sidebar/Sidebar.vue +110 -26
- package/src/layouts/sidebar/code-block/CodeBlockEditor.vue +33 -118
- package/src/layouts/sidebar/code-block/CodeBlockList.vue +93 -126
- package/src/layouts/workspace/Breadcrumb.vue +6 -7
- package/src/layouts/workspace/PageBar.vue +3 -3
- package/src/layouts/workspace/PageBarScrollContainer.vue +5 -3
- package/src/layouts/workspace/Stage.vue +36 -18
- package/src/layouts/workspace/ViewerMenu.vue +16 -15
- package/src/layouts/workspace/Workspace.vue +9 -6
- package/src/services/BaseService.ts +11 -2
- package/src/services/codeBlock.ts +136 -146
- package/src/services/componentList.ts +7 -2
- package/src/services/editor.ts +161 -85
- package/src/services/events.ts +7 -2
- package/src/services/history.ts +8 -17
- package/src/services/props.ts +8 -3
- package/src/services/storage.ts +1 -0
- package/src/services/ui.ts +20 -10
- package/src/theme/code-block.scss +46 -23
- package/src/theme/component-list-panel.scss +8 -5
- package/src/type.ts +84 -63
- package/src/utils/config.ts +1 -1
- package/src/utils/editor.ts +17 -3
- package/src/utils/index.ts +2 -1
- package/src/utils/logger.ts +1 -1
- package/src/utils/operator.ts +12 -12
- package/src/utils/props.ts +3 -3
- package/src/utils/scroll-viewer.ts +18 -2
- package/src/utils/stage.ts +10 -9
- package/src/utils/undo-redo.ts +1 -1
- package/types/Editor.vue.d.ts +48 -28
- package/types/components/CodeDraftEditor.vue.d.ts +55 -0
- package/types/components/ContentMenu.vue.d.ts +11 -87
- package/types/components/FunctionEditor.vue.d.ts +60 -0
- package/types/components/Icon.vue.d.ts +4 -50
- package/types/{layouts → components}/Layout.vue.d.ts +12 -1
- package/types/components/ScrollBar.vue.d.ts +7 -65
- package/types/components/ScrollViewer.vue.d.ts +45 -1
- package/types/components/ToolButton.vue.d.ts +12 -78
- package/types/fields/Code.vue.d.ts +11 -77
- package/types/fields/CodeLink.vue.d.ts +12 -82
- package/types/fields/CodeSelect.vue.d.ts +13 -77
- package/types/fields/UISelect.vue.d.ts +7 -65
- package/types/index.d.ts +1 -0
- package/types/layouts/AddPageBox.vue.d.ts +1 -43
- package/types/layouts/CodeEditor.vue.d.ts +22 -130
- package/types/layouts/NavMenu.vue.d.ts +9 -69
- package/types/layouts/sidebar/LayerMenu.vue.d.ts +16 -48
- package/types/layouts/sidebar/LayerPanel.vue.d.ts +24 -7
- package/types/layouts/sidebar/Sidebar.vue.d.ts +10 -5
- package/types/layouts/sidebar/code-block/CodeBlockEditor.vue.d.ts +24 -6
- package/types/layouts/sidebar/code-block/CodeBlockList.vue.d.ts +11 -5
- package/types/layouts/workspace/Breadcrumb.vue.d.ts +1 -43
- package/types/layouts/workspace/Stage.vue.d.ts +15 -43
- package/types/layouts/workspace/ViewerMenu.vue.d.ts +11 -66
- package/types/layouts/workspace/Workspace.vue.d.ts +25 -8
- package/types/services/BaseService.d.ts +2 -1
- package/types/services/codeBlock.d.ts +56 -47
- package/types/services/componentList.d.ts +1 -0
- package/types/services/editor.d.ts +10 -11
- package/types/services/events.d.ts +1 -0
- package/types/services/history.d.ts +5 -15
- package/types/services/props.d.ts +3 -2
- package/types/services/ui.d.ts +24 -24
- package/types/type.d.ts +70 -59
- package/types/utils/editor.d.ts +1 -0
- package/types/utils/index.d.ts +1 -0
- package/types/utils/operator.d.ts +1 -1
- package/types/utils/props.d.ts +1 -1
- package/types/utils/scroll-viewer.d.ts +5 -0
- package/dist/tmagic-editor.mjs.map +0 -1
- package/dist/tmagic-editor.umd.js.map +0 -1
- package/src/layouts/sidebar/TabPane.vue +0 -118
- package/types/layouts/sidebar/TabPane.vue.d.ts +0 -82
package/src/services/editor.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
|
3
3
|
*
|
|
4
|
-
* Copyright (C)
|
|
4
|
+
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
|
5
5
|
*
|
|
6
6
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
7
|
* you may not use this file except in compliance with the License.
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { reactive, toRaw } from 'vue';
|
|
20
|
-
import { cloneDeep, mergeWith, uniq } from 'lodash-es';
|
|
20
|
+
import { cloneDeep, isObject, mergeWith, uniq } from 'lodash-es';
|
|
21
21
|
|
|
22
22
|
import type { CodeBlockDSL, Id, MApp, MComponent, MContainer, MNode, MPage } from '@tmagic/schema';
|
|
23
23
|
import { NodeType } from '@tmagic/schema';
|
|
@@ -25,9 +25,9 @@ import StageCore from '@tmagic/stage';
|
|
|
25
25
|
import { getNodePath, isNumber, isPage, isPop } from '@tmagic/utils';
|
|
26
26
|
|
|
27
27
|
import codeBlockService from '../services/codeBlock';
|
|
28
|
-
import historyService
|
|
28
|
+
import historyService from '../services/history';
|
|
29
29
|
import storageService, { Protocol } from '../services/storage';
|
|
30
|
-
import type { AddMNode, EditorNodeInfo, PastePosition, StoreState } from '../type';
|
|
30
|
+
import type { AddMNode, EditorNodeInfo, PastePosition, StepValue, StoreState, StoreStateKey } from '../type';
|
|
31
31
|
import { LayerOffset, Layout } from '../type';
|
|
32
32
|
import {
|
|
33
33
|
change2Fixed,
|
|
@@ -73,7 +73,7 @@ class Editor extends BaseService {
|
|
|
73
73
|
'copy',
|
|
74
74
|
'paste',
|
|
75
75
|
'doPaste',
|
|
76
|
-
'
|
|
76
|
+
'doAlignCenter',
|
|
77
77
|
'alignCenter',
|
|
78
78
|
'moveLayer',
|
|
79
79
|
'moveToContainer',
|
|
@@ -91,29 +91,40 @@ class Editor extends BaseService {
|
|
|
91
91
|
|
|
92
92
|
/**
|
|
93
93
|
* 设置当前指点节点配置
|
|
94
|
-
* @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes'
|
|
94
|
+
* @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength'
|
|
95
95
|
* @param value MNode
|
|
96
|
-
* @returns MNode
|
|
97
96
|
*/
|
|
98
|
-
public set<T
|
|
99
|
-
this.state[name]
|
|
97
|
+
public set<K extends StoreStateKey, T extends StoreState[K]>(name: K, value: T) {
|
|
98
|
+
const preValue = this.state[name];
|
|
99
|
+
this.state[name] = value;
|
|
100
|
+
|
|
100
101
|
// set nodes时将node设置为nodes第一个元素
|
|
101
|
-
if (name === 'nodes') {
|
|
102
|
-
this.set('node',
|
|
102
|
+
if (name === 'nodes' && Array.isArray(value)) {
|
|
103
|
+
this.set('node', value[0]);
|
|
103
104
|
}
|
|
105
|
+
|
|
104
106
|
if (name === 'root') {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
+
if (Array.isArray(value)) {
|
|
108
|
+
throw new Error('root 不能为数组');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (isObject(value) && !(value instanceof StageCore) && !(value instanceof Map)) {
|
|
112
|
+
this.state.pageLength = value.items.length;
|
|
113
|
+
} else {
|
|
114
|
+
this.state.pageLength = 0;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
this.emit('root-change', value, preValue);
|
|
107
118
|
}
|
|
108
119
|
}
|
|
109
120
|
|
|
110
121
|
/**
|
|
111
122
|
* 获取当前指点节点配置
|
|
112
|
-
* @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes'
|
|
123
|
+
* @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength'
|
|
113
124
|
* @returns MNode
|
|
114
125
|
*/
|
|
115
|
-
public get<
|
|
116
|
-
return
|
|
126
|
+
public get<K extends StoreStateKey>(name: K): StoreState[K] {
|
|
127
|
+
return this.state[name];
|
|
117
128
|
}
|
|
118
129
|
|
|
119
130
|
/**
|
|
@@ -123,22 +134,29 @@ class Editor extends BaseService {
|
|
|
123
134
|
* @returns {EditorNodeInfo}
|
|
124
135
|
*/
|
|
125
136
|
public getNodeInfo(id: Id, raw = true): EditorNodeInfo {
|
|
126
|
-
let root = this.get
|
|
137
|
+
let root = this.get('root');
|
|
127
138
|
if (raw) {
|
|
128
139
|
root = toRaw(root);
|
|
129
140
|
}
|
|
130
|
-
|
|
141
|
+
|
|
142
|
+
const info: EditorNodeInfo = {
|
|
143
|
+
node: null,
|
|
144
|
+
parent: null,
|
|
145
|
+
page: null,
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
if (!root) return info;
|
|
131
149
|
|
|
132
150
|
if (id === root.id) {
|
|
133
|
-
|
|
151
|
+
info.node = root;
|
|
152
|
+
return info;
|
|
134
153
|
}
|
|
135
154
|
|
|
136
155
|
const path = getNodePath(id, root.items);
|
|
137
156
|
|
|
138
|
-
if (!path.length) return
|
|
157
|
+
if (!path.length) return info;
|
|
139
158
|
|
|
140
159
|
path.unshift(root);
|
|
141
|
-
const info: EditorNodeInfo = {};
|
|
142
160
|
|
|
143
161
|
info.node = path[path.length - 1] as MComponent;
|
|
144
162
|
info.parent = path[path.length - 2] as MContainer;
|
|
@@ -159,7 +177,7 @@ class Editor extends BaseService {
|
|
|
159
177
|
* @param {boolean} raw 是否使用toRaw
|
|
160
178
|
* @returns 组件节点配置
|
|
161
179
|
*/
|
|
162
|
-
public getNodeById(id: Id, raw = true): MNode |
|
|
180
|
+
public getNodeById(id: Id, raw = true): MNode | null {
|
|
163
181
|
const { node } = this.getNodeInfo(id, raw);
|
|
164
182
|
return node;
|
|
165
183
|
}
|
|
@@ -170,8 +188,7 @@ class Editor extends BaseService {
|
|
|
170
188
|
* @param {boolean} raw 是否使用toRaw
|
|
171
189
|
* @returns 指点组件的父节点配置
|
|
172
190
|
*/
|
|
173
|
-
public getParentById(id: Id, raw = true): MContainer |
|
|
174
|
-
if (!this.get<MApp | null>('root')) return;
|
|
191
|
+
public getParentById(id: Id, raw = true): MContainer | null {
|
|
175
192
|
const { parent } = this.getNodeInfo(id, raw);
|
|
176
193
|
return parent;
|
|
177
194
|
}
|
|
@@ -201,18 +218,18 @@ class Editor extends BaseService {
|
|
|
201
218
|
*/
|
|
202
219
|
public async select(config: MNode | Id): Promise<MNode> | never {
|
|
203
220
|
const { node, page, parent } = this.selectedConfigExceptionHandler(config);
|
|
204
|
-
this.set('nodes', [node]);
|
|
205
|
-
this.set('page', page
|
|
206
|
-
this.set('parent', parent
|
|
221
|
+
this.set('nodes', node ? [node] : []);
|
|
222
|
+
this.set('page', page);
|
|
223
|
+
this.set('parent', parent);
|
|
207
224
|
|
|
208
225
|
if (page) {
|
|
209
226
|
historyService.changePage(toRaw(page));
|
|
210
227
|
} else {
|
|
211
|
-
historyService.
|
|
228
|
+
historyService.resetState();
|
|
212
229
|
}
|
|
213
230
|
|
|
214
231
|
if (node?.id) {
|
|
215
|
-
this.get
|
|
232
|
+
this.get('stage')
|
|
216
233
|
?.renderer.runtime?.getApp?.()
|
|
217
234
|
?.emit(
|
|
218
235
|
'editor:select',
|
|
@@ -221,7 +238,7 @@ class Editor extends BaseService {
|
|
|
221
238
|
page,
|
|
222
239
|
parent,
|
|
223
240
|
},
|
|
224
|
-
getNodePath(node.id, this.get
|
|
241
|
+
getNodePath(node.id, this.get('root')?.items),
|
|
225
242
|
);
|
|
226
243
|
}
|
|
227
244
|
|
|
@@ -230,7 +247,7 @@ class Editor extends BaseService {
|
|
|
230
247
|
return node!;
|
|
231
248
|
}
|
|
232
249
|
|
|
233
|
-
public async selectNextNode(): Promise<MNode> | never {
|
|
250
|
+
public async selectNextNode(): Promise<MNode | null> | never {
|
|
234
251
|
const node = toRaw(this.get('node'));
|
|
235
252
|
|
|
236
253
|
if (!node || isPage(node) || node.type === NodeType.ROOT) return node;
|
|
@@ -244,21 +261,24 @@ class Editor extends BaseService {
|
|
|
244
261
|
const nextNode = parent.items[index + 1] || parent.items[0];
|
|
245
262
|
|
|
246
263
|
await this.select(nextNode);
|
|
247
|
-
this.get
|
|
264
|
+
this.get('stage')?.select(nextNode.id);
|
|
248
265
|
|
|
249
266
|
return nextNode;
|
|
250
267
|
}
|
|
251
268
|
|
|
252
269
|
public async selectNextPage(): Promise<MNode> | never {
|
|
253
|
-
const root = toRaw(this.get
|
|
270
|
+
const root = toRaw(this.get('root'));
|
|
254
271
|
const page = toRaw(this.get('page'));
|
|
255
272
|
|
|
273
|
+
if (!page) throw new Error('page不能为空');
|
|
274
|
+
if (!root) throw new Error('root不能为空');
|
|
275
|
+
|
|
256
276
|
const index = getNodeIndex(page, root);
|
|
257
277
|
|
|
258
278
|
const nextPage = root.items[index + 1] || root.items[0];
|
|
259
279
|
|
|
260
280
|
await this.select(nextPage);
|
|
261
|
-
this.get
|
|
281
|
+
this.get('stage')?.select(nextPage.id);
|
|
262
282
|
|
|
263
283
|
return nextPage;
|
|
264
284
|
}
|
|
@@ -292,20 +312,25 @@ class Editor extends BaseService {
|
|
|
292
312
|
}
|
|
293
313
|
|
|
294
314
|
public async doAdd(node: MNode, parent: MContainer): Promise<MNode> {
|
|
295
|
-
const root = this.get
|
|
296
|
-
|
|
297
|
-
|
|
315
|
+
const root = this.get('root');
|
|
316
|
+
|
|
317
|
+
if (!root) throw new Error('root为空');
|
|
318
|
+
|
|
319
|
+
const curNode = this.get('node');
|
|
320
|
+
const stage = this.get('stage');
|
|
298
321
|
|
|
299
|
-
if (
|
|
322
|
+
if (!curNode) throw new Error('当前选中节点为空');
|
|
323
|
+
|
|
324
|
+
if ((parent.type === NodeType.ROOT || curNode?.type === NodeType.ROOT) && node.type !== NodeType.PAGE) {
|
|
300
325
|
throw new Error('app下不能添加组件');
|
|
301
326
|
}
|
|
302
327
|
|
|
303
328
|
if (parent.id !== curNode.id && node.type !== NodeType.PAGE) {
|
|
304
329
|
const index = parent.items.indexOf(curNode);
|
|
305
|
-
parent
|
|
330
|
+
parent.items?.splice(index + 1, 0, node);
|
|
306
331
|
} else {
|
|
307
332
|
// 新增节点添加到配置中
|
|
308
|
-
parent
|
|
333
|
+
parent.items?.push(node);
|
|
309
334
|
}
|
|
310
335
|
|
|
311
336
|
const layout = await this.getLayout(toRaw(parent), node as MNode);
|
|
@@ -337,7 +362,7 @@ class Editor extends BaseService {
|
|
|
337
362
|
* @returns 添加后的节点
|
|
338
363
|
*/
|
|
339
364
|
public async add(addNode: AddMNode | MNode[], parent?: MContainer | null): Promise<MNode | MNode[]> {
|
|
340
|
-
const stage = this.get
|
|
365
|
+
const stage = this.get('stage');
|
|
341
366
|
|
|
342
367
|
// 新增多个组件只存在于粘贴多个组件,粘贴的是一个完整的config,所以不再需要getPropsValue
|
|
343
368
|
const addNodes = [];
|
|
@@ -353,6 +378,10 @@ class Editor extends BaseService {
|
|
|
353
378
|
|
|
354
379
|
const newNodes = await Promise.all(
|
|
355
380
|
addNodes.map((node) => {
|
|
381
|
+
const root = this.get('root');
|
|
382
|
+
if (isPage(node) && root) {
|
|
383
|
+
return this.doAdd(node, root);
|
|
384
|
+
}
|
|
356
385
|
const parentNode = parent && typeof parent !== 'function' ? parent : getAddParent(node);
|
|
357
386
|
if (!parentNode) throw new Error('未找到父元素');
|
|
358
387
|
return this.doAdd(node, parentNode);
|
|
@@ -375,7 +404,9 @@ class Editor extends BaseService {
|
|
|
375
404
|
}
|
|
376
405
|
}
|
|
377
406
|
|
|
378
|
-
|
|
407
|
+
if (!isPage(newNodes[0])) {
|
|
408
|
+
this.pushHistoryState();
|
|
409
|
+
}
|
|
379
410
|
|
|
380
411
|
this.emit('add', newNodes);
|
|
381
412
|
|
|
@@ -383,9 +414,8 @@ class Editor extends BaseService {
|
|
|
383
414
|
}
|
|
384
415
|
|
|
385
416
|
public async doRemove(node: MNode): Promise<void> {
|
|
386
|
-
const root = this.get
|
|
387
|
-
|
|
388
|
-
if (!root) throw new Error('没有root');
|
|
417
|
+
const root = this.get('root');
|
|
418
|
+
if (!root) throw new Error('root不能为空');
|
|
389
419
|
|
|
390
420
|
const { parent, node: curNode } = this.getNodeInfo(node.id);
|
|
391
421
|
|
|
@@ -396,7 +426,7 @@ class Editor extends BaseService {
|
|
|
396
426
|
if (typeof index !== 'number' || index === -1) throw new Error('找不要删除的节点');
|
|
397
427
|
|
|
398
428
|
parent.items?.splice(index, 1);
|
|
399
|
-
const stage = this.get
|
|
429
|
+
const stage = this.get('stage');
|
|
400
430
|
stage?.remove({ id: node.id, parentId: parent.id, root: cloneDeep(root) });
|
|
401
431
|
|
|
402
432
|
if (node.type === NodeType.PAGE) {
|
|
@@ -430,20 +460,24 @@ class Editor extends BaseService {
|
|
|
430
460
|
/**
|
|
431
461
|
* 删除组件
|
|
432
462
|
* @param {Object} node
|
|
433
|
-
* @return {Object} 删除的组件配置
|
|
434
463
|
*/
|
|
435
464
|
public async remove(nodeOrNodeList: MNode | MNode[]): Promise<void> {
|
|
436
465
|
const nodes = Array.isArray(nodeOrNodeList) ? nodeOrNodeList : [nodeOrNodeList];
|
|
437
466
|
|
|
438
467
|
await Promise.all(nodes.map((node) => this.doRemove(node)));
|
|
439
468
|
|
|
440
|
-
|
|
441
|
-
|
|
469
|
+
if (!isPage(nodes[0])) {
|
|
470
|
+
// 更新历史记录
|
|
471
|
+
this.pushHistoryState();
|
|
472
|
+
}
|
|
442
473
|
|
|
443
474
|
this.emit('remove', nodes);
|
|
444
475
|
}
|
|
445
476
|
|
|
446
477
|
public async doUpdate(config: MNode) {
|
|
478
|
+
const root = this.get('root');
|
|
479
|
+
if (!root) throw new Error('root为空');
|
|
480
|
+
|
|
447
481
|
if (!config?.id) throw new Error('没有配置或者配置缺少id值');
|
|
448
482
|
|
|
449
483
|
const info = this.getNodeInfo(config.id, false);
|
|
@@ -452,9 +486,13 @@ class Editor extends BaseService {
|
|
|
452
486
|
|
|
453
487
|
const node = cloneDeep(toRaw(info.node));
|
|
454
488
|
|
|
455
|
-
let newConfig = await this.toggleFixedPosition(toRaw(config), node,
|
|
489
|
+
let newConfig = await this.toggleFixedPosition(toRaw(config), node, root);
|
|
456
490
|
|
|
457
491
|
newConfig = mergeWith(cloneDeep(node), newConfig, (objValue, srcValue) => {
|
|
492
|
+
if (isObject(srcValue) && Array.isArray(objValue)) {
|
|
493
|
+
// 原来的配置是数组,新的配置是对象,则直接使用新的值
|
|
494
|
+
return srcValue;
|
|
495
|
+
}
|
|
458
496
|
if (Array.isArray(srcValue)) {
|
|
459
497
|
return srcValue;
|
|
460
498
|
}
|
|
@@ -463,7 +501,7 @@ class Editor extends BaseService {
|
|
|
463
501
|
if (!newConfig.type) throw new Error('配置缺少type值');
|
|
464
502
|
|
|
465
503
|
if (newConfig.type === NodeType.ROOT) {
|
|
466
|
-
this.set('root', newConfig);
|
|
504
|
+
this.set('root', newConfig as MApp);
|
|
467
505
|
return newConfig;
|
|
468
506
|
}
|
|
469
507
|
|
|
@@ -484,19 +522,19 @@ class Editor extends BaseService {
|
|
|
484
522
|
parentNodeItems[index] = newConfig;
|
|
485
523
|
|
|
486
524
|
// 将update后的配置更新到nodes中
|
|
487
|
-
const nodes = this.get
|
|
525
|
+
const nodes = this.get('nodes');
|
|
488
526
|
const targetIndex = nodes.findIndex((nodeItem: MNode) => `${nodeItem.id}` === `${newConfig.id}`);
|
|
489
527
|
nodes.splice(targetIndex, 1, newConfig);
|
|
490
528
|
this.set('nodes', [...nodes]);
|
|
491
529
|
|
|
492
|
-
this.get
|
|
530
|
+
this.get('stage')?.update({
|
|
493
531
|
config: cloneDeep(newConfig),
|
|
494
532
|
parentId: parent.id,
|
|
495
|
-
root: cloneDeep(
|
|
533
|
+
root: cloneDeep(root),
|
|
496
534
|
});
|
|
497
535
|
|
|
498
536
|
if (newConfig.type === NodeType.PAGE) {
|
|
499
|
-
this.set('page', newConfig);
|
|
537
|
+
this.set('page', newConfig as MPage);
|
|
500
538
|
}
|
|
501
539
|
|
|
502
540
|
this.addModifiedNodeId(newConfig.id);
|
|
@@ -517,7 +555,7 @@ class Editor extends BaseService {
|
|
|
517
555
|
this.pushHistoryState();
|
|
518
556
|
|
|
519
557
|
this.emit('update', newNodes);
|
|
520
|
-
|
|
558
|
+
codeBlockService.refreshCombineInfo();
|
|
521
559
|
return Array.isArray(config) ? newNodes : newNodes[0];
|
|
522
560
|
}
|
|
523
561
|
|
|
@@ -528,8 +566,15 @@ class Editor extends BaseService {
|
|
|
528
566
|
* @returns void
|
|
529
567
|
*/
|
|
530
568
|
public async sort(id1: Id, id2: Id): Promise<void> {
|
|
531
|
-
const
|
|
532
|
-
|
|
569
|
+
const root = this.get('root');
|
|
570
|
+
if (!root) throw new Error('root为空');
|
|
571
|
+
|
|
572
|
+
const node = this.get('node');
|
|
573
|
+
if (!node) throw new Error('当前节点为空');
|
|
574
|
+
|
|
575
|
+
const parent = cloneDeep(toRaw(this.get('parent')));
|
|
576
|
+
if (!parent) throw new Error('父节点为空');
|
|
577
|
+
|
|
533
578
|
const index2 = parent.items.findIndex((node: MNode) => `${node.id}` === `${id2}`);
|
|
534
579
|
// 在 id1 的兄弟组件中若无 id2 则直接 return
|
|
535
580
|
if (index2 < 0) return;
|
|
@@ -540,10 +585,10 @@ class Editor extends BaseService {
|
|
|
540
585
|
await this.update(parent);
|
|
541
586
|
await this.select(node);
|
|
542
587
|
|
|
543
|
-
this.get
|
|
588
|
+
this.get('stage')?.update({
|
|
544
589
|
config: cloneDeep(node),
|
|
545
590
|
parentId: parent.id,
|
|
546
|
-
root: cloneDeep(
|
|
591
|
+
root: cloneDeep(root),
|
|
547
592
|
});
|
|
548
593
|
|
|
549
594
|
this.addModifiedNodeId(parent.id);
|
|
@@ -571,9 +616,20 @@ class Editor extends BaseService {
|
|
|
571
616
|
|
|
572
617
|
if (!Array.isArray(config)) return;
|
|
573
618
|
|
|
619
|
+
const node = this.get('node');
|
|
620
|
+
|
|
621
|
+
let parent: MContainer | null = null;
|
|
622
|
+
// 粘贴的组件为当前选中组件的副本时,则添加到当前选中组件的父组件中
|
|
623
|
+
if (config.length === 1 && config[0].id === node?.id) {
|
|
624
|
+
parent = this.get('parent');
|
|
625
|
+
if (parent?.type === NodeType.ROOT) {
|
|
626
|
+
parent = this.get('page');
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
574
630
|
const pasteConfigs = await this.doPaste(config, position);
|
|
575
631
|
|
|
576
|
-
return this.add(pasteConfigs);
|
|
632
|
+
return this.add(pasteConfigs, parent);
|
|
577
633
|
}
|
|
578
634
|
|
|
579
635
|
public async doPaste(config: MNode[], position: PastePosition = {}): Promise<MNode[]> {
|
|
@@ -594,7 +650,7 @@ class Editor extends BaseService {
|
|
|
594
650
|
|
|
595
651
|
if (!node.style) return config;
|
|
596
652
|
|
|
597
|
-
const stage = this.get
|
|
653
|
+
const stage = this.get('stage');
|
|
598
654
|
const doc = stage?.renderer.contentWindow?.document;
|
|
599
655
|
|
|
600
656
|
if (doc) {
|
|
@@ -617,13 +673,17 @@ class Editor extends BaseService {
|
|
|
617
673
|
*/
|
|
618
674
|
public async alignCenter(config: MNode | MNode[]): Promise<MNode | MNode[]> {
|
|
619
675
|
const nodes = Array.isArray(config) ? config : [config];
|
|
620
|
-
const stage = this.get
|
|
676
|
+
const stage = this.get('stage');
|
|
621
677
|
|
|
622
678
|
const newNodes = await Promise.all(nodes.map((node) => this.doAlignCenter(node)));
|
|
623
679
|
|
|
624
680
|
const newNode = await this.update(newNodes);
|
|
625
681
|
|
|
626
|
-
|
|
682
|
+
if (newNodes.length > 1) {
|
|
683
|
+
await stage?.multiSelect(newNodes.map((node) => node.id));
|
|
684
|
+
} else {
|
|
685
|
+
await stage?.select(newNodes[0].id);
|
|
686
|
+
}
|
|
627
687
|
|
|
628
688
|
return newNode;
|
|
629
689
|
}
|
|
@@ -633,9 +693,16 @@ class Editor extends BaseService {
|
|
|
633
693
|
* @param offset 偏移量
|
|
634
694
|
*/
|
|
635
695
|
public async moveLayer(offset: number | LayerOffset): Promise<void> {
|
|
636
|
-
const
|
|
637
|
-
|
|
638
|
-
|
|
696
|
+
const root = this.get('root');
|
|
697
|
+
if (!root) throw new Error('root为空');
|
|
698
|
+
|
|
699
|
+
const parent = this.get('parent');
|
|
700
|
+
if (!parent) throw new Error('父节点为空');
|
|
701
|
+
|
|
702
|
+
const node = this.get('node');
|
|
703
|
+
if (!node) throw new Error('当前节点为空');
|
|
704
|
+
|
|
705
|
+
const brothers: MNode[] = parent.items || [];
|
|
639
706
|
const index = brothers.findIndex((item) => `${item.id}` === `${node?.id}`);
|
|
640
707
|
|
|
641
708
|
// 流式布局与绝对定位布局操作的相反的
|
|
@@ -653,10 +720,10 @@ class Editor extends BaseService {
|
|
|
653
720
|
}
|
|
654
721
|
|
|
655
722
|
const grandparent = this.getParentById(parent.id);
|
|
656
|
-
this.get
|
|
723
|
+
this.get('stage')?.update({
|
|
657
724
|
config: cloneDeep(toRaw(parent)),
|
|
658
725
|
parentId: grandparent?.id,
|
|
659
|
-
root: cloneDeep(
|
|
726
|
+
root: cloneDeep(root),
|
|
660
727
|
});
|
|
661
728
|
|
|
662
729
|
this.addModifiedNodeId(parent.id);
|
|
@@ -669,13 +736,12 @@ class Editor extends BaseService {
|
|
|
669
736
|
* @param targetId 容器ID
|
|
670
737
|
*/
|
|
671
738
|
public async moveToContainer(config: MNode, targetId: Id): Promise<MNode | undefined> {
|
|
739
|
+
const root = cloneDeep(this.get('root'));
|
|
672
740
|
const { node, parent } = this.getNodeInfo(config.id, false);
|
|
673
741
|
const target = this.getNodeById(targetId, false) as MContainer;
|
|
674
742
|
|
|
675
|
-
const stage = this.get
|
|
676
|
-
|
|
677
|
-
if (node && parent && stage) {
|
|
678
|
-
const root = cloneDeep(this.get<MApp>('root'));
|
|
743
|
+
const stage = this.get('stage');
|
|
744
|
+
if (root && node && parent && stage) {
|
|
679
745
|
const index = getNodeIndex(node, parent);
|
|
680
746
|
parent.items?.splice(index, 1);
|
|
681
747
|
|
|
@@ -749,8 +815,7 @@ class Editor extends BaseService {
|
|
|
749
815
|
});
|
|
750
816
|
}
|
|
751
817
|
|
|
752
|
-
public
|
|
753
|
-
this.removeAllListeners();
|
|
818
|
+
public resetState() {
|
|
754
819
|
this.set('root', null);
|
|
755
820
|
this.set('node', null);
|
|
756
821
|
this.set('nodes', []);
|
|
@@ -759,11 +824,17 @@ class Editor extends BaseService {
|
|
|
759
824
|
this.set('stage', null);
|
|
760
825
|
this.set('highlightNode', null);
|
|
761
826
|
this.set('modifiedNodeIds', new Map());
|
|
762
|
-
this.set('pageLength',
|
|
827
|
+
this.set('pageLength', 0);
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
public destroy() {
|
|
831
|
+
this.removeAllListeners();
|
|
832
|
+
this.resetState();
|
|
833
|
+
this.removeAllPlugins();
|
|
763
834
|
}
|
|
764
835
|
|
|
765
836
|
public resetModifiedNodeId() {
|
|
766
|
-
this.get
|
|
837
|
+
this.get('modifiedNodeIds').clear();
|
|
767
838
|
}
|
|
768
839
|
|
|
769
840
|
/**
|
|
@@ -771,9 +842,13 @@ class Editor extends BaseService {
|
|
|
771
842
|
* @returns {CodeBlockDSL | null}
|
|
772
843
|
*/
|
|
773
844
|
public async getCodeDsl(): Promise<CodeBlockDSL | null> {
|
|
774
|
-
const root = this.get
|
|
775
|
-
|
|
776
|
-
|
|
845
|
+
const root = this.get('root');
|
|
846
|
+
return root?.codeBlocks || null;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
public getCodeDslSync(): CodeBlockDSL | null {
|
|
850
|
+
const root = this.get('root');
|
|
851
|
+
return root?.codeBlocks || null;
|
|
777
852
|
}
|
|
778
853
|
|
|
779
854
|
/**
|
|
@@ -788,16 +863,17 @@ class Editor extends BaseService {
|
|
|
788
863
|
|
|
789
864
|
private addModifiedNodeId(id: Id) {
|
|
790
865
|
if (!this.isHistoryStateChange) {
|
|
791
|
-
this.get
|
|
866
|
+
this.get('modifiedNodeIds').set(id, id);
|
|
792
867
|
}
|
|
793
868
|
}
|
|
794
869
|
|
|
795
870
|
private pushHistoryState() {
|
|
796
871
|
const curNode = cloneDeep(toRaw(this.get('node')));
|
|
797
|
-
|
|
872
|
+
const page = this.get('page');
|
|
873
|
+
if (!this.isHistoryStateChange && curNode && page) {
|
|
798
874
|
historyService.push({
|
|
799
|
-
data: cloneDeep(toRaw(
|
|
800
|
-
modifiedNodeIds: this.get
|
|
875
|
+
data: cloneDeep(toRaw(page)),
|
|
876
|
+
modifiedNodeIds: this.get('modifiedNodeIds'),
|
|
801
877
|
nodeId: curNode.id,
|
|
802
878
|
});
|
|
803
879
|
}
|
|
@@ -813,7 +889,7 @@ class Editor extends BaseService {
|
|
|
813
889
|
setTimeout(async () => {
|
|
814
890
|
if (!value.nodeId) return;
|
|
815
891
|
await this.select(value.nodeId);
|
|
816
|
-
this.get
|
|
892
|
+
this.get('stage')?.select(value.nodeId);
|
|
817
893
|
}, 0);
|
|
818
894
|
}
|
|
819
895
|
|
package/src/services/events.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
|
3
3
|
*
|
|
4
|
-
* Copyright (C)
|
|
4
|
+
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
|
5
5
|
*
|
|
6
6
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
7
|
* you may not use this file except in compliance with the License.
|
|
@@ -76,10 +76,15 @@ class Events extends BaseService {
|
|
|
76
76
|
return cloneDeep(methodMap[type] || DEFAULT_METHODS);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
public
|
|
79
|
+
public resetState() {
|
|
80
80
|
eventMap = reactive({});
|
|
81
81
|
methodMap = reactive({});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public destroy() {
|
|
85
|
+
this.resetState();
|
|
82
86
|
this.removeAllListeners();
|
|
87
|
+
this.removeAllPlugins();
|
|
83
88
|
}
|
|
84
89
|
}
|
|
85
90
|
|
package/src/services/history.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
|
3
3
|
*
|
|
4
|
-
* Copyright (C)
|
|
4
|
+
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
|
5
5
|
*
|
|
6
6
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
7
|
* you may not use this file except in compliance with the License.
|
|
@@ -18,25 +18,13 @@
|
|
|
18
18
|
|
|
19
19
|
import { reactive } from 'vue';
|
|
20
20
|
|
|
21
|
-
import type {
|
|
21
|
+
import type { MPage } from '@tmagic/schema';
|
|
22
22
|
|
|
23
|
+
import { HistoryState, StepValue } from '../type';
|
|
23
24
|
import { UndoRedo } from '../utils/undo-redo';
|
|
24
25
|
|
|
25
26
|
import BaseService from './BaseService';
|
|
26
27
|
|
|
27
|
-
export interface StepValue {
|
|
28
|
-
data: MPage;
|
|
29
|
-
modifiedNodeIds: Map<Id, Id>;
|
|
30
|
-
nodeId: Id;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface HistoryState {
|
|
34
|
-
pageId?: Id;
|
|
35
|
-
pageSteps: Record<Id, UndoRedo<StepValue>>;
|
|
36
|
-
canRedo: boolean;
|
|
37
|
-
canUndo: boolean;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
28
|
class History extends BaseService {
|
|
41
29
|
public state = reactive<HistoryState>({
|
|
42
30
|
pageSteps: {},
|
|
@@ -76,9 +64,11 @@ class History extends BaseService {
|
|
|
76
64
|
}
|
|
77
65
|
|
|
78
66
|
this.setCanUndoRedo();
|
|
67
|
+
|
|
68
|
+
this.emit('page-change', this.state.pageSteps[this.state.pageId]);
|
|
79
69
|
}
|
|
80
70
|
|
|
81
|
-
public
|
|
71
|
+
public resetState(): void {
|
|
82
72
|
this.state.pageId = undefined;
|
|
83
73
|
this.state.pageSteps = {};
|
|
84
74
|
this.state.canRedo = false;
|
|
@@ -110,8 +100,9 @@ class History extends BaseService {
|
|
|
110
100
|
}
|
|
111
101
|
|
|
112
102
|
public destroy(): void {
|
|
113
|
-
this.
|
|
103
|
+
this.resetState();
|
|
114
104
|
this.removeAllListeners();
|
|
105
|
+
this.removeAllPlugins();
|
|
115
106
|
}
|
|
116
107
|
|
|
117
108
|
private getUndoRedo() {
|