@tmagic/editor 1.2.0-beta.2 → 1.2.0-beta.21

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.
Files changed (101) hide show
  1. package/dist/style.css +41 -28
  2. package/dist/{tmagic-editor.mjs → tmagic-editor.js} +1693 -1272
  3. package/dist/tmagic-editor.js.map +1 -0
  4. package/dist/{tmagic-editor.umd.js → tmagic-editor.umd.cjs} +1700 -1274
  5. package/dist/tmagic-editor.umd.cjs.map +1 -0
  6. package/package.json +16 -14
  7. package/src/Editor.vue +54 -37
  8. package/src/components/CodeDraftEditor.vue +145 -0
  9. package/src/components/ContentMenu.vue +4 -4
  10. package/src/components/FunctionEditor.vue +145 -0
  11. package/src/components/Icon.vue +7 -5
  12. package/src/components/Layout.vue +149 -0
  13. package/src/components/ScrollBar.vue +1 -1
  14. package/src/components/ScrollViewer.vue +10 -1
  15. package/src/components/ToolButton.vue +30 -15
  16. package/src/fields/Code.vue +1 -1
  17. package/src/fields/CodeLink.vue +1 -1
  18. package/src/fields/CodeSelect.vue +89 -110
  19. package/src/fields/UISelect.vue +9 -8
  20. package/src/index.ts +1 -0
  21. package/src/layouts/AddPageBox.vue +3 -2
  22. package/src/layouts/CodeEditor.vue +2 -3
  23. package/src/layouts/Framework.vue +62 -40
  24. package/src/layouts/NavMenu.vue +1 -1
  25. package/src/layouts/PropsPanel.vue +11 -13
  26. package/src/layouts/Resizer.vue +1 -1
  27. package/src/layouts/sidebar/ComponentListPanel.vue +15 -13
  28. package/src/layouts/sidebar/LayerMenu.vue +9 -7
  29. package/src/layouts/sidebar/LayerPanel.vue +277 -177
  30. package/src/layouts/sidebar/Sidebar.vue +113 -27
  31. package/src/layouts/sidebar/code-block/CodeBlockEditor.vue +34 -85
  32. package/src/layouts/sidebar/code-block/CodeBlockList.vue +55 -89
  33. package/src/layouts/workspace/Breadcrumb.vue +32 -0
  34. package/src/layouts/workspace/PageBar.vue +12 -11
  35. package/src/layouts/workspace/PageBarScrollContainer.vue +1 -1
  36. package/src/layouts/workspace/Stage.vue +26 -9
  37. package/src/layouts/workspace/ViewerMenu.vue +11 -9
  38. package/src/layouts/workspace/Workspace.vue +10 -3
  39. package/src/services/BaseService.ts +6 -1
  40. package/src/services/codeBlock.ts +124 -125
  41. package/src/services/componentList.ts +6 -1
  42. package/src/services/editor.ts +43 -19
  43. package/src/services/events.ts +6 -1
  44. package/src/services/history.ts +7 -16
  45. package/src/services/props.ts +7 -2
  46. package/src/services/storage.ts +1 -0
  47. package/src/services/ui.ts +12 -0
  48. package/src/theme/breadcrumb.scss +6 -0
  49. package/src/theme/code-block.scss +30 -21
  50. package/src/theme/component-list-panel.scss +3 -7
  51. package/src/theme/layer-panel.scss +7 -2
  52. package/src/theme/theme.scss +1 -0
  53. package/src/type.ts +76 -50
  54. package/src/utils/editor.ts +9 -1
  55. package/src/utils/index.ts +1 -0
  56. package/src/utils/props.ts +2 -2
  57. package/src/utils/scroll-viewer.ts +18 -2
  58. package/src/utils/stage.ts +9 -2
  59. package/types/Editor.vue.d.ts +42 -29
  60. package/types/components/CodeDraftEditor.vue.d.ts +148 -0
  61. package/types/components/ContentMenu.vue.d.ts +1 -3
  62. package/types/components/FunctionEditor.vue.d.ts +120 -0
  63. package/types/components/Icon.vue.d.ts +1 -3
  64. package/types/{layouts → components}/Layout.vue.d.ts +12 -1
  65. package/types/components/ScrollBar.vue.d.ts +1 -3
  66. package/types/components/ScrollViewer.vue.d.ts +45 -1
  67. package/types/components/ToolButton.vue.d.ts +1 -3
  68. package/types/fields/Code.vue.d.ts +1 -3
  69. package/types/fields/CodeLink.vue.d.ts +1 -3
  70. package/types/fields/CodeSelect.vue.d.ts +10 -12
  71. package/types/fields/UISelect.vue.d.ts +1 -3
  72. package/types/index.d.ts +1 -0
  73. package/types/layouts/AddPageBox.vue.d.ts +1 -3
  74. package/types/layouts/CodeEditor.vue.d.ts +6 -15
  75. package/types/layouts/NavMenu.vue.d.ts +1 -3
  76. package/types/layouts/sidebar/LayerMenu.vue.d.ts +25 -9
  77. package/types/layouts/sidebar/LayerPanel.vue.d.ts +38 -886
  78. package/types/layouts/sidebar/Sidebar.vue.d.ts +10 -5
  79. package/types/layouts/sidebar/code-block/CodeBlockEditor.vue.d.ts +1 -1
  80. package/types/layouts/sidebar/code-block/CodeBlockList.vue.d.ts +6 -5
  81. package/types/layouts/{sidebar/TabPane.vue.d.ts → workspace/Breadcrumb.vue.d.ts} +5 -43
  82. package/types/layouts/workspace/Stage.vue.d.ts +25 -8
  83. package/types/layouts/workspace/ViewerMenu.vue.d.ts +6 -3
  84. package/types/layouts/workspace/Workspace.vue.d.ts +23 -5
  85. package/types/services/BaseService.d.ts +2 -1
  86. package/types/services/codeBlock.d.ts +57 -36
  87. package/types/services/componentList.d.ts +1 -0
  88. package/types/services/editor.d.ts +5 -6
  89. package/types/services/events.d.ts +1 -0
  90. package/types/services/history.d.ts +5 -15
  91. package/types/services/props.d.ts +3 -2
  92. package/types/services/ui.d.ts +2 -1
  93. package/types/type.d.ts +64 -48
  94. package/types/utils/editor.d.ts +1 -0
  95. package/types/utils/index.d.ts +1 -0
  96. package/types/utils/props.d.ts +1 -1
  97. package/types/utils/scroll-viewer.d.ts +5 -0
  98. package/dist/tmagic-editor.mjs.map +0 -1
  99. package/dist/tmagic-editor.umd.js.map +0 -1
  100. package/src/layouts/Layout.vue +0 -100
  101. package/src/layouts/sidebar/TabPane.vue +0 -116
@@ -19,12 +19,12 @@
19
19
  import { reactive } from 'vue';
20
20
  import { cloneDeep, forIn, isEmpty, keys, omit, pick } from 'lodash-es';
21
21
 
22
- import { Id, MNode } from '@tmagic/schema';
22
+ import { CodeBlockContent, CodeBlockDSL, HookType, Id, MApp, MNode } from '@tmagic/schema';
23
23
 
24
24
  import editorService from '../services/editor';
25
- import type { CodeBlockContent, CodeBlockDSL, CodeState } from '../type';
26
- import { CodeEditorMode, CodeSelectOp } from '../type';
27
- import { error, info } from '../utils/logger';
25
+ import type { CodeRelation, CodeState, HookData } from '../type';
26
+ import { CODE_DRAFT_STORAGE_KEY, CodeEditorMode } from '../type';
27
+ import { info } from '../utils/logger';
28
28
 
29
29
  import BaseService from './BaseService';
30
30
 
@@ -37,6 +37,7 @@ class CodeBlock extends BaseService {
37
37
  mode: CodeEditorMode.EDITOR,
38
38
  combineIds: [],
39
39
  undeletableList: [],
40
+ relations: {},
40
41
  });
41
42
 
42
43
  constructor() {
@@ -70,22 +71,27 @@ class CodeBlock extends BaseService {
70
71
 
71
72
  /**
72
73
  * 获取活动的代码块dsl数据源(默认从dsl中的codeBlocks字段读取)
74
+ * 方法要支持钩子添加扩展,会被重写为异步方法,因此这里显示写为异步以提醒调用者需以异步形式调用
73
75
  * @param {boolean} forceRefresh 是否强制从活动dsl拉取刷新
74
76
  * @returns {CodeBlockDSL | null}
75
77
  */
76
78
  public async getCodeDsl(forceRefresh = false): Promise<CodeBlockDSL | null> {
79
+ return this.getCodeDslSync(forceRefresh);
80
+ }
81
+
82
+ public getCodeDslSync(forceRefresh = false): CodeBlockDSL | null {
77
83
  if (!this.state.codeDsl || forceRefresh) {
78
- this.state.codeDsl = await editorService.getCodeDsl();
84
+ this.state.codeDsl = editorService.getCodeDslSync();
79
85
  }
80
86
  return this.state.codeDsl;
81
87
  }
82
88
 
83
89
  /**
84
90
  * 根据代码块id获取代码块内容
85
- * @param {string} id 代码块id
91
+ * @param {Id} id 代码块id
86
92
  * @returns {CodeBlockContent | null}
87
93
  */
88
- public async getCodeContentById(id: string): Promise<CodeBlockContent | null> {
94
+ public async getCodeContentById(id: Id): Promise<CodeBlockContent | null> {
89
95
  if (!id) return null;
90
96
  const totalCodeDsl = await this.getCodeDsl();
91
97
  if (!totalCodeDsl) return null;
@@ -94,16 +100,24 @@ class CodeBlock extends BaseService {
94
100
 
95
101
  /**
96
102
  * 设置代码块ID和代码内容到源dsl
97
- * @param {string} id 代码块id
103
+ * @param {Id} id 代码块id
98
104
  * @param {CodeBlockContent} codeConfig 代码块内容配置信息
99
105
  * @returns {void}
100
106
  */
101
- public async setCodeDslById(id: string, codeConfig: CodeBlockContent): Promise<void> {
107
+ public async setCodeDslById(id: Id, codeConfig: CodeBlockContent): Promise<void> {
102
108
  let codeDsl = await this.getCodeDsl();
109
+ const codeConfigProcessed = codeConfig;
110
+ if (codeConfig.content) {
111
+ // 在保存的时候转换代码内容
112
+ // eslint-disable-next-line no-eval
113
+ codeConfigProcessed.content = eval(codeConfig.content);
114
+ }
103
115
  if (!codeDsl) {
104
116
  // dsl中无代码块字段
105
117
  codeDsl = {
106
- [id]: codeConfig,
118
+ [id]: {
119
+ ...codeConfigProcessed,
120
+ },
107
121
  };
108
122
  } else {
109
123
  const existContent = codeDsl[id] || {};
@@ -111,7 +125,7 @@ class CodeBlock extends BaseService {
111
125
  ...codeDsl,
112
126
  [id]: {
113
127
  ...existContent,
114
- ...codeConfig,
128
+ ...codeConfigProcessed,
115
129
  },
116
130
  };
117
131
  }
@@ -148,10 +162,10 @@ class CodeBlock extends BaseService {
148
162
  /**
149
163
  * 设置代码编辑面板展示状态及展示内容
150
164
  * @param {boolean} status 是否展示代码编辑面板
151
- * @param {string} id 代码块id
165
+ * @param {Id} id 代码块id
152
166
  * @returns {void}
153
167
  */
154
- public setCodeEditorContent(status: boolean, id: string): void {
168
+ public setCodeEditorContent(status: boolean, id: Id): void {
155
169
  if (!id) return;
156
170
  this.setId(id);
157
171
  this.state.isShowCodeEditor = status;
@@ -161,7 +175,7 @@ class CodeBlock extends BaseService {
161
175
  * 获取当前选中的代码块内容
162
176
  * @returns {CodeBlockContent | null}
163
177
  */
164
- public async getCurrentDsl() {
178
+ public async getCurrentDsl(): Promise<CodeBlockContent | null> {
165
179
  return await this.getCodeContentById(this.state.id);
166
180
  }
167
181
 
@@ -184,19 +198,19 @@ class CodeBlock extends BaseService {
184
198
 
185
199
  /**
186
200
  * 设置当前选中的代码块ID
187
- * @param {string} id 代码块id
201
+ * @param {Id} id 代码块id
188
202
  * @returns {void}
189
203
  */
190
- public setId(id: string) {
204
+ public setId(id: Id): void {
191
205
  if (!id) return;
192
206
  this.state.id = id;
193
207
  }
194
208
 
195
209
  /**
196
210
  * 获取当前选中的代码块ID
197
- * @returns {string} id 代码块id
211
+ * @returns {Id} id 代码块id
198
212
  */
199
- public getId(): string {
213
+ public getId(): Id {
200
214
  return this.state.id;
201
215
  }
202
216
 
@@ -235,98 +249,70 @@ class CodeBlock extends BaseService {
235
249
  }
236
250
 
237
251
  /**
238
- * 设置绑定关系
239
- * @param {Id} 组件id
240
- * @param {string[]} diffCodeIds 代码块id数组
241
- * @param {CodeSelectOp} opFlag 操作类型
242
- * @param {string} hook 代码块挂载hook名称
243
- * @returns {void}
252
+ * 刷新绑定关系
253
+ * @returns {CodeRelation | null}
244
254
  */
245
- public async setCombineRelation(compId: Id, diffCodeIds: string[], opFlag: CodeSelectOp, hook: string) {
246
- const codeDsl = cloneDeep(await this.getCodeDsl());
247
- if (!codeDsl) return;
248
- if (opFlag === CodeSelectOp.DELETE) {
249
- try {
250
- diffCodeIds.forEach((codeId) => {
251
- const compsContent = codeDsl[codeId].comps;
252
- const index = compsContent?.[compId].findIndex((item) => item === hook);
253
- if (typeof index !== 'undefined' && index !== -1) {
254
- compsContent?.[compId].splice(index, 1);
255
- }
256
- });
257
- } catch (e) {
258
- error(e);
259
- throw new Error('解绑代码块失败');
260
- }
261
- } else if (opFlag === CodeSelectOp.ADD) {
262
- try {
263
- diffCodeIds.forEach((codeId) => {
264
- const compsContent = codeDsl[codeId].comps;
265
- const existHooks = compsContent?.[compId];
266
- if (isEmpty(existHooks)) {
267
- // comps属性不存在,或者comps为空:新增
268
- codeDsl[codeId].comps = {
269
- ...(codeDsl[codeId].comps || {}),
270
- [compId]: [hook],
271
- };
272
- } else {
273
- // 往已有的关系中添加hook
274
- existHooks?.push(hook);
275
- }
276
- });
277
- } catch (e) {
278
- error(e);
279
- throw new Error('绑定代码块失败');
280
- }
281
- } else if (opFlag === CodeSelectOp.CHANGE) {
282
- // 单选修改
283
- forIn(codeDsl, (codeBlockContent, codeId) => {
284
- if (codeId === diffCodeIds[0]) {
285
- // 增加
286
- codeBlockContent.comps = {
287
- ...(codeBlockContent?.comps || {}),
288
- [compId]: [hook],
289
- };
290
- } else if (isEmpty(diffCodeIds) || codeId !== diffCodeIds[0]) {
291
- // 清空或者移除之前的选项
292
- const compHooks = codeBlockContent?.comps?.[compId];
293
- // continue
294
- if (!compHooks) return true;
295
- const index = compHooks.findIndex((hookName) => hookName === hook);
296
- if (index !== -1) {
297
- compHooks.splice(index, 1);
298
- // break
299
- return false;
300
- }
301
- }
302
- });
303
- }
304
- this.setCodeDsl(codeDsl);
255
+ public refreshCombineInfo(): CodeRelation | null {
256
+ const root = editorService.get<MApp | null>('root');
257
+ if (!root) return null;
258
+ const relations = {};
259
+ this.recurseMNode(root, relations);
260
+ this.state.relations = relations;
261
+ return this.state.relations;
262
+ }
263
+
264
+ /**
265
+ * 获取绑定关系
266
+ * @returns {CodeRelation}
267
+ */
268
+ public getCombineInfo(): CodeRelation {
269
+ return this.state.relations;
305
270
  }
306
271
 
307
272
  /**
308
273
  * 获取不可删除列表
309
- * @returns {string[]}
274
+ * @returns {Id[]}
310
275
  */
311
- public getUndeletableList(): string[] {
276
+ public getUndeletableList(): Id[] {
312
277
  return this.state.undeletableList;
313
278
  }
314
279
 
315
280
  /**
316
281
  * 设置不可删除列表:为业务逻辑预留的不可删除的代码块列表,由业务逻辑维护(如代码块上线后不可删除)
317
- * @param {string[]} codeIds 代码块id数组
282
+ * @param {Id[]} codeIds 代码块id数组
318
283
  * @returns {void}
319
284
  */
320
- public async setUndeleteableList(codeIds: string[]): Promise<void> {
285
+ public async setUndeleteableList(codeIds: Id[]): Promise<void> {
321
286
  this.state.undeletableList = codeIds;
322
287
  }
323
288
 
289
+ /**
290
+ * 设置代码草稿
291
+ */
292
+ public setCodeDraft(codeId: Id, content: string): void {
293
+ globalThis.localStorage.setItem(`${CODE_DRAFT_STORAGE_KEY}_${codeId}`, content);
294
+ }
295
+
296
+ /**
297
+ * 获取代码草稿
298
+ */
299
+ public getCodeDraft(codeId: Id): string | null {
300
+ return globalThis.localStorage.getItem(`${CODE_DRAFT_STORAGE_KEY}_${codeId}`);
301
+ }
302
+
303
+ /**
304
+ * 删除代码草稿
305
+ */
306
+ public removeCodeDraft(codeId: Id): void {
307
+ globalThis.localStorage.removeItem(`${CODE_DRAFT_STORAGE_KEY}_${codeId}`);
308
+ }
309
+
324
310
  /**
325
311
  * 在dsl数据源中删除指定id的代码块
326
- * @param {string[]} codeIds 需要删除的代码块id数组
312
+ * @param {Id[]} codeIds 需要删除的代码块id数组
327
313
  * @returns {CodeBlockDSL} 删除后的code dsl
328
314
  */
329
- public async deleteCodeDslByIds(codeIds: string[]): Promise<CodeBlockDSL> {
315
+ public async deleteCodeDslByIds(codeIds: Id[]): Promise<CodeBlockDSL> {
330
316
  const currentDsl = await this.getCodeDsl();
331
317
  const newDsl = omit(currentDsl, codeIds);
332
318
  await this.setCodeDsl(newDsl);
@@ -335,9 +321,9 @@ class CodeBlock extends BaseService {
335
321
 
336
322
  /**
337
323
  * 生成代码块唯一id
338
- * @returns {string} 代码块唯一id
324
+ * @returns {Id} 代码块唯一id
339
325
  */
340
- public async getUniqueId(): Promise<string> {
326
+ public async getUniqueId(): Promise<Id> {
341
327
  const newId = `code_${Math.random().toString(10).substring(2).substring(0, 4)}`;
342
328
  // 判断是否重复
343
329
  const dsl = await this.getCodeDsl();
@@ -346,46 +332,19 @@ class CodeBlock extends BaseService {
346
332
  return await this.getUniqueId();
347
333
  }
348
334
 
349
- /**
350
- * 一对一解除绑定关系 功能隐藏,暂时注释
351
- * @param {string} compId 组件id
352
- * @param {string} codeId 代码块id
353
- * @param {string[]} codeHooks 代码块挂载hook名称
354
- * @returns {boolean} 结果
355
- */
356
- // public async unbind(compId: Id, codeId: string, codeHooks: string[]): Promise<boolean> {
357
- // const nodeInfo = editorService.getNodeById(compId);
358
- // if (!nodeInfo) return false;
359
- // // 更新node节点信息
360
- // codeHooks.forEach((hook) => {
361
- // if (!isEmpty(nodeInfo[hook])) {
362
- // const newHookInfo = nodeInfo[hook].filter((item: string) => item !== codeId);
363
- // nodeInfo[hook] = newHookInfo;
364
- // editorService.update(nodeInfo);
365
- // }
366
- // });
367
- // // 更新绑定关系
368
- // const oldRelation = await this.getCompRelation();
369
- // const oldCodeIds = oldRelation[compId];
370
- // // 不能直接splice修改原数组,会导致绑定关系更新错乱
371
- // const newCodeIds = oldCodeIds.filter((item) => item !== codeId);
372
- // this.setCompRelation(compId, newCodeIds);
373
- // return true;
374
- // }
375
-
376
335
  /**
377
336
  * 通过组件id解除绑定关系(删除组件)
378
337
  * @param {MNode} compId 组件节点
379
338
  * @returns void
380
339
  */
381
- public async deleteCompsInRelation(node: MNode) {
340
+ public async deleteCompsInRelation(node: MNode): Promise<void> {
382
341
  const codeDsl = cloneDeep(await this.getCodeDsl());
383
342
  if (!codeDsl) return;
384
- this.recurseNodes(node, codeDsl);
343
+ this.refreshRelationDeep(node, codeDsl);
385
344
  this.setCodeDsl(codeDsl);
386
345
  }
387
346
 
388
- public destroy() {
347
+ public resetState() {
389
348
  this.state.isShowCodeEditor = false;
390
349
  this.state.codeDsl = null;
391
350
  this.state.id = '';
@@ -395,8 +354,19 @@ class CodeBlock extends BaseService {
395
354
  this.state.undeletableList = [];
396
355
  }
397
356
 
398
- // 删除组件时 如果是容器 需要遍历删除其包含节点的绑定信息
399
- private recurseNodes(node: MNode, codeDsl: CodeBlockDSL) {
357
+ public destroy(): void {
358
+ this.resetState();
359
+ this.removeAllListeners();
360
+ this.removeAllPlugins();
361
+ }
362
+
363
+ /**
364
+ * 删除组件时 如果是容器 需要遍历删除其包含节点的绑定信息
365
+ * @param {MNode} node 节点信息
366
+ * @param {CodeBlockDSL} codeDsl 代码块
367
+ * @returns void
368
+ */
369
+ private refreshRelationDeep(node: MNode, codeDsl: CodeBlockDSL): void {
400
370
  if (!node.id) return;
401
371
  forIn(codeDsl, (codeBlockContent) => {
402
372
  const compsContent = codeBlockContent.comps || {};
@@ -404,7 +374,36 @@ class CodeBlock extends BaseService {
404
374
  });
405
375
  if (!isEmpty(node.items)) {
406
376
  node.items.forEach((item: MNode) => {
407
- this.recurseNodes(item, codeDsl);
377
+ this.refreshRelationDeep(item, codeDsl);
378
+ });
379
+ }
380
+ }
381
+
382
+ /**
383
+ * 递归遍历dsl中挂载了代码块的节点,并更新绑定关系数据
384
+ * @param {MContainer} node 节点信息
385
+ * @returns void
386
+ */
387
+ private recurseMNode(node: MNode, relations: CodeRelation): void {
388
+ forIn(node, (value, key) => {
389
+ if (value?.hookType === HookType.CODE && !isEmpty(value.hookData)) {
390
+ value.hookData.forEach((relationItem: HookData) => {
391
+ // continue
392
+ if (!relationItem.codeId) return;
393
+ if (!relations[relationItem.codeId]) {
394
+ relations[relationItem.codeId] = {};
395
+ }
396
+ const codeItem = relations[relationItem.codeId];
397
+ if (isEmpty(codeItem[node.id])) {
398
+ codeItem[node.id] = [];
399
+ }
400
+ codeItem[node.id].push(key);
401
+ });
402
+ }
403
+ });
404
+ if (!isEmpty(node.items)) {
405
+ node.items.forEach((item: MNode) => {
406
+ this.recurseMNode(item, relations);
408
407
  });
409
408
  }
410
409
  }
@@ -42,9 +42,14 @@ class ComponentList extends BaseService {
42
42
  return this.state.list;
43
43
  }
44
44
 
45
- public destroy() {
45
+ public resetState() {
46
46
  this.state.list = [];
47
+ }
48
+
49
+ public destroy() {
50
+ this.resetState();
47
51
  this.removeAllListeners();
52
+ this.removeAllPlugins();
48
53
  }
49
54
  }
50
55
 
@@ -17,7 +17,7 @@
17
17
  */
18
18
 
19
19
  import { reactive, toRaw } from 'vue';
20
- import { cloneDeep, mergeWith, uniq } from 'lodash-es';
20
+ import { cloneDeep, isObject, mergeWith, uniq } from 'lodash-es';
21
21
 
22
22
  import type { CodeBlockDSL, Id, MApp, MComponent, MContainer, MNode, MPage } from '@tmagic/schema';
23
23
  import { NodeType } from '@tmagic/schema';
@@ -25,9 +25,9 @@ import StageCore from '@tmagic/stage';
25
25
  import { getNodePath, isNumber, isPage, isPop } from '@tmagic/utils';
26
26
 
27
27
  import codeBlockService from '../services/codeBlock';
28
- import historyService, { StepValue } from '../services/history';
28
+ import historyService from '../services/history';
29
29
  import storageService, { Protocol } from '../services/storage';
30
- import type { AddMNode, EditorNodeInfo, PastePosition, StoreState } from '../type';
30
+ import type { AddMNode, EditorNodeInfo, PastePosition, StepValue, StoreState } from '../type';
31
31
  import { LayerOffset, Layout } from '../type';
32
32
  import {
33
33
  change2Fixed,
@@ -73,7 +73,7 @@ class Editor extends BaseService {
73
73
  'copy',
74
74
  'paste',
75
75
  'doPaste',
76
- 'duAlignCenter',
76
+ 'doAlignCenter',
77
77
  'alignCenter',
78
78
  'moveLayer',
79
79
  'moveToContainer',
@@ -91,9 +91,8 @@ class Editor extends BaseService {
91
91
 
92
92
  /**
93
93
  * 设置当前指点节点配置
94
- * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes'
94
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength'
95
95
  * @param value MNode
96
- * @returns MNode
97
96
  */
98
97
  public set<T = MNode>(name: keyof StoreState, value: T) {
99
98
  this.state[name] = value as any;
@@ -109,7 +108,7 @@ class Editor extends BaseService {
109
108
 
110
109
  /**
111
110
  * 获取当前指点节点配置
112
- * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes'
111
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength'
113
112
  * @returns MNode
114
113
  */
115
114
  public get<T = MNode>(name: keyof StoreState): T {
@@ -208,7 +207,7 @@ class Editor extends BaseService {
208
207
  if (page) {
209
208
  historyService.changePage(toRaw(page));
210
209
  } else {
211
- historyService.empty();
210
+ historyService.resetState();
212
211
  }
213
212
 
214
213
  if (node?.id) {
@@ -353,6 +352,9 @@ class Editor extends BaseService {
353
352
 
354
353
  const newNodes = await Promise.all(
355
354
  addNodes.map((node) => {
355
+ if (isPage(node)) {
356
+ return this.doAdd(node, this.get('root'));
357
+ }
356
358
  const parentNode = parent && typeof parent !== 'function' ? parent : getAddParent(node);
357
359
  if (!parentNode) throw new Error('未找到父元素');
358
360
  return this.doAdd(node, parentNode);
@@ -375,7 +377,9 @@ class Editor extends BaseService {
375
377
  }
376
378
  }
377
379
 
378
- this.pushHistoryState();
380
+ if (!isPage(newNodes[0])) {
381
+ this.pushHistoryState();
382
+ }
379
383
 
380
384
  this.emit('add', newNodes);
381
385
 
@@ -430,15 +434,16 @@ class Editor extends BaseService {
430
434
  /**
431
435
  * 删除组件
432
436
  * @param {Object} node
433
- * @return {Object} 删除的组件配置
434
437
  */
435
438
  public async remove(nodeOrNodeList: MNode | MNode[]): Promise<void> {
436
439
  const nodes = Array.isArray(nodeOrNodeList) ? nodeOrNodeList : [nodeOrNodeList];
437
440
 
438
441
  await Promise.all(nodes.map((node) => this.doRemove(node)));
439
442
 
440
- // 更新历史记录
441
- this.pushHistoryState();
443
+ if (!isPage(nodes[0])) {
444
+ // 更新历史记录
445
+ this.pushHistoryState();
446
+ }
442
447
 
443
448
  this.emit('remove', nodes);
444
449
  }
@@ -455,6 +460,10 @@ class Editor extends BaseService {
455
460
  let newConfig = await this.toggleFixedPosition(toRaw(config), node, this.get<MApp>('root'));
456
461
 
457
462
  newConfig = mergeWith(cloneDeep(node), newConfig, (objValue, srcValue) => {
463
+ if (isObject(srcValue) && Array.isArray(objValue)) {
464
+ // 原来的配置是数组,新的配置是对象,则直接使用新的值
465
+ return srcValue;
466
+ }
458
467
  if (Array.isArray(srcValue)) {
459
468
  return srcValue;
460
469
  }
@@ -484,10 +493,10 @@ class Editor extends BaseService {
484
493
  parentNodeItems[index] = newConfig;
485
494
 
486
495
  // 将update后的配置更新到nodes中
487
- const nodes = this.get('nodes');
496
+ const nodes = this.get<MNode[]>('nodes') || [];
488
497
  const targetIndex = nodes.findIndex((nodeItem: MNode) => `${nodeItem.id}` === `${newConfig.id}`);
489
498
  nodes.splice(targetIndex, 1, newConfig);
490
- this.set('nodes', nodes);
499
+ this.set('nodes', [...nodes]);
491
500
 
492
501
  this.get<StageCore | null>('stage')?.update({
493
502
  config: cloneDeep(newConfig),
@@ -517,7 +526,7 @@ class Editor extends BaseService {
517
526
  this.pushHistoryState();
518
527
 
519
528
  this.emit('update', newNodes);
520
-
529
+ codeBlockService.refreshCombineInfo();
521
530
  return Array.isArray(config) ? newNodes : newNodes[0];
522
531
  }
523
532
 
@@ -573,7 +582,7 @@ class Editor extends BaseService {
573
582
 
574
583
  const pasteConfigs = await this.doPaste(config, position);
575
584
 
576
- return this.add(pasteConfigs);
585
+ return this.add(pasteConfigs, this.get('parent'));
577
586
  }
578
587
 
579
588
  public async doPaste(config: MNode[], position: PastePosition = {}): Promise<MNode[]> {
@@ -623,7 +632,11 @@ class Editor extends BaseService {
623
632
 
624
633
  const newNode = await this.update(newNodes);
625
634
 
626
- await stage?.multiSelect(newNodes.map((node) => node.id));
635
+ if (newNodes.length > 1) {
636
+ await stage?.multiSelect(newNodes.map((node) => node.id));
637
+ } else {
638
+ await stage?.select(newNodes[0].id);
639
+ }
627
640
 
628
641
  return newNode;
629
642
  }
@@ -749,8 +762,7 @@ class Editor extends BaseService {
749
762
  });
750
763
  }
751
764
 
752
- public destroy() {
753
- this.removeAllListeners();
765
+ public resetState() {
754
766
  this.set('root', null);
755
767
  this.set('node', null);
756
768
  this.set('nodes', []);
@@ -762,6 +774,12 @@ class Editor extends BaseService {
762
774
  this.set('pageLength', new Map());
763
775
  }
764
776
 
777
+ public destroy() {
778
+ this.removeAllListeners();
779
+ this.resetState();
780
+ this.removeAllPlugins();
781
+ }
782
+
765
783
  public resetModifiedNodeId() {
766
784
  this.get<Map<Id, Id>>('modifiedNodeIds').clear();
767
785
  }
@@ -776,6 +794,12 @@ class Editor extends BaseService {
776
794
  return root.codeBlocks || null;
777
795
  }
778
796
 
797
+ public getCodeDslSync(): CodeBlockDSL | null {
798
+ const root = this.get<MApp | null>('root');
799
+ if (!root) return null;
800
+ return root.codeBlocks || null;
801
+ }
802
+
779
803
  /**
780
804
  * 设置代码块到dsl的codeBlocks字段
781
805
  * @param {CodeBlockDSL} codeDsl 代码DSL
@@ -76,10 +76,15 @@ class Events extends BaseService {
76
76
  return cloneDeep(methodMap[type] || DEFAULT_METHODS);
77
77
  }
78
78
 
79
- public destroy() {
79
+ public resetState() {
80
80
  eventMap = reactive({});
81
81
  methodMap = reactive({});
82
+ }
83
+
84
+ public destroy() {
85
+ this.resetState();
82
86
  this.removeAllListeners();
87
+ this.removeAllPlugins();
83
88
  }
84
89
  }
85
90
 
@@ -18,25 +18,13 @@
18
18
 
19
19
  import { reactive } from 'vue';
20
20
 
21
- import type { Id, MPage } from '@tmagic/schema';
21
+ import type { MPage } from '@tmagic/schema';
22
22
 
23
+ import { HistoryState, StepValue } from '../type';
23
24
  import { UndoRedo } from '../utils/undo-redo';
24
25
 
25
26
  import BaseService from './BaseService';
26
27
 
27
- export interface StepValue {
28
- data: MPage;
29
- modifiedNodeIds: Map<Id, Id>;
30
- nodeId: Id;
31
- }
32
-
33
- export interface HistoryState {
34
- pageId?: Id;
35
- pageSteps: Record<Id, UndoRedo<StepValue>>;
36
- canRedo: boolean;
37
- canUndo: boolean;
38
- }
39
-
40
28
  class History extends BaseService {
41
29
  public state = reactive<HistoryState>({
42
30
  pageSteps: {},
@@ -76,9 +64,11 @@ class History extends BaseService {
76
64
  }
77
65
 
78
66
  this.setCanUndoRedo();
67
+
68
+ this.emit('page-change', this.state.pageSteps[this.state.pageId]);
79
69
  }
80
70
 
81
- public empty(): void {
71
+ public resetState(): void {
82
72
  this.state.pageId = undefined;
83
73
  this.state.pageSteps = {};
84
74
  this.state.canRedo = false;
@@ -110,8 +100,9 @@ class History extends BaseService {
110
100
  }
111
101
 
112
102
  public destroy(): void {
113
- this.empty();
103
+ this.resetState();
114
104
  this.removeAllListeners();
105
+ this.removeAllPlugins();
115
106
  }
116
107
 
117
108
  private getUndoRedo() {