@tmagic/editor 1.0.0-beta.8 → 1.0.0-rc.10
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/README.md +1 -0
- package/dist/style.css +94 -45
- package/dist/tmagic-editor.es.js +1622 -2069
- package/dist/tmagic-editor.es.js.map +1 -1
- package/dist/tmagic-editor.umd.js +1621 -2071
- package/dist/tmagic-editor.umd.js.map +1 -1
- package/dist/types/src/Editor.vue.d.ts +50 -2
- package/dist/types/src/fields/Code.vue.d.ts +4 -4
- package/dist/types/src/fields/CodeLink.vue.d.ts +1 -3
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/layouts/CodeEditor.vue.d.ts +13 -4
- package/dist/types/src/layouts/Framework.vue.d.ts +15 -3
- package/dist/types/src/layouts/PropsPanel.vue.d.ts +4 -5
- package/dist/types/src/layouts/sidebar/ComponentListPanel.vue.d.ts +2 -1
- package/dist/types/src/layouts/sidebar/Sidebar.vue.d.ts +2 -3
- package/dist/types/src/layouts/sidebar/TabPane.vue.d.ts +14 -0
- package/dist/types/src/layouts/workspace/Workspace.vue.d.ts +5 -31
- package/dist/types/src/services/BaseService.d.ts +5 -2
- package/dist/types/src/services/editor.d.ts +16 -6
- package/dist/types/src/services/history.d.ts +2 -18
- package/dist/types/src/services/props.d.ts +8 -2
- package/dist/types/src/services/ui.d.ts +2 -0
- package/dist/types/src/type.d.ts +25 -9
- package/dist/types/src/utils/editor.d.ts +5 -10
- package/dist/types/src/utils/polyfills.d.ts +0 -0
- package/dist/types/src/utils/props.d.ts +8 -1
- package/package.json +35 -12
- package/src/Editor.vue +44 -9
- package/src/components/ContentMenu.vue +140 -0
- package/src/components/ScrollViewer.vue +1 -1
- package/src/components/ToolButton.vue +72 -31
- package/src/fields/UISelect.vue +14 -6
- package/src/index.ts +2 -0
- package/src/layouts/AddPageBox.vue +3 -1
- package/src/layouts/CodeEditor.vue +20 -13
- package/src/layouts/Framework.vue +20 -7
- package/src/layouts/PropsPanel.vue +5 -4
- package/src/layouts/sidebar/ComponentListPanel.vue +25 -3
- package/src/layouts/sidebar/LayerMenu.vue +93 -95
- package/src/layouts/sidebar/LayerPanel.vue +80 -59
- package/src/layouts/sidebar/Sidebar.vue +13 -52
- package/src/layouts/sidebar/TabPane.vue +68 -0
- package/src/layouts/workspace/PageBar.vue +145 -17
- package/src/layouts/workspace/Stage.vue +65 -91
- package/src/layouts/workspace/ViewerMenu.vue +122 -68
- package/src/layouts/workspace/Workspace.vue +125 -42
- package/src/services/BaseService.ts +71 -23
- package/src/services/editor.ts +231 -69
- package/src/services/history.ts +7 -0
- package/src/services/props.ts +64 -9
- package/src/services/ui.ts +35 -5
- package/src/theme/common/var.scss +3 -2
- package/src/theme/component-list-panel.scss +2 -0
- package/src/theme/content-menu.scss +33 -26
- package/src/theme/layer-panel.scss +10 -0
- package/src/theme/nav-menu.scss +6 -2
- package/src/theme/page-bar.scss +42 -2
- package/src/theme/props-panel.scss +19 -17
- package/src/theme/workspace.scss +4 -0
- package/src/type.ts +27 -9
- package/src/utils/editor.ts +37 -56
- package/src/utils/polyfills.ts +8 -0
- package/src/utils/props.ts +50 -8
- package/LICENSE +0 -5432
- package/dist/types/src/components/ToolButton.vue.d.ts +0 -35
- package/dist/types/src/fields/UISelect.vue.d.ts +0 -32
- package/dist/types/src/layouts/sidebar/LayerMenu.vue.d.ts +0 -31
- package/dist/types/src/layouts/sidebar/LayerPanel.vue.d.ts +0 -973
- package/dist/types/src/layouts/workspace/PageBar.vue.d.ts +0 -11
- package/dist/types/src/layouts/workspace/Stage.vue.d.ts +0 -192
- package/dist/types/src/layouts/workspace/ViewerMenu.vue.d.ts +0 -18
- package/tsconfig.json +0 -9
- package/vite.config.ts +0 -30
package/src/services/editor.ts
CHANGED
|
@@ -21,8 +21,9 @@ import { cloneDeep, mergeWith } from 'lodash-es';
|
|
|
21
21
|
import serialize from 'serialize-javascript';
|
|
22
22
|
|
|
23
23
|
import type { Id, MApp, MComponent, MContainer, MNode, MPage } from '@tmagic/schema';
|
|
24
|
-
import
|
|
25
|
-
import
|
|
24
|
+
import { NodeType } from '@tmagic/schema';
|
|
25
|
+
import StageCore from '@tmagic/stage';
|
|
26
|
+
import { getNodePath, isNumber, isPage, isPop } from '@tmagic/utils';
|
|
26
27
|
|
|
27
28
|
import historyService, { StepValue } from '@editor/services/history';
|
|
28
29
|
import propsService from '@editor/services/props';
|
|
@@ -32,11 +33,11 @@ import {
|
|
|
32
33
|
change2Fixed,
|
|
33
34
|
COPY_STORAGE_KEY,
|
|
34
35
|
Fixed2Other,
|
|
36
|
+
generatePageNameByApp,
|
|
35
37
|
getNodeIndex,
|
|
36
38
|
initPosition,
|
|
37
39
|
isFixed,
|
|
38
40
|
setLayout,
|
|
39
|
-
setNewItemId,
|
|
40
41
|
} from '@editor/utils/editor';
|
|
41
42
|
import { log } from '@editor/utils/logger';
|
|
42
43
|
|
|
@@ -51,29 +52,37 @@ class Editor extends BaseService {
|
|
|
51
52
|
parent: null,
|
|
52
53
|
node: null,
|
|
53
54
|
stage: null,
|
|
55
|
+
highlightNode: null,
|
|
54
56
|
modifiedNodeIds: new Map(),
|
|
57
|
+
pageLength: 0,
|
|
55
58
|
});
|
|
56
59
|
|
|
57
60
|
constructor() {
|
|
58
|
-
super(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
61
|
+
super(
|
|
62
|
+
[
|
|
63
|
+
'getLayout',
|
|
64
|
+
'select',
|
|
65
|
+
'add',
|
|
66
|
+
'remove',
|
|
67
|
+
'update',
|
|
68
|
+
'sort',
|
|
69
|
+
'copy',
|
|
70
|
+
'paste',
|
|
71
|
+
'alignCenter',
|
|
72
|
+
'moveLayer',
|
|
73
|
+
'move',
|
|
74
|
+
'undo',
|
|
75
|
+
'redo',
|
|
76
|
+
'highlight',
|
|
77
|
+
],
|
|
78
|
+
// 需要注意循环依赖问题,如果函数间有相互调用的话,不能设置为串行调用
|
|
79
|
+
['select', 'update', 'moveLayer'],
|
|
80
|
+
);
|
|
72
81
|
}
|
|
73
82
|
|
|
74
83
|
/**
|
|
75
84
|
* 设置当前指点节点配置
|
|
76
|
-
* @param name 'root' | 'page' | 'parent' | 'node'
|
|
85
|
+
* @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode'
|
|
77
86
|
* @param value MNode
|
|
78
87
|
* @returns MNode
|
|
79
88
|
*/
|
|
@@ -82,6 +91,7 @@ class Editor extends BaseService {
|
|
|
82
91
|
log('store set ', name, ' ', value);
|
|
83
92
|
|
|
84
93
|
if (name === 'root') {
|
|
94
|
+
this.state.pageLength = (value as unknown as MApp)?.items?.length || 0;
|
|
85
95
|
this.emit('root-change', value);
|
|
86
96
|
}
|
|
87
97
|
}
|
|
@@ -101,7 +111,7 @@ class Editor extends BaseService {
|
|
|
101
111
|
* @returns {EditorNodeInfo}
|
|
102
112
|
*/
|
|
103
113
|
public getNodeInfo(id: Id): EditorNodeInfo {
|
|
104
|
-
const root = this.get<MApp | null>('root');
|
|
114
|
+
const root = toRaw(this.get<MApp | null>('root'));
|
|
105
115
|
if (!root) return {};
|
|
106
116
|
|
|
107
117
|
if (id === root.id) {
|
|
@@ -119,7 +129,7 @@ class Editor extends BaseService {
|
|
|
119
129
|
info.parent = path[path.length - 2] as MContainer;
|
|
120
130
|
|
|
121
131
|
path.forEach((item) => {
|
|
122
|
-
if (item.type ===
|
|
132
|
+
if (item.type === NodeType.PAGE) {
|
|
123
133
|
info.page = item as MPage;
|
|
124
134
|
return;
|
|
125
135
|
}
|
|
@@ -152,13 +162,15 @@ class Editor extends BaseService {
|
|
|
152
162
|
/**
|
|
153
163
|
* 只有容器拥有布局
|
|
154
164
|
*/
|
|
155
|
-
public async getLayout(
|
|
156
|
-
if (node
|
|
157
|
-
|
|
165
|
+
public async getLayout(parent: MNode, node?: MNode): Promise<Layout> {
|
|
166
|
+
if (node && typeof node !== 'function' && isFixed(node)) return Layout.FIXED;
|
|
167
|
+
|
|
168
|
+
if (parent.layout) {
|
|
169
|
+
return parent.layout;
|
|
158
170
|
}
|
|
159
171
|
|
|
160
172
|
// 如果该节点没有设置position,则认为是流式布局,例如获取root的布局时
|
|
161
|
-
if (!
|
|
173
|
+
if (!parent.style?.position) {
|
|
162
174
|
return Layout.RELATIVE;
|
|
163
175
|
}
|
|
164
176
|
|
|
@@ -166,28 +178,12 @@ class Editor extends BaseService {
|
|
|
166
178
|
}
|
|
167
179
|
|
|
168
180
|
/**
|
|
169
|
-
*
|
|
170
|
-
* @param config
|
|
181
|
+
* 选中指定节点(将指定节点设置成当前选中状态)
|
|
182
|
+
* @param config 指定节点配置或者ID
|
|
171
183
|
* @returns 当前选中的节点配置
|
|
172
184
|
*/
|
|
173
|
-
public async select(config: MNode | Id): Promise<MNode> {
|
|
174
|
-
|
|
175
|
-
if (typeof config === 'string' || typeof config === 'number') {
|
|
176
|
-
id = config;
|
|
177
|
-
} else {
|
|
178
|
-
id = config.id;
|
|
179
|
-
}
|
|
180
|
-
if (!id) {
|
|
181
|
-
throw new Error('没有ID,无法选中');
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const { node, parent, page } = this.getNodeInfo(id);
|
|
185
|
-
if (!node) throw new Error('获取不到组件信息');
|
|
186
|
-
|
|
187
|
-
if (node.id === this.state.root?.id) {
|
|
188
|
-
throw new Error('不能选根节点');
|
|
189
|
-
}
|
|
190
|
-
|
|
185
|
+
public async select(config: MNode | Id): Promise<MNode> | never {
|
|
186
|
+
const { node, page, parent } = this.selectedConfigExceptionHandler(config);
|
|
191
187
|
this.set('node', node);
|
|
192
188
|
this.set('page', page || null);
|
|
193
189
|
this.set('parent', parent || null);
|
|
@@ -198,7 +194,68 @@ class Editor extends BaseService {
|
|
|
198
194
|
historyService.empty();
|
|
199
195
|
}
|
|
200
196
|
|
|
201
|
-
|
|
197
|
+
if (node?.id) {
|
|
198
|
+
this.get<StageCore>('stage')
|
|
199
|
+
?.renderer.runtime?.getApp?.()
|
|
200
|
+
?.emit(
|
|
201
|
+
'editor:select',
|
|
202
|
+
{
|
|
203
|
+
node,
|
|
204
|
+
page,
|
|
205
|
+
parent,
|
|
206
|
+
},
|
|
207
|
+
getNodePath(node.id, this.get<MApp>('root').items),
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
this.emit('select', node);
|
|
212
|
+
|
|
213
|
+
return node!;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
public async selectNextNode(): Promise<MNode> | never {
|
|
217
|
+
const node = toRaw(this.get('node'));
|
|
218
|
+
|
|
219
|
+
if (!node || isPage(node) || node.type === NodeType.ROOT) return node;
|
|
220
|
+
|
|
221
|
+
const parent = toRaw(this.getParentById(node.id));
|
|
222
|
+
|
|
223
|
+
if (!parent) return node;
|
|
224
|
+
|
|
225
|
+
const index = getNodeIndex(node, parent);
|
|
226
|
+
|
|
227
|
+
const nextNode = parent.items[index + 1] || parent.items[0];
|
|
228
|
+
|
|
229
|
+
await this.select(nextNode);
|
|
230
|
+
this.get<StageCore>('stage')?.select(nextNode.id);
|
|
231
|
+
|
|
232
|
+
return nextNode;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
public async selectNextPage(): Promise<MNode> | never {
|
|
236
|
+
const root = toRaw(this.get<MApp>('root'));
|
|
237
|
+
const page = toRaw(this.get('page'));
|
|
238
|
+
|
|
239
|
+
const index = getNodeIndex(page, root);
|
|
240
|
+
|
|
241
|
+
const nextPage = root.items[index + 1] || root.items[0];
|
|
242
|
+
|
|
243
|
+
await this.select(nextPage);
|
|
244
|
+
this.get<StageCore>('stage')?.select(nextPage.id);
|
|
245
|
+
|
|
246
|
+
return nextPage;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* 高亮指定节点
|
|
251
|
+
* @param config 指定节点配置或者ID
|
|
252
|
+
* @returns 当前高亮的节点配置
|
|
253
|
+
*/
|
|
254
|
+
public highlight(config: MNode | Id): void {
|
|
255
|
+
const { node } = this.selectedConfigExceptionHandler(config);
|
|
256
|
+
const currentHighlightNode = this.get('highlightNode');
|
|
257
|
+
if (currentHighlightNode === node) return;
|
|
258
|
+
this.set('highlightNode', node);
|
|
202
259
|
}
|
|
203
260
|
|
|
204
261
|
/**
|
|
@@ -207,12 +264,14 @@ class Editor extends BaseService {
|
|
|
207
264
|
* @param parent 要添加到的容器组件节点配置,如果不设置,默认为当前选中的组件的父节点
|
|
208
265
|
* @returns 添加后的节点
|
|
209
266
|
*/
|
|
210
|
-
public async add(
|
|
267
|
+
public async add(addNode: AddMNode, parent?: MContainer | null): Promise<MNode> {
|
|
268
|
+
const { type, ...config } = addNode;
|
|
211
269
|
const curNode = this.get<MContainer>('node');
|
|
212
270
|
|
|
213
271
|
let parentNode: MNode | undefined;
|
|
272
|
+
const isPage = type === NodeType.PAGE;
|
|
214
273
|
|
|
215
|
-
if (
|
|
274
|
+
if (isPage) {
|
|
216
275
|
parentNode = this.get<MApp>('root');
|
|
217
276
|
// 由于支持中间件扩展,在parent参数为undefined时,parent会变成next函数
|
|
218
277
|
} else if (parent && typeof parent !== 'function') {
|
|
@@ -225,21 +284,38 @@ class Editor extends BaseService {
|
|
|
225
284
|
|
|
226
285
|
if (!parentNode) throw new Error('未找到父元素');
|
|
227
286
|
|
|
228
|
-
const layout = await this.getLayout(parentNode);
|
|
229
|
-
const newNode = initPosition(
|
|
287
|
+
const layout = await this.getLayout(toRaw(parentNode), addNode as MNode);
|
|
288
|
+
const newNode = initPosition(
|
|
289
|
+
{ ...toRaw(await propsService.getPropsValue(type, config)) },
|
|
290
|
+
layout,
|
|
291
|
+
parentNode,
|
|
292
|
+
this.get<StageCore>('stage'),
|
|
293
|
+
);
|
|
230
294
|
|
|
231
|
-
if ((parentNode?.type ===
|
|
295
|
+
if ((parentNode?.type === NodeType.ROOT || curNode.type === NodeType.ROOT) && newNode.type !== NodeType.PAGE) {
|
|
232
296
|
throw new Error('app下不能添加组件');
|
|
233
297
|
}
|
|
234
298
|
|
|
235
299
|
parentNode?.items?.push(newNode);
|
|
236
300
|
|
|
237
|
-
|
|
301
|
+
const stage = this.get<StageCore | null>('stage');
|
|
302
|
+
|
|
303
|
+
await stage?.add({ config: cloneDeep(newNode), root: cloneDeep(this.get('root')) });
|
|
238
304
|
|
|
239
305
|
await this.select(newNode);
|
|
240
306
|
|
|
241
307
|
this.addModifiedNodeId(newNode.id);
|
|
242
|
-
|
|
308
|
+
if (!isPage) {
|
|
309
|
+
this.pushHistoryState();
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
stage?.select(newNode.id);
|
|
313
|
+
|
|
314
|
+
if (isPage) {
|
|
315
|
+
this.state.pageLength += 1;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
this.emit('add', newNode);
|
|
243
319
|
|
|
244
320
|
return newNode;
|
|
245
321
|
}
|
|
@@ -265,17 +341,38 @@ class Editor extends BaseService {
|
|
|
265
341
|
if (typeof index !== 'number' || index === -1) throw new Error('找不要删除的节点');
|
|
266
342
|
|
|
267
343
|
parent.items?.splice(index, 1);
|
|
268
|
-
this.get<StageCore>('stage')
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
344
|
+
const stage = this.get<StageCore | null>('stage');
|
|
345
|
+
stage?.remove({ id: node.id, root: this.get('root') });
|
|
346
|
+
|
|
347
|
+
if (node.type === NodeType.PAGE) {
|
|
348
|
+
this.state.pageLength -= 1;
|
|
349
|
+
|
|
350
|
+
if (root.items[0]) {
|
|
351
|
+
await this.select(root.items[0]);
|
|
352
|
+
stage?.select(root.items[0].id);
|
|
353
|
+
} else {
|
|
354
|
+
this.set('node', null);
|
|
355
|
+
this.set('parent', null);
|
|
356
|
+
this.set('page', null);
|
|
357
|
+
this.set('stage', null);
|
|
358
|
+
this.set('highlightNode', null);
|
|
359
|
+
this.resetModifiedNodeId();
|
|
360
|
+
historyService.reset();
|
|
361
|
+
|
|
362
|
+
this.emit('remove', node);
|
|
363
|
+
|
|
364
|
+
return node;
|
|
365
|
+
}
|
|
272
366
|
} else {
|
|
273
367
|
await this.select(parent);
|
|
368
|
+
stage?.select(parent.id);
|
|
274
369
|
}
|
|
275
370
|
|
|
276
371
|
this.addModifiedNodeId(parent.id);
|
|
277
372
|
this.pushHistoryState();
|
|
278
373
|
|
|
374
|
+
this.emit('remove', node);
|
|
375
|
+
|
|
279
376
|
return node;
|
|
280
377
|
}
|
|
281
378
|
|
|
@@ -303,7 +400,7 @@ class Editor extends BaseService {
|
|
|
303
400
|
|
|
304
401
|
if (!newConfig.type) throw new Error('配置缺少type值');
|
|
305
402
|
|
|
306
|
-
if (newConfig.type ===
|
|
403
|
+
if (newConfig.type === NodeType.ROOT) {
|
|
307
404
|
this.set('root', newConfig);
|
|
308
405
|
return newConfig;
|
|
309
406
|
}
|
|
@@ -324,19 +421,21 @@ class Editor extends BaseService {
|
|
|
324
421
|
|
|
325
422
|
parentNodeItems[index] = newConfig;
|
|
326
423
|
|
|
327
|
-
if (newConfig.id === this.get('node').id) {
|
|
424
|
+
if (`${newConfig.id}` === `${this.get('node').id}`) {
|
|
328
425
|
this.set('node', newConfig);
|
|
329
426
|
}
|
|
330
427
|
|
|
331
|
-
this.get<StageCore>('stage')?.update({ config: cloneDeep(newConfig), root: this.get('root') });
|
|
428
|
+
this.get<StageCore | null>('stage')?.update({ config: cloneDeep(newConfig), root: this.get('root') });
|
|
332
429
|
|
|
333
|
-
if (newConfig.type ===
|
|
430
|
+
if (newConfig.type === NodeType.PAGE) {
|
|
334
431
|
this.set('page', newConfig);
|
|
335
432
|
}
|
|
336
433
|
|
|
337
434
|
this.addModifiedNodeId(newConfig.id);
|
|
338
435
|
this.pushHistoryState();
|
|
339
436
|
|
|
437
|
+
this.emit('update', newConfig);
|
|
438
|
+
|
|
340
439
|
return newConfig;
|
|
341
440
|
}
|
|
342
441
|
|
|
@@ -359,7 +458,7 @@ class Editor extends BaseService {
|
|
|
359
458
|
await this.update(parent);
|
|
360
459
|
await this.select(node);
|
|
361
460
|
|
|
362
|
-
this.get<StageCore>('stage')?.update({ config: cloneDeep(node), root: this.get('root') });
|
|
461
|
+
this.get<StageCore | null>('stage')?.update({ config: cloneDeep(node), root: this.get('root') });
|
|
363
462
|
|
|
364
463
|
this.addModifiedNodeId(parent.id);
|
|
365
464
|
this.pushHistoryState();
|
|
@@ -395,7 +494,7 @@ class Editor extends BaseService {
|
|
|
395
494
|
return;
|
|
396
495
|
}
|
|
397
496
|
|
|
398
|
-
setNewItemId(config, this.get('root'));
|
|
497
|
+
await propsService.setNewItemId(config, this.get('root'));
|
|
399
498
|
if (config.style) {
|
|
400
499
|
config.style = {
|
|
401
500
|
...config.style,
|
|
@@ -403,6 +502,10 @@ class Editor extends BaseService {
|
|
|
403
502
|
};
|
|
404
503
|
}
|
|
405
504
|
|
|
505
|
+
if (isPage(config)) {
|
|
506
|
+
config.name = generatePageNameByApp(this.get('root'));
|
|
507
|
+
}
|
|
508
|
+
|
|
406
509
|
return await this.add(config);
|
|
407
510
|
}
|
|
408
511
|
|
|
@@ -414,17 +517,28 @@ class Editor extends BaseService {
|
|
|
414
517
|
public async alignCenter(config: MNode): Promise<MNode | void> {
|
|
415
518
|
const parent = this.get<MContainer>('parent');
|
|
416
519
|
const node = this.get<MNode>('node');
|
|
417
|
-
const layout = await this.getLayout(parent);
|
|
520
|
+
const layout = await this.getLayout(toRaw(parent), toRaw(node));
|
|
418
521
|
if (layout === Layout.RELATIVE) {
|
|
419
522
|
return;
|
|
420
523
|
}
|
|
421
524
|
|
|
422
|
-
if (
|
|
525
|
+
if (!node.style) return;
|
|
526
|
+
|
|
527
|
+
const stage = this.get<StageCore>('stage');
|
|
528
|
+
const doc = stage?.renderer.contentWindow?.document;
|
|
529
|
+
|
|
530
|
+
if (doc) {
|
|
531
|
+
const parentEl = doc.getElementById(`${parent.id}`);
|
|
532
|
+
const el = doc.getElementById(`${node.id}`);
|
|
533
|
+
if (parentEl && el) {
|
|
534
|
+
node.style.left = (parentEl.clientWidth - el.clientWidth) / 2;
|
|
535
|
+
}
|
|
536
|
+
} else if (parent.style && isNumber(parent.style?.width) && isNumber(node.style?.width)) {
|
|
423
537
|
node.style.left = (parent.style.width - node.style.width) / 2;
|
|
424
538
|
}
|
|
425
539
|
|
|
426
540
|
await this.update(node);
|
|
427
|
-
this.get<StageCore>('stage')?.update({ config: cloneDeep(toRaw(node)), root: this.get('root') });
|
|
541
|
+
this.get<StageCore | null>('stage')?.update({ config: cloneDeep(toRaw(node)), root: this.get('root') });
|
|
428
542
|
this.addModifiedNodeId(config.id);
|
|
429
543
|
this.pushHistoryState();
|
|
430
544
|
|
|
@@ -441,15 +555,15 @@ class Editor extends BaseService {
|
|
|
441
555
|
const brothers: MNode[] = parent?.items || [];
|
|
442
556
|
const index = brothers.findIndex((item) => `${item.id}` === `${node?.id}`);
|
|
443
557
|
|
|
444
|
-
if (offset === LayerOffset.
|
|
558
|
+
if (offset === LayerOffset.TOP) {
|
|
445
559
|
brothers.splice(brothers.length - 1, 0, brothers.splice(index, 1)[0]);
|
|
446
|
-
} else if (offset === LayerOffset.
|
|
560
|
+
} else if (offset === LayerOffset.BOTTOM) {
|
|
447
561
|
brothers.splice(0, 0, brothers.splice(index, 1)[0]);
|
|
448
562
|
} else {
|
|
449
563
|
brothers.splice(index + parseInt(`${offset}`, 10), 0, brothers.splice(index, 1)[0]);
|
|
450
564
|
}
|
|
451
565
|
|
|
452
|
-
this.get<StageCore>('stage')?.update({ config: cloneDeep(toRaw(parent)), root: this.get('root') });
|
|
566
|
+
this.get<StageCore | null>('stage')?.update({ config: cloneDeep(toRaw(parent)), root: this.get('root') });
|
|
453
567
|
}
|
|
454
568
|
|
|
455
569
|
/**
|
|
@@ -472,6 +586,26 @@ class Editor extends BaseService {
|
|
|
472
586
|
return value;
|
|
473
587
|
}
|
|
474
588
|
|
|
589
|
+
public async move(left: number, top: number) {
|
|
590
|
+
const node = toRaw(this.get('node'));
|
|
591
|
+
if (!node || isPage(node)) return;
|
|
592
|
+
|
|
593
|
+
const { style, id } = node;
|
|
594
|
+
if (!style || style.position !== 'absolute') return;
|
|
595
|
+
|
|
596
|
+
if (top && !isNumber(style.top)) return;
|
|
597
|
+
if (left && !isNumber(style.left)) return;
|
|
598
|
+
|
|
599
|
+
this.update({
|
|
600
|
+
id,
|
|
601
|
+
style: {
|
|
602
|
+
...style,
|
|
603
|
+
left: Number(style.left) + left,
|
|
604
|
+
top: Number(style.top) + top,
|
|
605
|
+
},
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
|
|
475
609
|
public destroy() {
|
|
476
610
|
this.removeAllListeners();
|
|
477
611
|
this.set('root', null);
|
|
@@ -508,7 +642,11 @@ class Editor extends BaseService {
|
|
|
508
642
|
this.isHistoryStateChange = true;
|
|
509
643
|
await this.update(value.data);
|
|
510
644
|
this.set('modifiedNodeIds', value.modifiedNodeIds);
|
|
511
|
-
setTimeout(() =>
|
|
645
|
+
setTimeout(async () => {
|
|
646
|
+
if (!value.nodeId) return;
|
|
647
|
+
await this.select(value.nodeId);
|
|
648
|
+
this.get<StageCore | null>('stage')?.select(value.nodeId);
|
|
649
|
+
}, 0);
|
|
512
650
|
}
|
|
513
651
|
|
|
514
652
|
private async toggleFixedPosition(dist: MNode, src: MNode, root: MApp) {
|
|
@@ -524,6 +662,30 @@ class Editor extends BaseService {
|
|
|
524
662
|
|
|
525
663
|
return newConfig;
|
|
526
664
|
}
|
|
665
|
+
|
|
666
|
+
private selectedConfigExceptionHandler(config: MNode | Id): EditorNodeInfo {
|
|
667
|
+
let id: Id;
|
|
668
|
+
if (typeof config === 'string' || typeof config === 'number') {
|
|
669
|
+
id = config;
|
|
670
|
+
} else {
|
|
671
|
+
id = config.id;
|
|
672
|
+
}
|
|
673
|
+
if (!id) {
|
|
674
|
+
throw new Error('没有ID,无法选中');
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
const { node, parent, page } = this.getNodeInfo(id);
|
|
678
|
+
if (!node) throw new Error('获取不到组件信息');
|
|
679
|
+
|
|
680
|
+
if (node.id === this.state.root?.id) {
|
|
681
|
+
throw new Error('不能选根节点');
|
|
682
|
+
}
|
|
683
|
+
return {
|
|
684
|
+
node,
|
|
685
|
+
parent,
|
|
686
|
+
page,
|
|
687
|
+
};
|
|
688
|
+
}
|
|
527
689
|
}
|
|
528
690
|
|
|
529
691
|
export type EditorService = Editor;
|
package/src/services/history.ts
CHANGED
|
@@ -51,6 +51,13 @@ class History extends BaseService {
|
|
|
51
51
|
this.on('change', this.setCanUndoRedo);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
public reset() {
|
|
55
|
+
this.state.pageSteps = {};
|
|
56
|
+
this.state.pageId = undefined;
|
|
57
|
+
this.state.canRedo = false;
|
|
58
|
+
this.state.canUndo = false;
|
|
59
|
+
}
|
|
60
|
+
|
|
54
61
|
public changePage(page: MPage): void {
|
|
55
62
|
if (!page) return;
|
|
56
63
|
|
package/src/services/props.ts
CHANGED
|
@@ -17,11 +17,12 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { reactive } from 'vue';
|
|
20
|
-
import { cloneDeep } from 'lodash-es';
|
|
20
|
+
import { cloneDeep, mergeWith, random } from 'lodash-es';
|
|
21
21
|
|
|
22
22
|
import type { FormConfig } from '@tmagic/form';
|
|
23
|
-
import type { MComponent, MNode } from '@tmagic/schema';
|
|
24
|
-
import {
|
|
23
|
+
import type { Id, MComponent, MNode, MPage } from '@tmagic/schema';
|
|
24
|
+
import { NodeType } from '@tmagic/schema';
|
|
25
|
+
import { isPop, toLine } from '@tmagic/utils';
|
|
25
26
|
|
|
26
27
|
import type { PropsState } from '@editor/type';
|
|
27
28
|
import { DEFAULT_CONFIG, fillConfig, getDefaultPropsValue } from '@editor/utils/props';
|
|
@@ -35,7 +36,7 @@ class Props extends BaseService {
|
|
|
35
36
|
});
|
|
36
37
|
|
|
37
38
|
constructor() {
|
|
38
|
-
super(['setPropsConfig', 'getPropsConfig', 'setPropsValue', 'getPropsValue']);
|
|
39
|
+
super(['setPropsConfig', 'getPropsConfig', 'setPropsValue', 'getPropsValue', 'createId', 'setNewItemId']);
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
public setPropsConfigs(configs: Record<string, FormConfig>) {
|
|
@@ -87,7 +88,7 @@ class Props extends BaseService {
|
|
|
87
88
|
* @param type 组件类型
|
|
88
89
|
* @returns 组件初始值
|
|
89
90
|
*/
|
|
90
|
-
public async getPropsValue(type: string) {
|
|
91
|
+
public async getPropsValue(type: string, defaultValue: Record<string, any> = {}) {
|
|
91
92
|
if (type === 'area') {
|
|
92
93
|
const value = (await this.getPropsValue('button')) as MComponent;
|
|
93
94
|
value.className = 'action-area';
|
|
@@ -98,13 +99,67 @@ class Props extends BaseService {
|
|
|
98
99
|
return value;
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
const data = cloneDeep(defaultValue as any);
|
|
103
|
+
|
|
104
|
+
await this.setNewItemId(data);
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
...getDefaultPropsValue(type, await this.createId(type)),
|
|
108
|
+
...mergeWith(cloneDeep(this.state.propsValueMap[type] || {}), data),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
public async createId(type: string | number): Promise<string> {
|
|
113
|
+
return `${type}_${random(10000, false)}`;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 将组件与组件的子元素配置中的id都设置成一个新的ID
|
|
118
|
+
* @param {Object} config 组件配置
|
|
119
|
+
*/
|
|
120
|
+
/* eslint no-param-reassign: ["error", { "props": false }] */
|
|
121
|
+
public async setNewItemId(config: MNode, parent?: MPage) {
|
|
122
|
+
const oldId = config.id;
|
|
123
|
+
|
|
124
|
+
config.id = await this.createId(config.type || 'component');
|
|
125
|
+
|
|
126
|
+
// 只有弹窗在页面下的一级子元素才有效
|
|
127
|
+
if (isPop(config) && parent?.type === NodeType.PAGE) {
|
|
128
|
+
updatePopId(oldId, config.id, parent);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (config.items && Array.isArray(config.items)) {
|
|
132
|
+
for (const item of config.items) {
|
|
133
|
+
await this.setNewItemId(item, config as MPage);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
105
136
|
}
|
|
106
137
|
}
|
|
107
138
|
|
|
139
|
+
/**
|
|
140
|
+
* 复制页面时,需要把组件下关联的弹窗id换测复制出来的弹窗的id
|
|
141
|
+
* @param {number} oldId 复制的源弹窗id
|
|
142
|
+
* @param {number} popId 新的弹窗id
|
|
143
|
+
* @param {Object} pageConfig 页面配置
|
|
144
|
+
*/
|
|
145
|
+
const updatePopId = (oldId: Id, popId: Id, pageConfig: MPage) => {
|
|
146
|
+
pageConfig.items?.forEach((config) => {
|
|
147
|
+
if (config.pop === oldId) {
|
|
148
|
+
config.pop = popId;
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (config.popId === oldId) {
|
|
153
|
+
config.popId = popId;
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (Array.isArray(config.items)) {
|
|
158
|
+
updatePopId(oldId, popId, config as MPage);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
};
|
|
162
|
+
|
|
108
163
|
export type PropsService = Props;
|
|
109
164
|
|
|
110
165
|
export default new Props();
|