@tmagic/editor 1.1.0-beta.5 → 1.1.0-beta.8
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 +4 -0
- package/dist/{tmagic-editor.es.js → tmagic-editor.mjs} +1417 -1228
- package/dist/tmagic-editor.mjs.map +1 -0
- package/dist/tmagic-editor.umd.js +1433 -1242
- 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/Icon.vue +1 -1
- 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 +3 -3
- 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 +140 -147
- 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 +137 -139
- package/src/services/events.ts +1 -1
- package/src/services/history.ts +1 -1
- package/src/services/props.ts +18 -5
- package/src/services/storage.ts +133 -0
- package/src/services/ui.ts +2 -2
- package/src/theme/component-list-panel.scss +5 -0
- package/src/type.ts +22 -6
- package/src/utils/config.ts +1 -1
- package/src/utils/editor.ts +18 -11
- package/src/utils/operator.ts +163 -0
- package/src/utils/props.ts +2 -2
- 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 +22 -12
- 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 +6 -0
- 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 +6 -3
- 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 +27 -0
- package/{dist/types/src → types}/utils/polyfills.d.ts +0 -0
- package/{dist/types/src → types}/utils/props.d.ts +2 -2
- 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,51 +17,52 @@
|
|
|
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
|
-
|
|
37
|
-
generatePageNameByApp,
|
|
35
|
+
fixNodePosition,
|
|
38
36
|
getInitPositionStyle,
|
|
39
37
|
getNodeIndex,
|
|
40
38
|
isFixed,
|
|
41
39
|
setLayout,
|
|
42
|
-
} from '
|
|
40
|
+
} from '../utils/editor';
|
|
41
|
+
import { beforePaste, beforeRemove, getAddParent } from '../utils/operator';
|
|
43
42
|
|
|
44
43
|
import BaseService from './BaseService';
|
|
44
|
+
import propsService from './props';
|
|
45
45
|
|
|
46
46
|
class Editor extends BaseService {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
private state = reactive<StoreState>({
|
|
47
|
+
public state: StoreState = reactive({
|
|
50
48
|
root: null,
|
|
51
49
|
page: null,
|
|
52
50
|
parent: null,
|
|
53
51
|
node: null,
|
|
52
|
+
nodes: [],
|
|
54
53
|
stage: null,
|
|
55
54
|
highlightNode: null,
|
|
56
55
|
modifiedNodeIds: new Map(),
|
|
57
56
|
pageLength: 0,
|
|
58
57
|
});
|
|
58
|
+
private isHistoryStateChange = false;
|
|
59
59
|
|
|
60
60
|
constructor() {
|
|
61
61
|
super(
|
|
62
62
|
[
|
|
63
63
|
'getLayout',
|
|
64
64
|
'select',
|
|
65
|
+
'doAdd',
|
|
65
66
|
'add',
|
|
66
67
|
'remove',
|
|
67
68
|
'update',
|
|
@@ -83,13 +84,16 @@ class Editor extends BaseService {
|
|
|
83
84
|
|
|
84
85
|
/**
|
|
85
86
|
* 设置当前指点节点配置
|
|
86
|
-
* @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode'
|
|
87
|
+
* @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes'
|
|
87
88
|
* @param value MNode
|
|
88
89
|
* @returns MNode
|
|
89
90
|
*/
|
|
90
91
|
public set<T = MNode>(name: keyof StoreState, value: T) {
|
|
91
92
|
this.state[name] = value as any;
|
|
92
|
-
|
|
93
|
+
// set nodes时将node设置为nodes第一个元素
|
|
94
|
+
if (name === 'nodes') {
|
|
95
|
+
this.set('node', (value as unknown as MNode[])[0]);
|
|
96
|
+
}
|
|
93
97
|
if (name === 'root') {
|
|
94
98
|
this.state.pageLength = (value as unknown as MApp)?.items?.length || 0;
|
|
95
99
|
this.emit('root-change', value);
|
|
@@ -98,7 +102,7 @@ class Editor extends BaseService {
|
|
|
98
102
|
|
|
99
103
|
/**
|
|
100
104
|
* 获取当前指点节点配置
|
|
101
|
-
* @param name 'root' | 'page' | 'parent' | 'node'
|
|
105
|
+
* @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes'
|
|
102
106
|
* @returns MNode
|
|
103
107
|
*/
|
|
104
108
|
public get<T = MNode>(name: keyof StoreState): T {
|
|
@@ -190,7 +194,7 @@ class Editor extends BaseService {
|
|
|
190
194
|
*/
|
|
191
195
|
public async select(config: MNode | Id): Promise<MNode> | never {
|
|
192
196
|
const { node, page, parent } = this.selectedConfigExceptionHandler(config);
|
|
193
|
-
this.set('
|
|
197
|
+
this.set('nodes', [node]);
|
|
194
198
|
this.set('page', page || null);
|
|
195
199
|
this.set('parent', parent || null);
|
|
196
200
|
|
|
@@ -265,123 +269,113 @@ class Editor extends BaseService {
|
|
|
265
269
|
}
|
|
266
270
|
|
|
267
271
|
/**
|
|
268
|
-
*
|
|
269
|
-
* @param
|
|
270
|
-
* @
|
|
271
|
-
* @returns 添加后的节点
|
|
272
|
+
* 多选
|
|
273
|
+
* @param ids 指定节点ID
|
|
274
|
+
* @returns 加入多选的节点配置
|
|
272
275
|
*/
|
|
273
|
-
public
|
|
274
|
-
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
// 由于支持中间件扩展,在parent参数为undefined时,parent会变成next函数
|
|
284
|
-
} else if (parent && typeof parent !== 'function') {
|
|
285
|
-
parentNode = parent;
|
|
286
|
-
} else if (curNode.items) {
|
|
287
|
-
parentNode = curNode;
|
|
288
|
-
} else {
|
|
289
|
-
parentNode = this.getParentById(curNode.id, false);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
if (!parentNode) throw new Error('未找到父元素');
|
|
276
|
+
public multiSelect(ids: Id[]): void {
|
|
277
|
+
const nodes: MNode[] = [];
|
|
278
|
+
const idsUnique = uniq(ids);
|
|
279
|
+
idsUnique.forEach((id) => {
|
|
280
|
+
const { node } = this.getNodeInfo(id);
|
|
281
|
+
if (!node) return;
|
|
282
|
+
nodes.push(node);
|
|
283
|
+
});
|
|
284
|
+
this.set('nodes', nodes);
|
|
285
|
+
}
|
|
293
286
|
|
|
294
|
-
|
|
295
|
-
const
|
|
296
|
-
|
|
287
|
+
public async doAdd(node: MNode, parent: MContainer): Promise<MNode> {
|
|
288
|
+
const root = this.get<MApp>('root');
|
|
289
|
+
const curNode = this.get<MNode>('node');
|
|
290
|
+
const stage = this.get<StageCore | null>('stage');
|
|
297
291
|
|
|
298
|
-
if ((
|
|
292
|
+
if ((parent?.type === NodeType.ROOT || curNode.type === NodeType.ROOT) && node.type !== NodeType.PAGE) {
|
|
299
293
|
throw new Error('app下不能添加组件');
|
|
300
294
|
}
|
|
301
295
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
const stage = this.get<StageCore | null>('stage');
|
|
305
|
-
const root = this.get<MApp>('root');
|
|
296
|
+
const layout = await this.getLayout(toRaw(parent), node as MNode);
|
|
297
|
+
node.style = getInitPositionStyle(node.style, layout);
|
|
306
298
|
|
|
307
|
-
await stage?.add({ config: cloneDeep(
|
|
299
|
+
await stage?.add({ config: cloneDeep(node), parent: cloneDeep(parent), root: cloneDeep(root) });
|
|
308
300
|
|
|
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
|
-
}
|
|
301
|
+
node.style = fixNodePosition(node, parent, stage);
|
|
316
302
|
|
|
317
|
-
await
|
|
303
|
+
await stage?.update({ config: cloneDeep(node), root: cloneDeep(root) });
|
|
318
304
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
this.pushHistoryState();
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
if (isPage) {
|
|
325
|
-
this.state.pageLength += 1;
|
|
326
|
-
} else {
|
|
327
|
-
// 新增页面,这个时候页面还有渲染出来,此时select会出错,在runtime-ready的时候回去select
|
|
328
|
-
stage?.select(newNode.id);
|
|
329
|
-
}
|
|
305
|
+
// 新增节点添加到配置中
|
|
306
|
+
parent?.items?.push(node);
|
|
330
307
|
|
|
331
|
-
this.
|
|
308
|
+
this.addModifiedNodeId(node.id);
|
|
332
309
|
|
|
333
|
-
return
|
|
310
|
+
return node;
|
|
334
311
|
}
|
|
335
312
|
|
|
336
313
|
/**
|
|
337
|
-
*
|
|
338
|
-
* @param
|
|
339
|
-
* @
|
|
314
|
+
* 向指点容器添加组件节点
|
|
315
|
+
* @param addConfig 将要添加的组件节点配置
|
|
316
|
+
* @param parent 要添加到的容器组件节点配置,如果不设置,默认为当前选中的组件的父节点
|
|
317
|
+
* @returns 添加后的节点
|
|
340
318
|
*/
|
|
341
|
-
public async
|
|
342
|
-
|
|
319
|
+
public async add(addNode: AddMNode | MNode[], parent?: MContainer | null): Promise<MNode | MNode[]> {
|
|
320
|
+
const stage = this.get<StageCore | null>('stage');
|
|
343
321
|
|
|
344
|
-
const
|
|
322
|
+
const parentNode = parent && typeof parent !== 'function' ? parent : getAddParent(addNode);
|
|
323
|
+
if (!parentNode) throw new Error('未找到父元素');
|
|
345
324
|
|
|
346
|
-
|
|
325
|
+
// 新增多个组件只存在于粘贴多个组件,粘贴的是一个完整的config,所以不再需要getPropsValue
|
|
326
|
+
const addNodes = [];
|
|
327
|
+
if (!Array.isArray(addNode)) {
|
|
328
|
+
const { type, inputEvent, ...config } = addNode;
|
|
347
329
|
|
|
348
|
-
|
|
330
|
+
if (!type) throw new Error('组件类型不能为空');
|
|
349
331
|
|
|
350
|
-
|
|
332
|
+
addNodes.push({ ...toRaw(await propsService.getPropsValue(type, config)) });
|
|
333
|
+
} else {
|
|
334
|
+
addNodes.push(...addNode);
|
|
335
|
+
}
|
|
351
336
|
|
|
352
|
-
const
|
|
337
|
+
const newNodes = await Promise.all(addNodes.map((node) => this.doAdd(node, parentNode)));
|
|
353
338
|
|
|
354
|
-
if (
|
|
339
|
+
if (newNodes.length > 1) {
|
|
340
|
+
const newNodeIds = newNodes.map((node) => node.id);
|
|
341
|
+
// 触发选中样式
|
|
342
|
+
stage?.multiSelect(newNodeIds);
|
|
343
|
+
await this.multiSelect(newNodeIds);
|
|
344
|
+
} else {
|
|
345
|
+
await this.select(newNodes[0]);
|
|
355
346
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
347
|
+
if (isPage(newNodes[0])) {
|
|
348
|
+
this.state.pageLength += 1;
|
|
349
|
+
} else {
|
|
350
|
+
// 新增页面,这个时候页面还有渲染出来,此时select会出错,在runtime-ready的时候回去select
|
|
351
|
+
stage?.select(newNodes[0].id);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
359
354
|
|
|
360
|
-
|
|
361
|
-
this.state.pageLength -= 1;
|
|
355
|
+
this.pushHistoryState();
|
|
362
356
|
|
|
363
|
-
|
|
364
|
-
await this.select(root.items[0]);
|
|
365
|
-
stage?.select(root.items[0].id);
|
|
366
|
-
} else {
|
|
367
|
-
this.set('node', null);
|
|
368
|
-
this.set('parent', null);
|
|
369
|
-
this.set('page', null);
|
|
370
|
-
this.set('stage', null);
|
|
371
|
-
this.set('highlightNode', null);
|
|
372
|
-
this.resetModifiedNodeId();
|
|
373
|
-
historyService.reset();
|
|
357
|
+
this.emit('add', newNodes);
|
|
374
358
|
|
|
375
|
-
|
|
359
|
+
return newNodes.length > 1 ? newNodes[0] : newNodes;
|
|
360
|
+
}
|
|
376
361
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
362
|
+
/**
|
|
363
|
+
* 删除组件
|
|
364
|
+
* @param {Object} node
|
|
365
|
+
* @return {Object} 删除的组件配置
|
|
366
|
+
*/
|
|
367
|
+
public async remove(nodeOrNodeList: MNode | MNode[]): Promise<MNode | MNode[]> {
|
|
368
|
+
if (Array.isArray(nodeOrNodeList)) {
|
|
369
|
+
// 多选批量删除
|
|
370
|
+
const nodes = nodeOrNodeList;
|
|
371
|
+
return this.multiRemove(nodes);
|
|
382
372
|
}
|
|
383
|
-
|
|
384
|
-
|
|
373
|
+
const node = nodeOrNodeList;
|
|
374
|
+
const removeParent = await beforeRemove(node);
|
|
375
|
+
// 删除的是页面
|
|
376
|
+
if (!removeParent) return node;
|
|
377
|
+
// 更新历史记录
|
|
378
|
+
this.addModifiedNodeId(removeParent.id);
|
|
385
379
|
this.pushHistoryState();
|
|
386
380
|
|
|
387
381
|
this.emit('remove', node);
|
|
@@ -389,6 +383,25 @@ class Editor extends BaseService {
|
|
|
389
383
|
return node;
|
|
390
384
|
}
|
|
391
385
|
|
|
386
|
+
/**
|
|
387
|
+
* 批量删除
|
|
388
|
+
* @param nodes 批量删除的节点
|
|
389
|
+
* @returns 批量删除的节点
|
|
390
|
+
*/
|
|
391
|
+
public async multiRemove(nodes: MNode[]): Promise<MNode[]> {
|
|
392
|
+
await Promise.all(
|
|
393
|
+
nodes.map(async (removeNode) => {
|
|
394
|
+
await beforeRemove(removeNode);
|
|
395
|
+
}),
|
|
396
|
+
);
|
|
397
|
+
const nodeIds = nodes.map((node) => node.id);
|
|
398
|
+
this.addModifiedNodeId(nodeIds.join('-'));
|
|
399
|
+
this.pushHistoryState();
|
|
400
|
+
|
|
401
|
+
this.emit('multiRemove', nodes);
|
|
402
|
+
return nodes;
|
|
403
|
+
}
|
|
404
|
+
|
|
392
405
|
/**
|
|
393
406
|
* 更新节点
|
|
394
407
|
* @param config 新的节点配置,配置中需要有id信息
|
|
@@ -434,9 +447,11 @@ class Editor extends BaseService {
|
|
|
434
447
|
|
|
435
448
|
parentNodeItems[index] = newConfig;
|
|
436
449
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
}
|
|
450
|
+
// 将update后的配置更新到nodes中
|
|
451
|
+
const nodes = this.get('nodes');
|
|
452
|
+
const targetIndex = nodes.findIndex((nodeItem: MNode) => `${nodeItem.id}` === `${newConfig.id}`);
|
|
453
|
+
nodes.splice(targetIndex, 1, newConfig);
|
|
454
|
+
this.set('nodes', nodes);
|
|
440
455
|
|
|
441
456
|
this.get<StageCore | null>('stage')?.update({ config: cloneDeep(newConfig), root: cloneDeep(this.get('root')) });
|
|
442
457
|
|
|
@@ -482,44 +497,25 @@ class Editor extends BaseService {
|
|
|
482
497
|
* @param config 组件节点配置
|
|
483
498
|
* @returns 组件节点配置
|
|
484
499
|
*/
|
|
485
|
-
public async copy(config: MNode): Promise<void> {
|
|
486
|
-
|
|
500
|
+
public async copy(config: MNode | MNode[]): Promise<void> {
|
|
501
|
+
await storageService.setItem(COPY_STORAGE_KEY, Array.isArray(config) ? config : [config], {
|
|
502
|
+
protocol: Protocol.OBJECT,
|
|
503
|
+
});
|
|
487
504
|
}
|
|
488
505
|
|
|
489
506
|
/**
|
|
490
507
|
* 从localStorage中获取节点,然后添加到当前容器中
|
|
491
|
-
* @param position
|
|
508
|
+
* @param position 粘贴的坐标
|
|
492
509
|
* @returns 添加后的组件节点配置
|
|
493
510
|
*/
|
|
494
|
-
public async paste(position:
|
|
495
|
-
const
|
|
496
|
-
// eslint-disable-next-line prefer-const
|
|
497
|
-
let config: any = {};
|
|
498
|
-
if (!configStr) {
|
|
499
|
-
return;
|
|
500
|
-
}
|
|
511
|
+
public async paste(position: PastePosition = {}): Promise<MNode | MNode[] | void> {
|
|
512
|
+
const config: MNode[] = await storageService.getItem(COPY_STORAGE_KEY);
|
|
501
513
|
|
|
502
|
-
|
|
503
|
-
// eslint-disable-next-line no-eval
|
|
504
|
-
eval(`config = ${configStr}`);
|
|
505
|
-
} catch (e) {
|
|
506
|
-
console.error(e);
|
|
507
|
-
return;
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
config = await propsService.setNewItemId(config, this.get('root'));
|
|
511
|
-
if (config.style) {
|
|
512
|
-
config.style = {
|
|
513
|
-
...config.style,
|
|
514
|
-
...position,
|
|
515
|
-
};
|
|
516
|
-
}
|
|
514
|
+
if (!Array.isArray(config)) return;
|
|
517
515
|
|
|
518
|
-
|
|
519
|
-
config.name = generatePageNameByApp(this.get('root'));
|
|
520
|
-
}
|
|
516
|
+
const pasteConfigs = await beforePaste(position, config);
|
|
521
517
|
|
|
522
|
-
return
|
|
518
|
+
return this.add(pasteConfigs);
|
|
523
519
|
}
|
|
524
520
|
|
|
525
521
|
/**
|
|
@@ -610,7 +606,7 @@ class Editor extends BaseService {
|
|
|
610
606
|
return srcValue;
|
|
611
607
|
}
|
|
612
608
|
});
|
|
613
|
-
newConfig.style = getInitPositionStyle(newConfig.style, layout
|
|
609
|
+
newConfig.style = getInitPositionStyle(newConfig.style, layout);
|
|
614
610
|
|
|
615
611
|
target.items.push(newConfig);
|
|
616
612
|
|
|
@@ -653,7 +649,7 @@ class Editor extends BaseService {
|
|
|
653
649
|
const node = toRaw(this.get('node'));
|
|
654
650
|
if (!node || isPage(node)) return;
|
|
655
651
|
|
|
656
|
-
const { style, id } = node;
|
|
652
|
+
const { style, id, type } = node;
|
|
657
653
|
if (!style || style.position !== 'absolute') return;
|
|
658
654
|
|
|
659
655
|
if (top && !isNumber(style.top)) return;
|
|
@@ -661,6 +657,7 @@ class Editor extends BaseService {
|
|
|
661
657
|
|
|
662
658
|
this.update({
|
|
663
659
|
id,
|
|
660
|
+
type,
|
|
664
661
|
style: {
|
|
665
662
|
...style,
|
|
666
663
|
left: Number(style.left) + left,
|
|
@@ -673,6 +670,7 @@ class Editor extends BaseService {
|
|
|
673
670
|
this.removeAllListeners();
|
|
674
671
|
this.set('root', null);
|
|
675
672
|
this.set('node', null);
|
|
673
|
+
this.set('nodes', []);
|
|
676
674
|
this.set('page', null);
|
|
677
675
|
this.set('parent', null);
|
|
678
676
|
}
|
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 } from '
|
|
27
|
+
import type { PropsState } from '../type';
|
|
28
|
+
import { DEFAULT_CONFIG, fillConfig } from '../utils/props';
|
|
29
29
|
|
|
30
30
|
import BaseService from './BaseService';
|
|
31
31
|
|
|
@@ -121,12 +121,25 @@ class Props extends BaseService {
|
|
|
121
121
|
return {
|
|
122
122
|
id,
|
|
123
123
|
...defaultPropsValue,
|
|
124
|
-
...mergeWith(cloneDeep(this.state.propsValueMap[type] || {}), data),
|
|
124
|
+
...mergeWith({}, cloneDeep(this.state.propsValueMap[type] || {}), data),
|
|
125
125
|
};
|
|
126
126
|
}
|
|
127
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
|
+
|
|
128
141
|
public async createId(type: string | number): Promise<string> {
|
|
129
|
-
return `${type}_${
|
|
142
|
+
return `${type}_${this.guid()}`;
|
|
130
143
|
}
|
|
131
144
|
|
|
132
145
|
/**
|
|
@@ -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
|
|