@tmagic/editor 1.1.0-beta.3 → 1.1.0-beta.6
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 +1 -1
- package/dist/{tmagic-editor.es.js → tmagic-editor.mjs} +1342 -1130
- package/dist/tmagic-editor.mjs.map +1 -0
- package/dist/tmagic-editor.umd.js +1357 -1145
- 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 +2 -2
- package/src/components/ToolButton.vue +150 -167
- package/src/fields/UISelect.vue +50 -70
- package/src/index.ts +1 -0
- 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 +53 -64
- package/src/layouts/workspace/Stage.vue +195 -220
- 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 +118 -135
- package/src/services/events.ts +1 -1
- package/src/services/history.ts +1 -1
- package/src/services/props.ts +63 -11
- package/src/services/storage.ts +133 -0
- package/src/services/ui.ts +10 -4
- package/src/theme/framework.scss +1 -1
- package/src/theme/stage.scss +2 -2
- package/src/type.ts +23 -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 +87 -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/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 +26 -2
- 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 +16 -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,65 +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
|
-
const
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
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
|
+
}
|
|
279
283
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
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);
|
|
290
301
|
|
|
291
|
-
|
|
302
|
+
// 增加历史记录 多选不可能选中page
|
|
303
|
+
this.addModifiedNodeId(newNodeIds.join('-'));
|
|
304
|
+
this.pushHistoryState();
|
|
292
305
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
newNode.style = getInitPositionStyle(newNode.style, layout, parentNode, this.get<StageCore>('stage'));
|
|
306
|
+
// 触发选中样式
|
|
307
|
+
stage?.multiSelect(newNodeIds);
|
|
296
308
|
|
|
297
|
-
|
|
298
|
-
throw new Error('app下不能添加组件');
|
|
299
|
-
}
|
|
309
|
+
this.emit('multiAdd', newNodes);
|
|
300
310
|
|
|
301
|
-
|
|
311
|
+
return newNodes;
|
|
312
|
+
}
|
|
302
313
|
|
|
314
|
+
/**
|
|
315
|
+
* 向指点容器添加组件节点
|
|
316
|
+
* @param addConfig 将要添加的组件节点配置
|
|
317
|
+
* @param parent 要添加到的容器组件节点配置,如果不设置,默认为当前选中的组件的父节点
|
|
318
|
+
* @returns 添加后的节点
|
|
319
|
+
*/
|
|
320
|
+
public async add(addNode: AddMNode, parent?: MContainer | null): Promise<MNode> {
|
|
303
321
|
const stage = this.get<StageCore | null>('stage');
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
const fixedLeft = fixNodeLeft(newNode, parentNode, stage?.renderer.contentWindow?.document);
|
|
310
|
-
if (typeof fixedLeft !== 'undefined') {
|
|
311
|
-
newNode.style.left = fixedLeft;
|
|
312
|
-
await stage?.update({ config: cloneDeep(newNode), root: cloneDeep(root) });
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
322
|
+
// 新增元素到配置
|
|
323
|
+
const { parentNode, newNode, layout, isPage } = await beforeAdd(addNode, parent);
|
|
324
|
+
// 将新增元素事件通知到stage以更新渲染
|
|
325
|
+
await notifyAddToStage(parentNode, newNode, layout);
|
|
326
|
+
// 更新编辑器选中元素
|
|
316
327
|
await this.select(newNode);
|
|
317
|
-
|
|
328
|
+
// 增加历史记录
|
|
318
329
|
this.addModifiedNodeId(newNode.id);
|
|
319
330
|
if (!isPage) {
|
|
320
331
|
this.pushHistoryState();
|
|
321
332
|
}
|
|
322
333
|
|
|
323
|
-
stage?.select(newNode.id);
|
|
324
|
-
|
|
325
334
|
if (isPage) {
|
|
326
335
|
this.state.pageLength += 1;
|
|
336
|
+
} else {
|
|
337
|
+
// 新增页面,这个时候页面还有渲染出来,此时select会出错,在runtime-ready的时候回去select
|
|
338
|
+
stage?.select(newNode.id);
|
|
327
339
|
}
|
|
328
340
|
|
|
329
341
|
this.emit('add', newNode);
|
|
@@ -336,50 +348,18 @@ class Editor extends BaseService {
|
|
|
336
348
|
* @param {Object} node
|
|
337
349
|
* @return {Object} 删除的组件配置
|
|
338
350
|
*/
|
|
339
|
-
public async remove(
|
|
340
|
-
if (
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
if (!root) throw new Error('没有root');
|
|
345
|
-
|
|
346
|
-
const { parent, node: curNode } = this.getNodeInfo(node.id, false);
|
|
347
|
-
|
|
348
|
-
if (!parent || !curNode) throw new Error('找不要删除的节点');
|
|
349
|
-
|
|
350
|
-
const index = getNodeIndex(curNode, parent);
|
|
351
|
-
|
|
352
|
-
if (typeof index !== 'number' || index === -1) throw new Error('找不要删除的节点');
|
|
353
|
-
|
|
354
|
-
parent.items?.splice(index, 1);
|
|
355
|
-
const stage = this.get<StageCore | null>('stage');
|
|
356
|
-
stage?.remove({ id: node.id, root: cloneDeep(this.get('root')) });
|
|
357
|
-
|
|
358
|
-
if (node.type === NodeType.PAGE) {
|
|
359
|
-
this.state.pageLength -= 1;
|
|
360
|
-
|
|
361
|
-
if (root.items[0]) {
|
|
362
|
-
await this.select(root.items[0]);
|
|
363
|
-
stage?.select(root.items[0].id);
|
|
364
|
-
} else {
|
|
365
|
-
this.set('node', null);
|
|
366
|
-
this.set('parent', null);
|
|
367
|
-
this.set('page', null);
|
|
368
|
-
this.set('stage', null);
|
|
369
|
-
this.set('highlightNode', null);
|
|
370
|
-
this.resetModifiedNodeId();
|
|
371
|
-
historyService.reset();
|
|
372
|
-
|
|
373
|
-
this.emit('remove', node);
|
|
374
|
-
|
|
375
|
-
return node;
|
|
376
|
-
}
|
|
377
|
-
} else {
|
|
378
|
-
await this.select(parent);
|
|
379
|
-
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);
|
|
380
356
|
}
|
|
381
|
-
|
|
382
|
-
|
|
357
|
+
const node = nodeOrNodeList;
|
|
358
|
+
const removeParent = await beforeRemove(node);
|
|
359
|
+
// 删除的是页面
|
|
360
|
+
if (!removeParent) return node;
|
|
361
|
+
// 更新历史记录
|
|
362
|
+
this.addModifiedNodeId(removeParent.id);
|
|
383
363
|
this.pushHistoryState();
|
|
384
364
|
|
|
385
365
|
this.emit('remove', node);
|
|
@@ -387,6 +367,25 @@ class Editor extends BaseService {
|
|
|
387
367
|
return node;
|
|
388
368
|
}
|
|
389
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
|
+
|
|
390
389
|
/**
|
|
391
390
|
* 更新节点
|
|
392
391
|
* @param config 新的节点配置,配置中需要有id信息
|
|
@@ -432,9 +431,11 @@ class Editor extends BaseService {
|
|
|
432
431
|
|
|
433
432
|
parentNodeItems[index] = newConfig;
|
|
434
433
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
}
|
|
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);
|
|
438
439
|
|
|
439
440
|
this.get<StageCore | null>('stage')?.update({ config: cloneDeep(newConfig), root: cloneDeep(this.get('root')) });
|
|
440
441
|
|
|
@@ -480,44 +481,25 @@ class Editor extends BaseService {
|
|
|
480
481
|
* @param config 组件节点配置
|
|
481
482
|
* @returns 组件节点配置
|
|
482
483
|
*/
|
|
483
|
-
public async copy(config: MNode): Promise<void> {
|
|
484
|
-
|
|
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
|
+
});
|
|
485
488
|
}
|
|
486
489
|
|
|
487
490
|
/**
|
|
488
491
|
* 从localStorage中获取节点,然后添加到当前容器中
|
|
489
|
-
* @param position
|
|
492
|
+
* @param position 粘贴的坐标
|
|
490
493
|
* @returns 添加后的组件节点配置
|
|
491
494
|
*/
|
|
492
|
-
public async paste(position:
|
|
493
|
-
const
|
|
494
|
-
// eslint-disable-next-line prefer-const
|
|
495
|
-
let config: any = {};
|
|
496
|
-
if (!configStr) {
|
|
497
|
-
return;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
try {
|
|
501
|
-
// eslint-disable-next-line no-eval
|
|
502
|
-
eval(`config = ${configStr}`);
|
|
503
|
-
} catch (e) {
|
|
504
|
-
console.error(e);
|
|
505
|
-
return;
|
|
506
|
-
}
|
|
495
|
+
public async paste(position: PastePosition = {}): Promise<MNode[] | void> {
|
|
496
|
+
const config = await storageService.getItem(COPY_STORAGE_KEY);
|
|
507
497
|
|
|
508
|
-
|
|
509
|
-
if (config.style) {
|
|
510
|
-
config.style = {
|
|
511
|
-
...config.style,
|
|
512
|
-
...position,
|
|
513
|
-
};
|
|
514
|
-
}
|
|
498
|
+
if (!config) return;
|
|
515
499
|
|
|
516
|
-
|
|
517
|
-
config.name = generatePageNameByApp(this.get('root'));
|
|
518
|
-
}
|
|
500
|
+
const pasteConfigs = await beforePaste(position, config);
|
|
519
501
|
|
|
520
|
-
return await this.
|
|
502
|
+
return await this.multiAdd(pasteConfigs);
|
|
521
503
|
}
|
|
522
504
|
|
|
523
505
|
/**
|
|
@@ -539,8 +521,8 @@ class Editor extends BaseService {
|
|
|
539
521
|
const doc = stage?.renderer.contentWindow?.document;
|
|
540
522
|
|
|
541
523
|
if (doc) {
|
|
542
|
-
const parentEl = doc.getElementById(`${parent.id}`);
|
|
543
524
|
const el = doc.getElementById(`${node.id}`);
|
|
525
|
+
const parentEl = el?.offsetParent;
|
|
544
526
|
if (parentEl && el) {
|
|
545
527
|
node.style.left = (parentEl.clientWidth - el.clientWidth) / 2;
|
|
546
528
|
}
|
|
@@ -671,6 +653,7 @@ class Editor extends BaseService {
|
|
|
671
653
|
this.removeAllListeners();
|
|
672
654
|
this.set('root', null);
|
|
673
655
|
this.set('node', null);
|
|
656
|
+
this.set('nodes', []);
|
|
674
657
|
this.set('page', null);
|
|
675
658
|
this.set('parent', null);
|
|
676
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>) {
|
|
@@ -88,7 +96,7 @@ class Props extends BaseService {
|
|
|
88
96
|
* @param type 组件类型
|
|
89
97
|
* @returns 组件初始值
|
|
90
98
|
*/
|
|
91
|
-
public async getPropsValue(type: string, defaultValue: Record<string, any> = {}) {
|
|
99
|
+
public async getPropsValue(type: string, { inputEvent, ...defaultValue }: Record<string, any> = {}) {
|
|
92
100
|
if (type === 'area') {
|
|
93
101
|
const value = (await this.getPropsValue('button')) as MComponent;
|
|
94
102
|
value.className = 'action-area';
|
|
@@ -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
|
|
|
@@ -155,10 +155,16 @@ class Ui extends BaseService {
|
|
|
155
155
|
const { stageRect, stageContainerRect } = state;
|
|
156
156
|
const { height, width } = stageContainerRect;
|
|
157
157
|
if (!width || !height) return 1;
|
|
158
|
-
|
|
158
|
+
|
|
159
|
+
// 30为标尺的大小
|
|
160
|
+
const stageWidth = stageRect.width + 30;
|
|
161
|
+
const stageHeight = stageRect.height + 30;
|
|
162
|
+
|
|
163
|
+
if (width > stageWidth && height > stageHeight) {
|
|
159
164
|
return 1;
|
|
160
165
|
}
|
|
161
|
-
|
|
166
|
+
// 60/80是为了不要让画布太过去贴住四周(这样好看些)
|
|
167
|
+
return Math.min((width - 60) / stageWidth || 1, (height - 80) / stageHeight || 1);
|
|
162
168
|
}
|
|
163
169
|
}
|
|
164
170
|
|