@tmagic/editor 1.1.0-beta.4 → 1.1.0-beta.7
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/{tmagic-editor.es.js → tmagic-editor.mjs} +1361 -1144
- package/dist/tmagic-editor.mjs.map +1 -0
- package/dist/tmagic-editor.umd.js +1375 -1158
- package/dist/tmagic-editor.umd.js.map +1 -1
- package/package.json +21 -19
- package/src/Editor.vue +14 -12
- package/src/components/ContentMenu.vue +87 -99
- package/src/components/ScrollViewer.vue +1 -1
- package/src/components/ToolButton.vue +150 -167
- package/src/fields/UISelect.vue +49 -70
- package/src/index.ts +2 -1
- package/src/layouts/AddPageBox.vue +2 -2
- package/src/layouts/Framework.vue +1 -1
- package/src/layouts/NavMenu.vue +2 -2
- package/src/layouts/PropsPanel.vue +1 -1
- package/src/layouts/Resizer.vue +1 -1
- package/src/layouts/sidebar/ComponentListPanel.vue +2 -2
- package/src/layouts/sidebar/LayerMenu.vue +2 -2
- package/src/layouts/sidebar/LayerPanel.vue +13 -4
- package/src/layouts/sidebar/Sidebar.vue +1 -1
- package/src/layouts/sidebar/TabPane.vue +2 -2
- package/src/layouts/workspace/PageBar.vue +63 -192
- package/src/layouts/workspace/PageBarScrollContainer.vue +111 -0
- package/src/layouts/workspace/Stage.vue +195 -222
- package/src/layouts/workspace/ViewerMenu.vue +142 -146
- package/src/layouts/workspace/Workspace.vue +11 -11
- package/src/services/BaseService.ts +1 -1
- package/src/services/componentList.ts +3 -3
- package/src/services/editor.ts +117 -135
- package/src/services/events.ts +1 -1
- package/src/services/history.ts +1 -1
- package/src/services/props.ts +62 -10
- package/src/services/storage.ts +133 -0
- package/src/services/ui.ts +2 -2
- package/src/type.ts +22 -6
- package/src/utils/config.ts +1 -1
- package/src/utils/editor.ts +1 -1
- package/src/utils/operator.ts +210 -0
- package/src/utils/props.ts +2 -24
- package/src/utils/scroll-viewer.ts +1 -1
- package/tsconfig.build.json +13 -0
- package/{dist/types/src → types}/Editor.vue.d.ts +7 -8
- package/types/components/ContentMenu.vue.d.ts +116 -0
- package/types/components/Icon.vue.d.ts +13 -0
- package/types/components/ScrollViewer.vue.d.ts +22 -0
- package/types/components/ToolButton.vue.d.ts +109 -0
- package/types/fields/Code.vue.d.ts +23 -0
- package/{dist/types/src → types}/fields/CodeLink.vue.d.ts +4 -6
- package/types/fields/UISelect.vue.d.ts +83 -0
- package/{dist/types/src → types}/index.d.ts +4 -1
- package/types/layouts/AddPageBox.vue.d.ts +4 -0
- package/{dist/types/src → types}/layouts/CodeEditor.vue.d.ts +11 -13
- package/types/layouts/Framework.vue.d.ts +22 -0
- package/types/layouts/NavMenu.vue.d.ts +25 -0
- package/types/layouts/PropsPanel.vue.d.ts +245 -0
- package/types/layouts/Resizer.vue.d.ts +12 -0
- package/types/layouts/sidebar/ComponentListPanel.vue.d.ts +14 -0
- package/types/layouts/sidebar/LayerMenu.vue.d.ts +100 -0
- package/types/layouts/sidebar/LayerPanel.vue.d.ts +1099 -0
- package/{dist/types/src → types}/layouts/sidebar/Sidebar.vue.d.ts +3 -5
- package/types/layouts/sidebar/TabPane.vue.d.ts +14 -0
- package/types/layouts/workspace/PageBar.vue.d.ts +54 -0
- package/types/layouts/workspace/PageBarScrollContainer.vue.d.ts +48 -0
- package/types/layouts/workspace/Stage.vue.d.ts +46 -0
- package/types/layouts/workspace/ViewerMenu.vue.d.ts +88 -0
- package/types/layouts/workspace/Workspace.vue.d.ts +6 -0
- package/{dist/types/src → types}/services/BaseService.d.ts +0 -0
- package/types/services/componentList.d.ts +14 -0
- package/{dist/types/src → types}/services/editor.d.ts +26 -11
- package/{dist/types/src → types}/services/events.d.ts +0 -0
- package/{dist/types/src → types}/services/history.d.ts +0 -0
- package/{dist/types/src → types}/services/props.d.ts +25 -1
- package/types/services/storage.d.ts +56 -0
- package/{dist/types/src → types}/services/ui.d.ts +1 -1
- package/{dist/types/src → types}/type.d.ts +15 -0
- package/{dist/types/src → types}/utils/compose.d.ts +1 -1
- package/{dist/types/src → types}/utils/config.d.ts +0 -0
- package/{dist/types/src → types}/utils/editor.d.ts +2 -2
- package/{dist/types/src → types}/utils/index.d.ts +0 -0
- package/{dist/types/src → types}/utils/logger.d.ts +0 -0
- package/types/utils/operator.d.ts +44 -0
- package/{dist/types/src → types}/utils/polyfills.d.ts +0 -0
- package/{dist/types/src → types}/utils/props.d.ts +2 -22
- package/{dist/types/src → types}/utils/scroll-viewer.d.ts +0 -0
- package/{dist/types/src → types}/utils/undo-redo.d.ts +0 -0
- package/dist/tmagic-editor.es.js.map +0 -1
- package/dist/types/src/components/Icon.vue.d.ts +0 -14
- package/dist/types/src/components/ScrollViewer.vue.d.ts +0 -24
- package/dist/types/src/fields/Code.vue.d.ts +0 -25
- package/dist/types/src/layouts/AddPageBox.vue.d.ts +0 -5
- package/dist/types/src/layouts/Framework.vue.d.ts +0 -24
- package/dist/types/src/layouts/NavMenu.vue.d.ts +0 -27
- package/dist/types/src/layouts/PropsPanel.vue.d.ts +0 -13
- package/dist/types/src/layouts/Resizer.vue.d.ts +0 -14
- package/dist/types/src/layouts/sidebar/ComponentListPanel.vue.d.ts +0 -16
- package/dist/types/src/layouts/sidebar/TabPane.vue.d.ts +0 -16
- package/dist/types/src/layouts/workspace/Workspace.vue.d.ts +0 -8
- package/dist/types/src/shims-vue.d.ts +0 -6
- package/dist/types/src/vite-env.d.ts +0 -1
- package/src/vite-env.d.ts +0 -1
package/src/services/editor.ts
CHANGED
|
@@ -17,45 +17,43 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { reactive, toRaw } from 'vue';
|
|
20
|
-
import { cloneDeep, mergeWith } from 'lodash-es';
|
|
21
|
-
import serialize from 'serialize-javascript';
|
|
20
|
+
import { cloneDeep, mergeWith, uniq } from 'lodash-es';
|
|
22
21
|
|
|
23
22
|
import type { Id, MApp, MComponent, MContainer, MNode, MPage } from '@tmagic/schema';
|
|
24
23
|
import { NodeType } from '@tmagic/schema';
|
|
25
24
|
import StageCore from '@tmagic/stage';
|
|
26
25
|
import { getNodePath, isNumber, isPage, isPop } from '@tmagic/utils';
|
|
27
26
|
|
|
28
|
-
import historyService, { StepValue } from '
|
|
29
|
-
import
|
|
30
|
-
import type { AddMNode, EditorNodeInfo, StoreState } from '
|
|
31
|
-
import { LayerOffset, Layout } from '
|
|
27
|
+
import historyService, { StepValue } from '../services/history';
|
|
28
|
+
import storageService, { Protocol } from '../services/storage';
|
|
29
|
+
import type { AddMNode, EditorNodeInfo, PastePosition, StoreState } from '../type';
|
|
30
|
+
import { LayerOffset, Layout } from '../type';
|
|
32
31
|
import {
|
|
33
32
|
change2Fixed,
|
|
34
33
|
COPY_STORAGE_KEY,
|
|
35
34
|
Fixed2Other,
|
|
36
|
-
fixNodeLeft,
|
|
37
|
-
generatePageNameByApp,
|
|
38
35
|
getInitPositionStyle,
|
|
39
36
|
getNodeIndex,
|
|
40
37
|
isFixed,
|
|
41
38
|
setLayout,
|
|
42
|
-
} from '
|
|
39
|
+
} from '../utils/editor';
|
|
40
|
+
import { beforeAdd, beforePaste, beforeRemove, notifyAddToStage } from '../utils/operator';
|
|
43
41
|
|
|
44
42
|
import BaseService from './BaseService';
|
|
45
43
|
|
|
46
44
|
class Editor extends BaseService {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
private state = reactive<StoreState>({
|
|
45
|
+
public state: StoreState = reactive({
|
|
50
46
|
root: null,
|
|
51
47
|
page: null,
|
|
52
48
|
parent: null,
|
|
53
49
|
node: null,
|
|
50
|
+
nodes: [],
|
|
54
51
|
stage: null,
|
|
55
52
|
highlightNode: null,
|
|
56
53
|
modifiedNodeIds: new Map(),
|
|
57
54
|
pageLength: 0,
|
|
58
55
|
});
|
|
56
|
+
private isHistoryStateChange = false;
|
|
59
57
|
|
|
60
58
|
constructor() {
|
|
61
59
|
super(
|
|
@@ -83,13 +81,16 @@ class Editor extends BaseService {
|
|
|
83
81
|
|
|
84
82
|
/**
|
|
85
83
|
* 设置当前指点节点配置
|
|
86
|
-
* @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode'
|
|
84
|
+
* @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes'
|
|
87
85
|
* @param value MNode
|
|
88
86
|
* @returns MNode
|
|
89
87
|
*/
|
|
90
88
|
public set<T = MNode>(name: keyof StoreState, value: T) {
|
|
91
89
|
this.state[name] = value as any;
|
|
92
|
-
|
|
90
|
+
// set nodes时将node设置为nodes第一个元素
|
|
91
|
+
if (name === 'nodes') {
|
|
92
|
+
this.set('node', (value as unknown as MNode[])[0]);
|
|
93
|
+
}
|
|
93
94
|
if (name === 'root') {
|
|
94
95
|
this.state.pageLength = (value as unknown as MApp)?.items?.length || 0;
|
|
95
96
|
this.emit('root-change', value);
|
|
@@ -98,7 +99,7 @@ class Editor extends BaseService {
|
|
|
98
99
|
|
|
99
100
|
/**
|
|
100
101
|
* 获取当前指点节点配置
|
|
101
|
-
* @param name 'root' | 'page' | 'parent' | 'node'
|
|
102
|
+
* @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes'
|
|
102
103
|
* @returns MNode
|
|
103
104
|
*/
|
|
104
105
|
public get<T = MNode>(name: keyof StoreState): T {
|
|
@@ -190,7 +191,7 @@ class Editor extends BaseService {
|
|
|
190
191
|
*/
|
|
191
192
|
public async select(config: MNode | Id): Promise<MNode> | never {
|
|
192
193
|
const { node, page, parent } = this.selectedConfigExceptionHandler(config);
|
|
193
|
-
this.set('
|
|
194
|
+
this.set('nodes', [node]);
|
|
194
195
|
this.set('page', page || null);
|
|
195
196
|
this.set('parent', parent || null);
|
|
196
197
|
|
|
@@ -265,66 +266,76 @@ class Editor extends BaseService {
|
|
|
265
266
|
}
|
|
266
267
|
|
|
267
268
|
/**
|
|
268
|
-
*
|
|
269
|
-
* @param
|
|
270
|
-
* @
|
|
271
|
-
* @returns 添加后的节点
|
|
269
|
+
* 多选
|
|
270
|
+
* @param ids 指定节点ID
|
|
271
|
+
* @returns 加入多选的节点配置
|
|
272
272
|
*/
|
|
273
|
-
public
|
|
274
|
-
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
273
|
+
public multiSelect(ids: Id[]): void {
|
|
274
|
+
const nodes: MNode[] = [];
|
|
275
|
+
const idsUnique = uniq(ids);
|
|
276
|
+
idsUnique.forEach((id) => {
|
|
277
|
+
const { node } = this.getNodeInfo(id);
|
|
278
|
+
if (!node) return;
|
|
279
|
+
nodes.push(node);
|
|
280
|
+
});
|
|
281
|
+
this.set('nodes', nodes);
|
|
282
|
+
}
|
|
280
283
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
284
|
+
/**
|
|
285
|
+
* 批量向容器添加节点
|
|
286
|
+
* @param configs 将要添加的节点数组
|
|
287
|
+
* @returns 添加后的节点
|
|
288
|
+
*/
|
|
289
|
+
public async multiAdd(configs: MNode[]): Promise<MNode[]> {
|
|
290
|
+
const stage = this.get<StageCore | null>('stage');
|
|
291
|
+
const newNodes: MNode[] = await Promise.all(
|
|
292
|
+
configs.map(async (configItem: MNode): Promise<MNode> => {
|
|
293
|
+
// 新增元素到配置
|
|
294
|
+
const { parentNode, newNode, layout } = await beforeAdd(configItem as AddMNode);
|
|
295
|
+
// 将新增元素事件通知到stage以更新渲染
|
|
296
|
+
await notifyAddToStage(parentNode, newNode, layout);
|
|
297
|
+
return newNode;
|
|
298
|
+
}),
|
|
299
|
+
);
|
|
300
|
+
const newNodeIds: Id[] = newNodes.map((node) => node.id);
|
|
291
301
|
|
|
292
|
-
|
|
302
|
+
// 增加历史记录 多选不可能选中page
|
|
303
|
+
this.addModifiedNodeId(newNodeIds.join('-'));
|
|
304
|
+
this.pushHistoryState();
|
|
293
305
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
newNode.style = getInitPositionStyle(newNode.style, layout, parentNode, this.get<StageCore>('stage'));
|
|
306
|
+
// 触发选中样式
|
|
307
|
+
stage?.multiSelect(newNodeIds);
|
|
297
308
|
|
|
298
|
-
|
|
299
|
-
throw new Error('app下不能添加组件');
|
|
300
|
-
}
|
|
309
|
+
this.emit('multiAdd', newNodes);
|
|
301
310
|
|
|
302
|
-
|
|
311
|
+
return newNodes;
|
|
312
|
+
}
|
|
303
313
|
|
|
314
|
+
/**
|
|
315
|
+
* 向指点容器添加组件节点
|
|
316
|
+
* @param addConfig 将要添加的组件节点配置
|
|
317
|
+
* @param parent 要添加到的容器组件节点配置,如果不设置,默认为当前选中的组件的父节点
|
|
318
|
+
* @returns 添加后的节点
|
|
319
|
+
*/
|
|
320
|
+
public async add(addNode: AddMNode, parent?: MContainer | null): Promise<MNode> {
|
|
304
321
|
const stage = this.get<StageCore | null>('stage');
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
const fixedLeft = fixNodeLeft(newNode, parentNode, stage?.renderer.contentWindow?.document);
|
|
311
|
-
if (typeof fixedLeft !== 'undefined') {
|
|
312
|
-
newNode.style.left = fixedLeft;
|
|
313
|
-
await stage?.update({ config: cloneDeep(newNode), root: cloneDeep(root) });
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
322
|
+
// 新增元素到配置
|
|
323
|
+
const { parentNode, newNode, layout, isPage } = await beforeAdd(addNode, parent);
|
|
324
|
+
// 将新增元素事件通知到stage以更新渲染
|
|
325
|
+
await notifyAddToStage(parentNode, newNode, layout);
|
|
326
|
+
// 更新编辑器选中元素
|
|
317
327
|
await this.select(newNode);
|
|
318
|
-
|
|
328
|
+
// 增加历史记录
|
|
319
329
|
this.addModifiedNodeId(newNode.id);
|
|
320
330
|
if (!isPage) {
|
|
321
331
|
this.pushHistoryState();
|
|
322
332
|
}
|
|
323
333
|
|
|
324
|
-
stage?.select(newNode.id);
|
|
325
|
-
|
|
326
334
|
if (isPage) {
|
|
327
335
|
this.state.pageLength += 1;
|
|
336
|
+
} else {
|
|
337
|
+
// 新增页面,这个时候页面还有渲染出来,此时select会出错,在runtime-ready的时候回去select
|
|
338
|
+
stage?.select(newNode.id);
|
|
328
339
|
}
|
|
329
340
|
|
|
330
341
|
this.emit('add', newNode);
|
|
@@ -337,50 +348,18 @@ class Editor extends BaseService {
|
|
|
337
348
|
* @param {Object} node
|
|
338
349
|
* @return {Object} 删除的组件配置
|
|
339
350
|
*/
|
|
340
|
-
public async remove(
|
|
341
|
-
if (
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
if (!root) throw new Error('没有root');
|
|
346
|
-
|
|
347
|
-
const { parent, node: curNode } = this.getNodeInfo(node.id, false);
|
|
348
|
-
|
|
349
|
-
if (!parent || !curNode) throw new Error('找不要删除的节点');
|
|
350
|
-
|
|
351
|
-
const index = getNodeIndex(curNode, parent);
|
|
352
|
-
|
|
353
|
-
if (typeof index !== 'number' || index === -1) throw new Error('找不要删除的节点');
|
|
354
|
-
|
|
355
|
-
parent.items?.splice(index, 1);
|
|
356
|
-
const stage = this.get<StageCore | null>('stage');
|
|
357
|
-
stage?.remove({ id: node.id, root: cloneDeep(this.get('root')) });
|
|
358
|
-
|
|
359
|
-
if (node.type === NodeType.PAGE) {
|
|
360
|
-
this.state.pageLength -= 1;
|
|
361
|
-
|
|
362
|
-
if (root.items[0]) {
|
|
363
|
-
await this.select(root.items[0]);
|
|
364
|
-
stage?.select(root.items[0].id);
|
|
365
|
-
} else {
|
|
366
|
-
this.set('node', null);
|
|
367
|
-
this.set('parent', null);
|
|
368
|
-
this.set('page', null);
|
|
369
|
-
this.set('stage', null);
|
|
370
|
-
this.set('highlightNode', null);
|
|
371
|
-
this.resetModifiedNodeId();
|
|
372
|
-
historyService.reset();
|
|
373
|
-
|
|
374
|
-
this.emit('remove', node);
|
|
375
|
-
|
|
376
|
-
return node;
|
|
377
|
-
}
|
|
378
|
-
} else {
|
|
379
|
-
await this.select(parent);
|
|
380
|
-
stage?.select(parent.id);
|
|
351
|
+
public async remove(nodeOrNodeList: MNode | MNode[]): Promise<MNode | MNode[]> {
|
|
352
|
+
if (Array.isArray(nodeOrNodeList)) {
|
|
353
|
+
// 多选批量删除
|
|
354
|
+
const nodes = nodeOrNodeList;
|
|
355
|
+
return this.multiRemove(nodes);
|
|
381
356
|
}
|
|
382
|
-
|
|
383
|
-
|
|
357
|
+
const node = nodeOrNodeList;
|
|
358
|
+
const removeParent = await beforeRemove(node);
|
|
359
|
+
// 删除的是页面
|
|
360
|
+
if (!removeParent) return node;
|
|
361
|
+
// 更新历史记录
|
|
362
|
+
this.addModifiedNodeId(removeParent.id);
|
|
384
363
|
this.pushHistoryState();
|
|
385
364
|
|
|
386
365
|
this.emit('remove', node);
|
|
@@ -388,6 +367,25 @@ class Editor extends BaseService {
|
|
|
388
367
|
return node;
|
|
389
368
|
}
|
|
390
369
|
|
|
370
|
+
/**
|
|
371
|
+
* 批量删除
|
|
372
|
+
* @param nodes 批量删除的节点
|
|
373
|
+
* @returns 批量删除的节点
|
|
374
|
+
*/
|
|
375
|
+
public async multiRemove(nodes: MNode[]): Promise<MNode[]> {
|
|
376
|
+
await Promise.all(
|
|
377
|
+
nodes.map(async (removeNode) => {
|
|
378
|
+
await beforeRemove(removeNode);
|
|
379
|
+
}),
|
|
380
|
+
);
|
|
381
|
+
const nodeIds = nodes.map((node) => node.id);
|
|
382
|
+
this.addModifiedNodeId(nodeIds.join('-'));
|
|
383
|
+
this.pushHistoryState();
|
|
384
|
+
|
|
385
|
+
this.emit('multiRemove', nodes);
|
|
386
|
+
return nodes;
|
|
387
|
+
}
|
|
388
|
+
|
|
391
389
|
/**
|
|
392
390
|
* 更新节点
|
|
393
391
|
* @param config 新的节点配置,配置中需要有id信息
|
|
@@ -433,9 +431,11 @@ class Editor extends BaseService {
|
|
|
433
431
|
|
|
434
432
|
parentNodeItems[index] = newConfig;
|
|
435
433
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
}
|
|
434
|
+
// 将update后的配置更新到nodes中
|
|
435
|
+
const nodes = this.get('nodes');
|
|
436
|
+
const targetIndex = nodes.findIndex((nodeItem: MNode) => `${nodeItem.id}` === `${newConfig.id}`);
|
|
437
|
+
nodes.splice(targetIndex, 1, newConfig);
|
|
438
|
+
this.set('nodes', nodes);
|
|
439
439
|
|
|
440
440
|
this.get<StageCore | null>('stage')?.update({ config: cloneDeep(newConfig), root: cloneDeep(this.get('root')) });
|
|
441
441
|
|
|
@@ -481,44 +481,25 @@ class Editor extends BaseService {
|
|
|
481
481
|
* @param config 组件节点配置
|
|
482
482
|
* @returns 组件节点配置
|
|
483
483
|
*/
|
|
484
|
-
public async copy(config: MNode): Promise<void> {
|
|
485
|
-
|
|
484
|
+
public async copy(config: MNode | MNode[]): Promise<void> {
|
|
485
|
+
await storageService.setItem(COPY_STORAGE_KEY, Array.isArray(config) ? config : [config], {
|
|
486
|
+
protocol: Protocol.OBJECT,
|
|
487
|
+
});
|
|
486
488
|
}
|
|
487
489
|
|
|
488
490
|
/**
|
|
489
491
|
* 从localStorage中获取节点,然后添加到当前容器中
|
|
490
|
-
* @param position
|
|
492
|
+
* @param position 粘贴的坐标
|
|
491
493
|
* @returns 添加后的组件节点配置
|
|
492
494
|
*/
|
|
493
|
-
public async paste(position:
|
|
494
|
-
const
|
|
495
|
-
// eslint-disable-next-line prefer-const
|
|
496
|
-
let config: any = {};
|
|
497
|
-
if (!configStr) {
|
|
498
|
-
return;
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
try {
|
|
502
|
-
// eslint-disable-next-line no-eval
|
|
503
|
-
eval(`config = ${configStr}`);
|
|
504
|
-
} catch (e) {
|
|
505
|
-
console.error(e);
|
|
506
|
-
return;
|
|
507
|
-
}
|
|
495
|
+
public async paste(position: PastePosition = {}): Promise<MNode[] | void> {
|
|
496
|
+
const config = await storageService.getItem(COPY_STORAGE_KEY);
|
|
508
497
|
|
|
509
|
-
|
|
510
|
-
if (config.style) {
|
|
511
|
-
config.style = {
|
|
512
|
-
...config.style,
|
|
513
|
-
...position,
|
|
514
|
-
};
|
|
515
|
-
}
|
|
498
|
+
if (!config) return;
|
|
516
499
|
|
|
517
|
-
|
|
518
|
-
config.name = generatePageNameByApp(this.get('root'));
|
|
519
|
-
}
|
|
500
|
+
const pasteConfigs = await beforePaste(position, config);
|
|
520
501
|
|
|
521
|
-
return await this.
|
|
502
|
+
return await this.multiAdd(pasteConfigs);
|
|
522
503
|
}
|
|
523
504
|
|
|
524
505
|
/**
|
|
@@ -672,6 +653,7 @@ class Editor extends BaseService {
|
|
|
672
653
|
this.removeAllListeners();
|
|
673
654
|
this.set('root', null);
|
|
674
655
|
this.set('node', null);
|
|
656
|
+
this.set('nodes', []);
|
|
675
657
|
this.set('page', null);
|
|
676
658
|
this.set('parent', null);
|
|
677
659
|
}
|
package/src/services/events.ts
CHANGED
|
@@ -22,7 +22,7 @@ import { cloneDeep } from 'lodash-es';
|
|
|
22
22
|
import { DEFAULT_EVENTS, DEFAULT_METHODS, EventOption } from '@tmagic/core';
|
|
23
23
|
import { toLine } from '@tmagic/utils';
|
|
24
24
|
|
|
25
|
-
import type { ComponentGroup } from '
|
|
25
|
+
import type { ComponentGroup } from '../type';
|
|
26
26
|
|
|
27
27
|
import BaseService from './BaseService';
|
|
28
28
|
|
package/src/services/history.ts
CHANGED
package/src/services/props.ts
CHANGED
|
@@ -17,15 +17,15 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { reactive } from 'vue';
|
|
20
|
-
import { cloneDeep, mergeWith
|
|
20
|
+
import { cloneDeep, mergeWith } from 'lodash-es';
|
|
21
21
|
|
|
22
22
|
import type { FormConfig } from '@tmagic/form';
|
|
23
23
|
import type { Id, MComponent, MNode, MPage } from '@tmagic/schema';
|
|
24
24
|
import { NodeType } from '@tmagic/schema';
|
|
25
25
|
import { isPop, toLine } from '@tmagic/utils';
|
|
26
26
|
|
|
27
|
-
import type { PropsState } from '
|
|
28
|
-
import { DEFAULT_CONFIG, fillConfig
|
|
27
|
+
import type { PropsState } from '../type';
|
|
28
|
+
import { DEFAULT_CONFIG, fillConfig } from '../utils/props';
|
|
29
29
|
|
|
30
30
|
import BaseService from './BaseService';
|
|
31
31
|
|
|
@@ -36,7 +36,15 @@ class Props extends BaseService {
|
|
|
36
36
|
});
|
|
37
37
|
|
|
38
38
|
constructor() {
|
|
39
|
-
super([
|
|
39
|
+
super([
|
|
40
|
+
'setPropsConfig',
|
|
41
|
+
'getPropsConfig',
|
|
42
|
+
'setPropsValue',
|
|
43
|
+
'getPropsValue',
|
|
44
|
+
'createId',
|
|
45
|
+
'setNewItemId',
|
|
46
|
+
'getDefaultPropsValue',
|
|
47
|
+
]);
|
|
40
48
|
}
|
|
41
49
|
|
|
42
50
|
public setPropsConfigs(configs: Record<string, FormConfig>) {
|
|
@@ -99,18 +107,39 @@ class Props extends BaseService {
|
|
|
99
107
|
return value;
|
|
100
108
|
}
|
|
101
109
|
|
|
102
|
-
const data =
|
|
103
|
-
|
|
104
|
-
|
|
110
|
+
const [id, defaultPropsValue, data] = await Promise.all([
|
|
111
|
+
this.createId(type),
|
|
112
|
+
this.getDefaultPropsValue(type),
|
|
113
|
+
this.setNewItemId(
|
|
114
|
+
cloneDeep({
|
|
115
|
+
type,
|
|
116
|
+
...defaultValue,
|
|
117
|
+
} as any),
|
|
118
|
+
),
|
|
119
|
+
]);
|
|
105
120
|
|
|
106
121
|
return {
|
|
107
|
-
|
|
108
|
-
...
|
|
122
|
+
id,
|
|
123
|
+
...defaultPropsValue,
|
|
124
|
+
...mergeWith({}, cloneDeep(this.state.propsValueMap[type] || {}), data),
|
|
109
125
|
};
|
|
110
126
|
}
|
|
111
127
|
|
|
128
|
+
/**
|
|
129
|
+
* 生成指定位数的GUID,无【-】格式
|
|
130
|
+
* @param digit 位数,默认值8
|
|
131
|
+
* @returns
|
|
132
|
+
*/
|
|
133
|
+
guid(digit = 8): string {
|
|
134
|
+
return 'x'.repeat(digit).replace(/[xy]/g, (c) => {
|
|
135
|
+
const r = (Math.random() * 16) | 0;
|
|
136
|
+
const v = c == 'x' ? r : (r & 0x3) | 0x8;
|
|
137
|
+
return v.toString(16);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
112
141
|
public async createId(type: string | number): Promise<string> {
|
|
113
|
-
return `${type}_${
|
|
142
|
+
return `${type}_${this.guid()}`;
|
|
114
143
|
}
|
|
115
144
|
|
|
116
145
|
/**
|
|
@@ -133,6 +162,29 @@ class Props extends BaseService {
|
|
|
133
162
|
await this.setNewItemId(item, config as MPage);
|
|
134
163
|
}
|
|
135
164
|
}
|
|
165
|
+
|
|
166
|
+
return config;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* 获取默认属性配置
|
|
171
|
+
* @param type 组件类型
|
|
172
|
+
* @returns Object
|
|
173
|
+
*/
|
|
174
|
+
public async getDefaultPropsValue(type: string) {
|
|
175
|
+
return ['page', 'container'].includes(type)
|
|
176
|
+
? {
|
|
177
|
+
type,
|
|
178
|
+
layout: 'absolute',
|
|
179
|
+
style: {},
|
|
180
|
+
name: type,
|
|
181
|
+
items: [],
|
|
182
|
+
}
|
|
183
|
+
: {
|
|
184
|
+
type,
|
|
185
|
+
style: {},
|
|
186
|
+
name: type,
|
|
187
|
+
};
|
|
136
188
|
}
|
|
137
189
|
}
|
|
138
190
|
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import serialize from 'serialize-javascript';
|
|
2
|
+
|
|
3
|
+
import BaseService from './BaseService';
|
|
4
|
+
|
|
5
|
+
interface Options {
|
|
6
|
+
namespace?: string;
|
|
7
|
+
protocol?: Protocol;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export enum Protocol {
|
|
11
|
+
OBJECT = 'object',
|
|
12
|
+
JSON = 'json',
|
|
13
|
+
STRING = 'string',
|
|
14
|
+
NUMBER = 'number',
|
|
15
|
+
BOOLEAN = 'boolean',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 数据存储服务
|
|
20
|
+
*/
|
|
21
|
+
export class WebStorage extends BaseService {
|
|
22
|
+
private storage: Storage = globalThis.localStorage;
|
|
23
|
+
private namespace = 'tmagic';
|
|
24
|
+
|
|
25
|
+
constructor() {
|
|
26
|
+
super(['getStorage', 'getNamespace', 'clear', 'getItem', 'removeItem', 'setItem']);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 获取数据存储对象,可以通过
|
|
31
|
+
* const storageService = new StorageService();
|
|
32
|
+
* storageService.usePlugin({
|
|
33
|
+
* // 替换存储对象为 sessionStorage
|
|
34
|
+
* async afterGetStorage(): Promise<Storage> {
|
|
35
|
+
* return window.sessionStorage;
|
|
36
|
+
* },
|
|
37
|
+
* });
|
|
38
|
+
*/
|
|
39
|
+
public async getStorage(): Promise<Storage> {
|
|
40
|
+
return this.storage;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public async getNamespace(): Promise<string> {
|
|
44
|
+
return this.namespace;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 清理,支持storageService.usePlugin
|
|
49
|
+
*/
|
|
50
|
+
public async clear(): Promise<void> {
|
|
51
|
+
const storage = await this.getStorage();
|
|
52
|
+
storage.clear();
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 获取存储项,支持storageService.usePlugin
|
|
56
|
+
*/
|
|
57
|
+
public async getItem(key: string, options: Options = {}): Promise<any> {
|
|
58
|
+
const [storage, namespace] = await Promise.all([this.getStorage(), this.getNamespace()]);
|
|
59
|
+
const { protocol = options.protocol, item } = this.getValueAndProtocol(
|
|
60
|
+
storage.getItem(`${options.namespace || namespace}:${key}`),
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
if (item === null) return null;
|
|
64
|
+
|
|
65
|
+
switch (protocol) {
|
|
66
|
+
case Protocol.OBJECT:
|
|
67
|
+
// eslint-disable-next-line no-eval
|
|
68
|
+
return eval(`(${item})`);
|
|
69
|
+
case Protocol.JSON:
|
|
70
|
+
return JSON.parse(item);
|
|
71
|
+
case Protocol.NUMBER:
|
|
72
|
+
return Number(item);
|
|
73
|
+
case Protocol.BOOLEAN:
|
|
74
|
+
return Boolean(item);
|
|
75
|
+
default:
|
|
76
|
+
return item;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* 获取指定索引位置的key
|
|
81
|
+
*/
|
|
82
|
+
public async key(index: number): Promise<string | null> {
|
|
83
|
+
const storage = await this.getStorage();
|
|
84
|
+
return storage.key(index);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 移除存储项,支持storageService.usePlugin
|
|
89
|
+
*/
|
|
90
|
+
public async removeItem(key: string, options: Options = {}): Promise<void> {
|
|
91
|
+
const [storage, namespace] = await Promise.all([this.getStorage(), this.getNamespace()]);
|
|
92
|
+
storage.removeItem(`${options.namespace || namespace}:${key}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* 设置存储项,支持storageService.usePlugin
|
|
97
|
+
*/
|
|
98
|
+
public async setItem(key: string, value: any, options: Options = {}): Promise<void> {
|
|
99
|
+
const [storage, namespace] = await Promise.all([this.getStorage(), this.getNamespace()]);
|
|
100
|
+
let item = value;
|
|
101
|
+
const protocol = options.protocol ? `${options.protocol}:` : '';
|
|
102
|
+
if (typeof value === Protocol.STRING || typeof value === Protocol.NUMBER) {
|
|
103
|
+
item = `${protocol}${value}`;
|
|
104
|
+
} else {
|
|
105
|
+
item = `${protocol}${serialize(value)}`;
|
|
106
|
+
}
|
|
107
|
+
storage.setItem(`${options.namespace || namespace}:${key}`, item);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private getValueAndProtocol(value: string | null) {
|
|
111
|
+
let protocol = '';
|
|
112
|
+
|
|
113
|
+
if (value === null) {
|
|
114
|
+
return {
|
|
115
|
+
item: value,
|
|
116
|
+
protocol,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const item = value.replace(new RegExp(`^(${Object.values(Protocol).join('|')})(:)(.+)`), (_$0, $1, _$2, $3) => {
|
|
121
|
+
protocol = $1;
|
|
122
|
+
return $3;
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
protocol,
|
|
127
|
+
item,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export type StorageService = WebStorage;
|
|
133
|
+
export default new WebStorage();
|
package/src/services/ui.ts
CHANGED
|
@@ -20,8 +20,8 @@ import { reactive, toRaw } from 'vue';
|
|
|
20
20
|
|
|
21
21
|
import type StageCore from '@tmagic/stage';
|
|
22
22
|
|
|
23
|
-
import editorService from '
|
|
24
|
-
import type { GetColumnWidth, SetColumnWidth, StageRect, UiState } from '
|
|
23
|
+
import editorService from '../services/editor';
|
|
24
|
+
import type { GetColumnWidth, SetColumnWidth, StageRect, UiState } from '../type';
|
|
25
25
|
|
|
26
26
|
import BaseService from './BaseService';
|
|
27
27
|
|