@tmagic/editor 1.0.0-beta.9 → 1.0.0-rc.11
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 +72 -36
- package/dist/tmagic-editor.es.js +1555 -2080
- package/dist/tmagic-editor.es.js.map +1 -1
- package/dist/tmagic-editor.umd.js +1555 -2082
- 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 -32
- package/dist/types/src/services/BaseService.d.ts +5 -2
- package/dist/types/src/services/editor.d.ts +11 -5
- 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 +24 -9
- package/dist/types/src/utils/editor.d.ts +4 -9
- package/dist/types/src/utils/polyfills.d.ts +0 -0
- package/dist/types/src/utils/props.d.ts +10 -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 +69 -30
- package/src/fields/UISelect.vue +14 -6
- package/src/index.ts +2 -0
- 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 +31 -54
- package/src/layouts/sidebar/Sidebar.vue +13 -52
- package/src/layouts/sidebar/TabPane.vue +68 -0
- package/src/layouts/workspace/PageBar.vue +136 -13
- package/src/layouts/workspace/Stage.vue +62 -104
- package/src/layouts/workspace/ViewerMenu.vue +119 -67
- package/src/layouts/workspace/Workspace.vue +124 -46
- package/src/services/BaseService.ts +71 -23
- package/src/services/editor.ts +196 -52
- 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 +1 -0
- package/src/theme/component-list-panel.scss +2 -0
- package/src/theme/content-menu.scss +33 -26
- package/src/theme/layer-panel.scss +2 -1
- package/src/theme/nav-menu.scss +6 -2
- package/src/theme/page-bar.scss +36 -3
- package/src/theme/props-panel.scss +19 -17
- package/src/theme/workspace.scss +4 -0
- package/src/type.ts +26 -9
- package/src/utils/editor.ts +35 -55
- package/src/utils/polyfills.ts +8 -0
- package/src/utils/props.ts +51 -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 -979
- package/dist/types/src/layouts/workspace/PageBar.vue.d.ts +0 -11
- package/dist/types/src/layouts/workspace/Stage.vue.d.ts +0 -193
- 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
|
@@ -22,8 +22,8 @@ import serialize from 'serialize-javascript';
|
|
|
22
22
|
|
|
23
23
|
import type { Id, MApp, MComponent, MContainer, MNode, MPage } from '@tmagic/schema';
|
|
24
24
|
import { NodeType } from '@tmagic/schema';
|
|
25
|
-
import
|
|
26
|
-
import { getNodePath, isPop } from '@tmagic/utils';
|
|
25
|
+
import StageCore from '@tmagic/stage';
|
|
26
|
+
import { getNodePath, isNumber, isPage, isPop } from '@tmagic/utils';
|
|
27
27
|
|
|
28
28
|
import historyService, { StepValue } from '@editor/services/history';
|
|
29
29
|
import propsService from '@editor/services/props';
|
|
@@ -33,11 +33,11 @@ import {
|
|
|
33
33
|
change2Fixed,
|
|
34
34
|
COPY_STORAGE_KEY,
|
|
35
35
|
Fixed2Other,
|
|
36
|
+
generatePageNameByApp,
|
|
36
37
|
getNodeIndex,
|
|
37
38
|
initPosition,
|
|
38
39
|
isFixed,
|
|
39
40
|
setLayout,
|
|
40
|
-
setNewItemId,
|
|
41
41
|
} from '@editor/utils/editor';
|
|
42
42
|
import { log } from '@editor/utils/logger';
|
|
43
43
|
|
|
@@ -54,24 +54,30 @@ class Editor extends BaseService {
|
|
|
54
54
|
stage: null,
|
|
55
55
|
highlightNode: null,
|
|
56
56
|
modifiedNodeIds: new Map(),
|
|
57
|
+
pageLength: 0,
|
|
57
58
|
});
|
|
58
59
|
|
|
59
60
|
constructor() {
|
|
60
|
-
super(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
+
);
|
|
75
81
|
}
|
|
76
82
|
|
|
77
83
|
/**
|
|
@@ -85,6 +91,7 @@ class Editor extends BaseService {
|
|
|
85
91
|
log('store set ', name, ' ', value);
|
|
86
92
|
|
|
87
93
|
if (name === 'root') {
|
|
94
|
+
this.state.pageLength = (value as unknown as MApp)?.items?.length || 0;
|
|
88
95
|
this.emit('root-change', value);
|
|
89
96
|
}
|
|
90
97
|
}
|
|
@@ -101,10 +108,14 @@ class Editor extends BaseService {
|
|
|
101
108
|
/**
|
|
102
109
|
* 根据id获取组件、组件的父组件以及组件所属的页面节点
|
|
103
110
|
* @param {number | string} id 组件id
|
|
111
|
+
* @param {boolean} raw 是否使用toRaw
|
|
104
112
|
* @returns {EditorNodeInfo}
|
|
105
113
|
*/
|
|
106
|
-
public getNodeInfo(id: Id): EditorNodeInfo {
|
|
107
|
-
|
|
114
|
+
public getNodeInfo(id: Id, raw = true): EditorNodeInfo {
|
|
115
|
+
let root = this.get<MApp | null>('root');
|
|
116
|
+
if (raw) {
|
|
117
|
+
root = toRaw(root);
|
|
118
|
+
}
|
|
108
119
|
if (!root) return {};
|
|
109
120
|
|
|
110
121
|
if (id === root.id) {
|
|
@@ -134,34 +145,38 @@ class Editor extends BaseService {
|
|
|
134
145
|
/**
|
|
135
146
|
* 根据ID获取指点节点配置
|
|
136
147
|
* @param id 组件ID
|
|
148
|
+
* @param {boolean} raw 是否使用toRaw
|
|
137
149
|
* @returns 组件节点配置
|
|
138
150
|
*/
|
|
139
|
-
public getNodeById(id: Id): MNode | undefined {
|
|
140
|
-
const { node } = this.getNodeInfo(id);
|
|
151
|
+
public getNodeById(id: Id, raw = true): MNode | undefined {
|
|
152
|
+
const { node } = this.getNodeInfo(id, raw);
|
|
141
153
|
return node;
|
|
142
154
|
}
|
|
143
155
|
|
|
144
156
|
/**
|
|
145
157
|
* 根据ID获取指点节点的父节点配置
|
|
146
158
|
* @param id 组件ID
|
|
159
|
+
* @param {boolean} raw 是否使用toRaw
|
|
147
160
|
* @returns 指点组件的父节点配置
|
|
148
161
|
*/
|
|
149
|
-
public getParentById(id: Id): MContainer | undefined {
|
|
162
|
+
public getParentById(id: Id, raw = true): MContainer | undefined {
|
|
150
163
|
if (!this.get<MApp | null>('root')) return;
|
|
151
|
-
const { parent } = this.getNodeInfo(id);
|
|
164
|
+
const { parent } = this.getNodeInfo(id, raw);
|
|
152
165
|
return parent;
|
|
153
166
|
}
|
|
154
167
|
|
|
155
168
|
/**
|
|
156
169
|
* 只有容器拥有布局
|
|
157
170
|
*/
|
|
158
|
-
public async getLayout(
|
|
159
|
-
if (node
|
|
160
|
-
|
|
171
|
+
public async getLayout(parent: MNode, node?: MNode): Promise<Layout> {
|
|
172
|
+
if (node && typeof node !== 'function' && isFixed(node)) return Layout.FIXED;
|
|
173
|
+
|
|
174
|
+
if (parent.layout) {
|
|
175
|
+
return parent.layout;
|
|
161
176
|
}
|
|
162
177
|
|
|
163
178
|
// 如果该节点没有设置position,则认为是流式布局,例如获取root的布局时
|
|
164
|
-
if (!
|
|
179
|
+
if (!parent.style?.position) {
|
|
165
180
|
return Layout.RELATIVE;
|
|
166
181
|
}
|
|
167
182
|
|
|
@@ -184,9 +199,59 @@ class Editor extends BaseService {
|
|
|
184
199
|
} else {
|
|
185
200
|
historyService.empty();
|
|
186
201
|
}
|
|
202
|
+
|
|
203
|
+
if (node?.id) {
|
|
204
|
+
this.get<StageCore>('stage')
|
|
205
|
+
?.renderer.runtime?.getApp?.()
|
|
206
|
+
?.emit(
|
|
207
|
+
'editor:select',
|
|
208
|
+
{
|
|
209
|
+
node,
|
|
210
|
+
page,
|
|
211
|
+
parent,
|
|
212
|
+
},
|
|
213
|
+
getNodePath(node.id, this.get<MApp>('root').items),
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
this.emit('select', node);
|
|
218
|
+
|
|
187
219
|
return node!;
|
|
188
220
|
}
|
|
189
221
|
|
|
222
|
+
public async selectNextNode(): Promise<MNode> | never {
|
|
223
|
+
const node = toRaw(this.get('node'));
|
|
224
|
+
|
|
225
|
+
if (!node || isPage(node) || node.type === NodeType.ROOT) return node;
|
|
226
|
+
|
|
227
|
+
const parent = toRaw(this.getParentById(node.id));
|
|
228
|
+
|
|
229
|
+
if (!parent) return node;
|
|
230
|
+
|
|
231
|
+
const index = getNodeIndex(node, parent);
|
|
232
|
+
|
|
233
|
+
const nextNode = parent.items[index + 1] || parent.items[0];
|
|
234
|
+
|
|
235
|
+
await this.select(nextNode);
|
|
236
|
+
this.get<StageCore>('stage')?.select(nextNode.id);
|
|
237
|
+
|
|
238
|
+
return nextNode;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
public async selectNextPage(): Promise<MNode> | never {
|
|
242
|
+
const root = toRaw(this.get<MApp>('root'));
|
|
243
|
+
const page = toRaw(this.get('page'));
|
|
244
|
+
|
|
245
|
+
const index = getNodeIndex(page, root);
|
|
246
|
+
|
|
247
|
+
const nextPage = root.items[index + 1] || root.items[0];
|
|
248
|
+
|
|
249
|
+
await this.select(nextPage);
|
|
250
|
+
this.get<StageCore>('stage')?.select(nextPage.id);
|
|
251
|
+
|
|
252
|
+
return nextPage;
|
|
253
|
+
}
|
|
254
|
+
|
|
190
255
|
/**
|
|
191
256
|
* 高亮指定节点
|
|
192
257
|
* @param config 指定节点配置或者ID
|
|
@@ -194,8 +259,8 @@ class Editor extends BaseService {
|
|
|
194
259
|
*/
|
|
195
260
|
public highlight(config: MNode | Id): void {
|
|
196
261
|
const { node } = this.selectedConfigExceptionHandler(config);
|
|
197
|
-
const
|
|
198
|
-
if (
|
|
262
|
+
const currentHighlightNode = this.get('highlightNode');
|
|
263
|
+
if (currentHighlightNode === node) return;
|
|
199
264
|
this.set('highlightNode', node);
|
|
200
265
|
}
|
|
201
266
|
|
|
@@ -205,12 +270,14 @@ class Editor extends BaseService {
|
|
|
205
270
|
* @param parent 要添加到的容器组件节点配置,如果不设置,默认为当前选中的组件的父节点
|
|
206
271
|
* @returns 添加后的节点
|
|
207
272
|
*/
|
|
208
|
-
public async add(
|
|
273
|
+
public async add(addNode: AddMNode, parent?: MContainer | null): Promise<MNode> {
|
|
274
|
+
const { type, ...config } = addNode;
|
|
209
275
|
const curNode = this.get<MContainer>('node');
|
|
210
276
|
|
|
211
277
|
let parentNode: MNode | undefined;
|
|
278
|
+
const isPage = type === NodeType.PAGE;
|
|
212
279
|
|
|
213
|
-
if (
|
|
280
|
+
if (isPage) {
|
|
214
281
|
parentNode = this.get<MApp>('root');
|
|
215
282
|
// 由于支持中间件扩展,在parent参数为undefined时,parent会变成next函数
|
|
216
283
|
} else if (parent && typeof parent !== 'function') {
|
|
@@ -218,13 +285,18 @@ class Editor extends BaseService {
|
|
|
218
285
|
} else if (curNode.items) {
|
|
219
286
|
parentNode = curNode;
|
|
220
287
|
} else {
|
|
221
|
-
parentNode = this.getParentById(curNode.id);
|
|
288
|
+
parentNode = this.getParentById(curNode.id, false);
|
|
222
289
|
}
|
|
223
290
|
|
|
224
291
|
if (!parentNode) throw new Error('未找到父元素');
|
|
225
292
|
|
|
226
|
-
const layout = await this.getLayout(parentNode);
|
|
227
|
-
const newNode = initPosition(
|
|
293
|
+
const layout = await this.getLayout(toRaw(parentNode), addNode as MNode);
|
|
294
|
+
const newNode = initPosition(
|
|
295
|
+
{ ...toRaw(await propsService.getPropsValue(type, config)) },
|
|
296
|
+
layout,
|
|
297
|
+
parentNode,
|
|
298
|
+
this.get<StageCore>('stage'),
|
|
299
|
+
);
|
|
228
300
|
|
|
229
301
|
if ((parentNode?.type === NodeType.ROOT || curNode.type === NodeType.ROOT) && newNode.type !== NodeType.PAGE) {
|
|
230
302
|
throw new Error('app下不能添加组件');
|
|
@@ -232,15 +304,25 @@ class Editor extends BaseService {
|
|
|
232
304
|
|
|
233
305
|
parentNode?.items?.push(newNode);
|
|
234
306
|
|
|
235
|
-
|
|
307
|
+
const stage = this.get<StageCore | null>('stage');
|
|
308
|
+
|
|
309
|
+
await stage?.add({ config: cloneDeep(newNode), root: cloneDeep(this.get('root')) });
|
|
236
310
|
|
|
237
311
|
await this.select(newNode);
|
|
238
312
|
|
|
239
313
|
this.addModifiedNodeId(newNode.id);
|
|
240
|
-
if (
|
|
314
|
+
if (!isPage) {
|
|
241
315
|
this.pushHistoryState();
|
|
242
316
|
}
|
|
243
317
|
|
|
318
|
+
stage?.select(newNode.id);
|
|
319
|
+
|
|
320
|
+
if (isPage) {
|
|
321
|
+
this.state.pageLength += 1;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
this.emit('add', newNode);
|
|
325
|
+
|
|
244
326
|
return newNode;
|
|
245
327
|
}
|
|
246
328
|
|
|
@@ -256,7 +338,7 @@ class Editor extends BaseService {
|
|
|
256
338
|
|
|
257
339
|
if (!root) throw new Error('没有root');
|
|
258
340
|
|
|
259
|
-
const { parent, node: curNode } = this.getNodeInfo(node.id);
|
|
341
|
+
const { parent, node: curNode } = this.getNodeInfo(node.id, false);
|
|
260
342
|
|
|
261
343
|
if (!parent || !curNode) throw new Error('找不要删除的节点');
|
|
262
344
|
|
|
@@ -265,17 +347,38 @@ class Editor extends BaseService {
|
|
|
265
347
|
if (typeof index !== 'number' || index === -1) throw new Error('找不要删除的节点');
|
|
266
348
|
|
|
267
349
|
parent.items?.splice(index, 1);
|
|
268
|
-
this.get<StageCore>('stage')
|
|
350
|
+
const stage = this.get<StageCore | null>('stage');
|
|
351
|
+
stage?.remove({ id: node.id, root: this.get('root') });
|
|
269
352
|
|
|
270
353
|
if (node.type === NodeType.PAGE) {
|
|
271
|
-
|
|
354
|
+
this.state.pageLength -= 1;
|
|
355
|
+
|
|
356
|
+
if (root.items[0]) {
|
|
357
|
+
await this.select(root.items[0]);
|
|
358
|
+
stage?.select(root.items[0].id);
|
|
359
|
+
} else {
|
|
360
|
+
this.set('node', null);
|
|
361
|
+
this.set('parent', null);
|
|
362
|
+
this.set('page', null);
|
|
363
|
+
this.set('stage', null);
|
|
364
|
+
this.set('highlightNode', null);
|
|
365
|
+
this.resetModifiedNodeId();
|
|
366
|
+
historyService.reset();
|
|
367
|
+
|
|
368
|
+
this.emit('remove', node);
|
|
369
|
+
|
|
370
|
+
return node;
|
|
371
|
+
}
|
|
272
372
|
} else {
|
|
273
373
|
await this.select(parent);
|
|
374
|
+
stage?.select(parent.id);
|
|
274
375
|
}
|
|
275
376
|
|
|
276
377
|
this.addModifiedNodeId(parent.id);
|
|
277
378
|
this.pushHistoryState();
|
|
278
379
|
|
|
380
|
+
this.emit('remove', node);
|
|
381
|
+
|
|
279
382
|
return node;
|
|
280
383
|
}
|
|
281
384
|
|
|
@@ -287,7 +390,7 @@ class Editor extends BaseService {
|
|
|
287
390
|
public async update(config: MNode): Promise<MNode> {
|
|
288
391
|
if (!config?.id) throw new Error('没有配置或者配置缺少id值');
|
|
289
392
|
|
|
290
|
-
const info = this.getNodeInfo(config.id);
|
|
393
|
+
const info = this.getNodeInfo(config.id, false);
|
|
291
394
|
|
|
292
395
|
if (!info.node) throw new Error(`获取不到id为${config.id}的节点`);
|
|
293
396
|
|
|
@@ -324,11 +427,11 @@ class Editor extends BaseService {
|
|
|
324
427
|
|
|
325
428
|
parentNodeItems[index] = newConfig;
|
|
326
429
|
|
|
327
|
-
if (newConfig.id === this.get('node').id) {
|
|
430
|
+
if (`${newConfig.id}` === `${this.get('node').id}`) {
|
|
328
431
|
this.set('node', newConfig);
|
|
329
432
|
}
|
|
330
433
|
|
|
331
|
-
this.get<StageCore>('stage')?.update({ config: cloneDeep(newConfig), root: this.get('root') });
|
|
434
|
+
this.get<StageCore | null>('stage')?.update({ config: cloneDeep(newConfig), root: this.get('root') });
|
|
332
435
|
|
|
333
436
|
if (newConfig.type === NodeType.PAGE) {
|
|
334
437
|
this.set('page', newConfig);
|
|
@@ -337,6 +440,8 @@ class Editor extends BaseService {
|
|
|
337
440
|
this.addModifiedNodeId(newConfig.id);
|
|
338
441
|
this.pushHistoryState();
|
|
339
442
|
|
|
443
|
+
this.emit('update', newConfig);
|
|
444
|
+
|
|
340
445
|
return newConfig;
|
|
341
446
|
}
|
|
342
447
|
|
|
@@ -359,7 +464,7 @@ class Editor extends BaseService {
|
|
|
359
464
|
await this.update(parent);
|
|
360
465
|
await this.select(node);
|
|
361
466
|
|
|
362
|
-
this.get<StageCore>('stage')?.update({ config: cloneDeep(node), root: this.get('root') });
|
|
467
|
+
this.get<StageCore | null>('stage')?.update({ config: cloneDeep(node), root: this.get('root') });
|
|
363
468
|
|
|
364
469
|
this.addModifiedNodeId(parent.id);
|
|
365
470
|
this.pushHistoryState();
|
|
@@ -395,7 +500,7 @@ class Editor extends BaseService {
|
|
|
395
500
|
return;
|
|
396
501
|
}
|
|
397
502
|
|
|
398
|
-
setNewItemId(config, this.get('root'));
|
|
503
|
+
await propsService.setNewItemId(config, this.get('root'));
|
|
399
504
|
if (config.style) {
|
|
400
505
|
config.style = {
|
|
401
506
|
...config.style,
|
|
@@ -403,6 +508,10 @@ class Editor extends BaseService {
|
|
|
403
508
|
};
|
|
404
509
|
}
|
|
405
510
|
|
|
511
|
+
if (isPage(config)) {
|
|
512
|
+
config.name = generatePageNameByApp(this.get('root'));
|
|
513
|
+
}
|
|
514
|
+
|
|
406
515
|
return await this.add(config);
|
|
407
516
|
}
|
|
408
517
|
|
|
@@ -414,17 +523,28 @@ class Editor extends BaseService {
|
|
|
414
523
|
public async alignCenter(config: MNode): Promise<MNode | void> {
|
|
415
524
|
const parent = this.get<MContainer>('parent');
|
|
416
525
|
const node = this.get<MNode>('node');
|
|
417
|
-
const layout = await this.getLayout(parent);
|
|
526
|
+
const layout = await this.getLayout(toRaw(parent), toRaw(node));
|
|
418
527
|
if (layout === Layout.RELATIVE) {
|
|
419
528
|
return;
|
|
420
529
|
}
|
|
421
530
|
|
|
422
|
-
if (
|
|
531
|
+
if (!node.style) return;
|
|
532
|
+
|
|
533
|
+
const stage = this.get<StageCore>('stage');
|
|
534
|
+
const doc = stage?.renderer.contentWindow?.document;
|
|
535
|
+
|
|
536
|
+
if (doc) {
|
|
537
|
+
const parentEl = doc.getElementById(`${parent.id}`);
|
|
538
|
+
const el = doc.getElementById(`${node.id}`);
|
|
539
|
+
if (parentEl && el) {
|
|
540
|
+
node.style.left = (parentEl.clientWidth - el.clientWidth) / 2;
|
|
541
|
+
}
|
|
542
|
+
} else if (parent.style && isNumber(parent.style?.width) && isNumber(node.style?.width)) {
|
|
423
543
|
node.style.left = (parent.style.width - node.style.width) / 2;
|
|
424
544
|
}
|
|
425
545
|
|
|
426
546
|
await this.update(node);
|
|
427
|
-
this.get<StageCore>('stage')?.update({ config: cloneDeep(toRaw(node)), root: this.get('root') });
|
|
547
|
+
this.get<StageCore | null>('stage')?.update({ config: cloneDeep(toRaw(node)), root: this.get('root') });
|
|
428
548
|
this.addModifiedNodeId(config.id);
|
|
429
549
|
this.pushHistoryState();
|
|
430
550
|
|
|
@@ -441,15 +561,15 @@ class Editor extends BaseService {
|
|
|
441
561
|
const brothers: MNode[] = parent?.items || [];
|
|
442
562
|
const index = brothers.findIndex((item) => `${item.id}` === `${node?.id}`);
|
|
443
563
|
|
|
444
|
-
if (offset === LayerOffset.
|
|
564
|
+
if (offset === LayerOffset.TOP) {
|
|
445
565
|
brothers.splice(brothers.length - 1, 0, brothers.splice(index, 1)[0]);
|
|
446
|
-
} else if (offset === LayerOffset.
|
|
566
|
+
} else if (offset === LayerOffset.BOTTOM) {
|
|
447
567
|
brothers.splice(0, 0, brothers.splice(index, 1)[0]);
|
|
448
568
|
} else {
|
|
449
569
|
brothers.splice(index + parseInt(`${offset}`, 10), 0, brothers.splice(index, 1)[0]);
|
|
450
570
|
}
|
|
451
571
|
|
|
452
|
-
this.get<StageCore>('stage')?.update({ config: cloneDeep(toRaw(parent)), root: this.get('root') });
|
|
572
|
+
this.get<StageCore | null>('stage')?.update({ config: cloneDeep(toRaw(parent)), root: this.get('root') });
|
|
453
573
|
}
|
|
454
574
|
|
|
455
575
|
/**
|
|
@@ -472,6 +592,26 @@ class Editor extends BaseService {
|
|
|
472
592
|
return value;
|
|
473
593
|
}
|
|
474
594
|
|
|
595
|
+
public async move(left: number, top: number) {
|
|
596
|
+
const node = toRaw(this.get('node'));
|
|
597
|
+
if (!node || isPage(node)) return;
|
|
598
|
+
|
|
599
|
+
const { style, id } = node;
|
|
600
|
+
if (!style || style.position !== 'absolute') return;
|
|
601
|
+
|
|
602
|
+
if (top && !isNumber(style.top)) return;
|
|
603
|
+
if (left && !isNumber(style.left)) return;
|
|
604
|
+
|
|
605
|
+
this.update({
|
|
606
|
+
id,
|
|
607
|
+
style: {
|
|
608
|
+
...style,
|
|
609
|
+
left: Number(style.left) + left,
|
|
610
|
+
top: Number(style.top) + top,
|
|
611
|
+
},
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
|
|
475
615
|
public destroy() {
|
|
476
616
|
this.removeAllListeners();
|
|
477
617
|
this.set('root', null);
|
|
@@ -508,7 +648,11 @@ class Editor extends BaseService {
|
|
|
508
648
|
this.isHistoryStateChange = true;
|
|
509
649
|
await this.update(value.data);
|
|
510
650
|
this.set('modifiedNodeIds', value.modifiedNodeIds);
|
|
511
|
-
setTimeout(() =>
|
|
651
|
+
setTimeout(async () => {
|
|
652
|
+
if (!value.nodeId) return;
|
|
653
|
+
await this.select(value.nodeId);
|
|
654
|
+
this.get<StageCore | null>('stage')?.select(value.nodeId);
|
|
655
|
+
}, 0);
|
|
512
656
|
}
|
|
513
657
|
|
|
514
658
|
private async toggleFixedPosition(dist: MNode, src: MNode, root: MApp) {
|
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();
|
package/src/services/ui.ts
CHANGED
|
@@ -25,6 +25,17 @@ import type { GetColumnWidth, SetColumnWidth, StageRect, UiState } from '@editor
|
|
|
25
25
|
|
|
26
26
|
import BaseService from './BaseService';
|
|
27
27
|
|
|
28
|
+
const DEFAULT_LEFT_COLUMN_WIDTH = 310;
|
|
29
|
+
const DEFAULT_RIGHT_COLUMN_WIDTH = 480;
|
|
30
|
+
|
|
31
|
+
const COLUMN_WIDTH_STORAGE_KEY = '$MagicEditorColumnWidthData';
|
|
32
|
+
|
|
33
|
+
const defaultColumnWidth = {
|
|
34
|
+
left: DEFAULT_LEFT_COLUMN_WIDTH,
|
|
35
|
+
center: globalThis.document.body.clientWidth - DEFAULT_LEFT_COLUMN_WIDTH - DEFAULT_RIGHT_COLUMN_WIDTH,
|
|
36
|
+
right: DEFAULT_RIGHT_COLUMN_WIDTH,
|
|
37
|
+
};
|
|
38
|
+
|
|
28
39
|
const state = reactive<UiState>({
|
|
29
40
|
uiSelectMode: false,
|
|
30
41
|
showSrc: false,
|
|
@@ -37,13 +48,10 @@ const state = reactive<UiState>({
|
|
|
37
48
|
width: 375,
|
|
38
49
|
height: 817,
|
|
39
50
|
},
|
|
40
|
-
columnWidth:
|
|
41
|
-
left: 310,
|
|
42
|
-
center: globalThis.document.body.clientWidth - 310 - 400,
|
|
43
|
-
right: 400,
|
|
44
|
-
},
|
|
51
|
+
columnWidth: defaultColumnWidth,
|
|
45
52
|
showGuides: true,
|
|
46
53
|
showRule: true,
|
|
54
|
+
propsPanelSize: 'small',
|
|
47
55
|
});
|
|
48
56
|
|
|
49
57
|
class Ui extends BaseService {
|
|
@@ -54,6 +62,16 @@ class Ui extends BaseService {
|
|
|
54
62
|
center: 'auto',
|
|
55
63
|
});
|
|
56
64
|
});
|
|
65
|
+
|
|
66
|
+
const columnWidthCacheData = globalThis.localStorage.getItem(COLUMN_WIDTH_STORAGE_KEY);
|
|
67
|
+
if (columnWidthCacheData) {
|
|
68
|
+
try {
|
|
69
|
+
const columnWidthCache = JSON.parse(columnWidthCacheData);
|
|
70
|
+
this.setColumnWidth(columnWidthCache);
|
|
71
|
+
} catch (e) {
|
|
72
|
+
console.error(e);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
57
75
|
}
|
|
58
76
|
|
|
59
77
|
public set<T = any>(name: keyof UiState, value: T) {
|
|
@@ -88,6 +106,11 @@ class Ui extends BaseService {
|
|
|
88
106
|
return (state as any)[name];
|
|
89
107
|
}
|
|
90
108
|
|
|
109
|
+
public zoom(zoom: number) {
|
|
110
|
+
this.set('zoom', (this.get<number>('zoom') * 100 + zoom * 100) / 100);
|
|
111
|
+
if (this.get<number>('zoom') < 0.1) this.set('zoom', 0.1);
|
|
112
|
+
}
|
|
113
|
+
|
|
91
114
|
private setColumnWidth({ left, center, right }: SetColumnWidth) {
|
|
92
115
|
const columnWidth = {
|
|
93
116
|
...toRaw(this.get<GetColumnWidth>('columnWidth')),
|
|
@@ -104,10 +127,17 @@ class Ui extends BaseService {
|
|
|
104
127
|
if (!center || center === 'auto') {
|
|
105
128
|
const bodyWidth = globalThis.document.body.clientWidth;
|
|
106
129
|
columnWidth.center = bodyWidth - (columnWidth?.left || 0) - (columnWidth?.right || 0);
|
|
130
|
+
if (columnWidth.center <= 0) {
|
|
131
|
+
columnWidth.left = defaultColumnWidth.left;
|
|
132
|
+
columnWidth.center = defaultColumnWidth.center;
|
|
133
|
+
columnWidth.right = defaultColumnWidth.right;
|
|
134
|
+
}
|
|
107
135
|
} else {
|
|
108
136
|
columnWidth.center = center;
|
|
109
137
|
}
|
|
110
138
|
|
|
139
|
+
globalThis.localStorage.setItem(COLUMN_WIDTH_STORAGE_KEY, JSON.stringify(columnWidth));
|
|
140
|
+
|
|
111
141
|
state.columnWidth = columnWidth;
|
|
112
142
|
}
|
|
113
143
|
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
top: 0;
|
|
13
13
|
left: 0;
|
|
14
14
|
box-sizing: border-box;
|
|
15
|
+
z-index: 1;
|
|
15
16
|
|
|
16
17
|
.el-input__prefix {
|
|
17
18
|
padding: 10px;
|
|
@@ -62,6 +63,7 @@
|
|
|
62
63
|
color: $--font-color;
|
|
63
64
|
flex-direction: column;
|
|
64
65
|
width: 42px;
|
|
66
|
+
cursor: pointer;
|
|
65
67
|
|
|
66
68
|
i {
|
|
67
69
|
font-size: 20px;
|