@tmagic/editor 1.2.9 → 1.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/style.css +26 -32
  2. package/dist/tmagic-editor.js +1694 -1342
  3. package/dist/tmagic-editor.js.map +1 -1
  4. package/dist/tmagic-editor.umd.cjs +1699 -1344
  5. package/dist/tmagic-editor.umd.cjs.map +1 -1
  6. package/package.json +9 -9
  7. package/src/Editor.vue +10 -240
  8. package/src/components/CodeDraftEditor.vue +10 -6
  9. package/src/components/FunctionEditor.vue +1 -1
  10. package/src/editorProps.ts +135 -0
  11. package/src/fields/CodeSelect.vue +24 -13
  12. package/src/fields/CodeSelectCol.vue +117 -0
  13. package/src/fields/EventSelect.vue +216 -0
  14. package/src/index.ts +7 -0
  15. package/src/initService.ts +198 -0
  16. package/src/layouts/sidebar/code-block/CodeBlockList.vue +49 -127
  17. package/src/services/codeBlock.ts +29 -150
  18. package/src/services/dep.ts +328 -0
  19. package/src/services/editor.ts +2 -28
  20. package/src/theme/code-block.scss +5 -33
  21. package/src/theme/event.scss +24 -0
  22. package/src/theme/theme.scss +1 -0
  23. package/src/type.ts +18 -3
  24. package/src/utils/dep.ts +21 -0
  25. package/src/utils/props.ts +1 -35
  26. package/types/Editor.vue.d.ts +49 -82
  27. package/types/editorProps.d.ts +109 -0
  28. package/types/fields/CodeSelect.vue.d.ts +28 -5
  29. package/types/fields/CodeSelectCol.vue.d.ts +30 -0
  30. package/types/fields/EventSelect.vue.d.ts +26 -0
  31. package/types/index.d.ts +3 -0
  32. package/types/initService.d.ts +8 -0
  33. package/types/services/codeBlock.d.ts +3 -36
  34. package/types/services/dep.d.ts +130 -0
  35. package/types/services/editor.d.ts +1 -13
  36. package/types/services/props.d.ts +1 -15
  37. package/types/type.d.ts +17 -3
  38. package/types/utils/dep.d.ts +3 -0
  39. package/types/utils/props.d.ts +2 -16
@@ -19,190 +19,118 @@
19
19
  <!-- 代码块列表 -->
20
20
  <TMagicScrollbar>
21
21
  <TMagicTree
22
- v-if="!isEmpty(state.codeList)"
23
22
  ref="tree"
23
+ class="magic-editor-layer-tree"
24
24
  node-key="id"
25
25
  empty-text="暂无代码块"
26
- :data="state.codeList"
26
+ default-expand-all
27
+ :expand-on-click-node="false"
28
+ :data="codeList"
27
29
  :highlight-current="true"
28
30
  :filter-node-method="filterNode"
29
- @node-click="toggleCombineRelation"
31
+ @node-click="clickHandler"
30
32
  >
31
33
  <template #default="{ data }">
32
34
  <div :id="data.id" class="list-container">
33
35
  <div class="list-item">
34
36
  <span class="code-name">{{ data.name }}({{ data.id }})</span>
35
37
  <!-- 右侧工具栏 -->
36
- <div class="right-tool">
38
+ <div class="right-tool" v-if="data.type === 'code'">
37
39
  <TMagicTooltip effect="dark" :content="editable ? '编辑' : '查看'" placement="bottom">
38
40
  <Icon :icon="editable ? Edit : View" class="edit-icon" @click.stop="editCode(`${data.id}`)"></Icon>
39
41
  </TMagicTooltip>
40
- <TMagicTooltip
41
- effect="dark"
42
- content="查看绑定关系"
43
- placement="bottom"
44
- v-if="data.combineInfo && data.combineInfo.length > 0"
45
- >
46
- <Icon :icon="Link" class="edit-icon" @click.stop="toggleCombineRelation(data)"></Icon>
47
- </TMagicTooltip>
48
42
  <TMagicTooltip effect="dark" content="删除" placement="bottom" v-if="editable">
49
43
  <Icon :icon="Close" class="edit-icon" @click.stop="deleteCode(`${data.id}`)"></Icon>
50
44
  </TMagicTooltip>
51
45
  <slot name="code-block-panel-tool" :id="data.id" :data="data.codeBlockContent"></slot>
52
46
  </div>
53
47
  </div>
54
- <!-- 展示代码块下绑定的组件 -->
55
- <div
56
- class="code-comp-map-wrapper"
57
- v-if="data.showRelation && data.combineInfo && data.combineInfo.length > 0"
58
- >
59
- <svg class="arrow-left" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-029747aa="">
60
- <path
61
- fill="currentColor"
62
- d="M609.408 149.376 277.76 489.6a32 32 0 0 0 0 44.672l331.648 340.352a29.12 29.12 0 0 0 41.728 0 30.592 30.592 0 0 0 0-42.752L339.264 511.936l311.872-319.872a30.592 30.592 0 0 0 0-42.688 29.12 29.12 0 0 0-41.728 0z"
63
- ></path>
64
- </svg>
65
- <TMagicButton
66
- v-for="(comp, index) in data.combineInfo"
67
- :key="index"
68
- class="code-comp"
69
- size="small"
70
- :plain="true"
71
- @click.stop="selectComp(comp.compId)"
72
- >{{ comp.compName }}</TMagicButton
73
- >
74
- </div>
75
48
  </div>
76
49
  </template>
77
50
  </TMagicTree>
78
51
  </TMagicScrollbar>
79
52
 
80
53
  <!-- 代码块编辑区 -->
81
- <code-block-editor v-if="isShowCodeBlockEditor" :paramsColConfig="paramsColConfig">
54
+ <CodeBlockEditor v-if="isShowCodeBlockEditor" :paramsColConfig="paramsColConfig">
82
55
  <template #code-block-edit-panel-header="{ id }">
83
56
  <slot name="code-block-edit-panel-header" :id="id"></slot>
84
57
  </template>
85
- </code-block-editor>
58
+ </CodeBlockEditor>
86
59
  </div>
87
60
  </template>
88
61
 
89
- <script lang="ts" setup name="MEditorCodeBlockList">
90
- import { computed, inject, reactive, ref, watch } from 'vue';
91
- import { Close, Edit, Link, View } from '@element-plus/icons-vue';
92
- import { cloneDeep, forIn, isEmpty } from 'lodash-es';
62
+ <script setup lang="ts" name="MEditorCodeBlockList">
63
+ import { computed, inject, ref } from 'vue';
64
+ import { Close, Edit, View } from '@element-plus/icons-vue';
93
65
 
94
66
  import { TMagicButton, TMagicInput, tMagicMessage, TMagicScrollbar, TMagicTooltip, TMagicTree } from '@tmagic/design';
95
67
  import { ColumnConfig } from '@tmagic/form';
96
68
  import { CodeBlockContent, Id } from '@tmagic/schema';
97
69
 
98
70
  import Icon from '../../../components/Icon.vue';
99
- import type { CodeRelation, CombineInfo, Services } from '../../../type';
100
- import { CodeDeleteErrorType, CodeDslItem, ListState } from '../../../type';
71
+ import { CodeDeleteErrorType, CodeDslItem, Services } from '../../../type';
101
72
 
102
- import codeBlockEditor from './CodeBlockEditor.vue';
73
+ import CodeBlockEditor from './CodeBlockEditor.vue';
103
74
 
104
75
  const props = defineProps<{
105
76
  customError?: (id: Id, errorType: CodeDeleteErrorType) => any;
106
77
  paramsColConfig?: ColumnConfig;
107
78
  }>();
108
79
 
109
- const services = inject<Services>('services');
80
+ const { codeBlockService, depService, editorService } = inject<Services>('services') || {};
110
81
 
111
82
  // 代码块列表
112
- const state = reactive<ListState>({
113
- codeList: [],
114
- });
83
+ const codeList = computed(() =>
84
+ Object.values(depService?.targets['code-block'] || {}).map((target) => ({
85
+ id: target.id,
86
+ name: target.name,
87
+ type: 'code',
88
+ children: Object.entries(target.deps).map(([id, dep]) => ({
89
+ name: dep.name,
90
+ type: 'node',
91
+ id,
92
+ children: dep.keys.map((key) => ({ name: key, id: key, type: 'key' })),
93
+ })),
94
+ })),
95
+ );
115
96
 
116
- const editable = computed(() => services?.codeBlockService.getEditStatus());
97
+ const editable = computed(() => codeBlockService?.getEditStatus());
117
98
 
118
99
  // 是否展示代码编辑区
119
- const isShowCodeBlockEditor = computed(() => services?.codeBlockService.getCodeEditorShowStatus() || false);
120
- // 获取绑定关系
121
- const codeCombineInfo = ref<CodeRelation | null>(null);
122
-
123
- // 根据代码块ID获取其绑定的组件信息
124
- const getBindCompsByCodeId = (codeId: Id): CombineInfo[] => {
125
- if (!codeCombineInfo.value) return [];
126
- const bindsComp = [] as CombineInfo[];
127
- forIn(codeCombineInfo.value, (codeIds, compId) => {
128
- if (codeIds.includes(codeId)) {
129
- bindsComp.push({
130
- compId,
131
- compName: getCompName(compId),
132
- });
133
- }
134
- });
135
- return bindsComp as CombineInfo[];
136
- };
137
-
138
- // 更新代码块列表
139
- const refreshCodeList = async () => {
140
- const codeDsl = cloneDeep(await services?.codeBlockService.getCodeDsl()) || null;
141
- codeCombineInfo.value = cloneDeep(services?.codeBlockService.getCombineInfo()) || null;
142
- if (!codeDsl || !codeCombineInfo.value) return;
143
- state.codeList = [];
144
- forIn(codeDsl, (value: CodeBlockContent, codeId: Id) => {
145
- state.codeList.push({
146
- id: codeId,
147
- name: value.name,
148
- codeBlockContent: value,
149
- showRelation: true,
150
- combineInfo: getBindCompsByCodeId(codeId),
151
- });
152
- });
153
- };
154
-
155
- watch(
156
- () => services?.editorService.get('root'),
157
- () => {
158
- services?.codeBlockService.refreshAllRelations();
159
- refreshCodeList();
160
- },
161
- {
162
- immediate: true,
163
- },
164
- );
165
-
166
- watch(
167
- [() => services?.codeBlockService.getCodeDslSync(), () => services?.codeBlockService.getCombineInfo()],
168
- () => {
169
- refreshCodeList();
170
- },
171
- {
172
- deep: true,
173
- },
174
- );
100
+ const isShowCodeBlockEditor = computed(() => codeBlockService?.getCodeEditorShowStatus() || false);
175
101
 
176
102
  // 新增代码块
177
103
  const createCodeBlock = async () => {
178
- const { codeBlockService } = services || {};
179
104
  if (!codeBlockService) {
180
105
  tMagicMessage.error('新增代码块失败');
181
106
  return;
182
107
  }
108
+
183
109
  const codeConfig: CodeBlockContent = {
184
110
  name: '代码块',
185
111
  content: `({app, params}) => {\n // place your code here\n}`,
186
112
  params: [],
187
113
  };
114
+
188
115
  const id = await codeBlockService.getUniqueId();
116
+
189
117
  await codeBlockService.setCodeDslById(id, codeConfig);
190
118
  codeBlockService.setCodeEditorContent(true, id);
191
119
  };
192
120
 
193
121
  // 编辑代码块
194
122
  const editCode = async (key: Id) => {
195
- services?.codeBlockService.setCodeEditorContent(true, key);
123
+ codeBlockService?.setCodeEditorContent(true, key);
196
124
  };
197
125
 
198
126
  // 删除代码块
199
127
  const deleteCode = (key: Id) => {
200
- const currentCode = state.codeList.find((codeItem: CodeDslItem) => codeItem.id === key);
201
- const existBinds = !isEmpty(currentCode?.combineInfo);
202
- const undeleteableList = services?.codeBlockService.getUndeletableList() || [];
128
+ const currentCode = codeList.value.find((codeItem) => codeItem.id === key);
129
+ const existBinds = Boolean(currentCode?.children.length);
130
+ const undeleteableList = codeBlockService?.getUndeletableList() || [];
203
131
  if (!existBinds && !undeleteableList.includes(key)) {
204
132
  // 无绑定关系,且不在不可删除列表中
205
- services?.codeBlockService.deleteCodeDslByIds([key]);
133
+ codeBlockService?.deleteCodeDslByIds([key]);
206
134
  } else {
207
135
  if (typeof props.customError === 'function') {
208
136
  props.customError(key, existBinds ? CodeDeleteErrorType.BIND : CodeDeleteErrorType.UNDELETEABLE);
@@ -213,37 +141,31 @@ const deleteCode = (key: Id) => {
213
141
  };
214
142
 
215
143
  const filterText = ref('');
216
- const tree = ref();
144
+ const tree = ref<InstanceType<typeof TMagicTree>>();
217
145
 
218
146
  const filterNode = (value: string, data: CodeDslItem): boolean => {
219
147
  if (!value) {
220
148
  return true;
221
149
  }
222
- return `${data.name}${data.id}`.indexOf(value) !== -1;
150
+ return `${data.name}${data.id}`.toLocaleLowerCase().indexOf(value.toLocaleLowerCase()) !== -1;
223
151
  };
224
152
 
225
153
  const filterTextChangeHandler = (val: string) => {
226
154
  tree.value?.filter(val);
227
155
  };
228
156
 
229
- // 展示/隐藏组件绑定关系
230
- const toggleCombineRelation = (data: CodeDslItem) => {
231
- const { id } = data;
232
- const currentCode = state.codeList.find((item) => item.id === id);
233
- if (!currentCode) return;
234
- currentCode.showRelation = !currentCode?.showRelation;
235
- };
236
-
237
- // 获取组件名称展示到tag上
238
- const getCompName = (compId: Id): string => {
239
- const node = services?.editorService.getNodeById(compId);
240
- return node?.name || String(compId);
241
- };
242
-
243
157
  // 选中组件
244
158
  const selectComp = (compId: Id) => {
245
- const stage = services?.editorService.get('stage');
246
- services?.editorService.select(compId);
159
+ const stage = editorService?.get('stage');
160
+ editorService?.select(compId);
247
161
  stage?.select(compId);
248
162
  };
163
+
164
+ const clickHandler = (data: any, node: any) => {
165
+ if (data.type === 'node') {
166
+ selectComp(data.id);
167
+ } else if (data.type === 'key') {
168
+ selectComp(node.parent.data.id);
169
+ }
170
+ };
249
171
  </script>
@@ -17,12 +17,11 @@
17
17
  */
18
18
 
19
19
  import { reactive } from 'vue';
20
- import { cloneDeep, forIn, isEmpty, keys, omit, pick, union } from 'lodash-es';
20
+ import { keys, pick } from 'lodash-es';
21
21
 
22
- import { CodeBlockContent, CodeBlockDSL, HookType, Id, MNode, MPage } from '@tmagic/schema';
22
+ import { CodeBlockContent, CodeBlockDSL, Id } from '@tmagic/schema';
23
23
 
24
- import editorService from '../services/editor';
25
- import type { CodeRelation, CodeState, HookData } from '../type';
24
+ import type { CodeState } from '../type';
26
25
  import { CODE_DRAFT_STORAGE_KEY } from '../type';
27
26
  import { info } from '../utils/logger';
28
27
 
@@ -36,13 +35,10 @@ class CodeBlock extends BaseService {
36
35
  editable: true,
37
36
  combineIds: [],
38
37
  undeletableList: [],
39
- relations: {},
40
38
  });
41
39
 
42
40
  constructor() {
43
41
  super([
44
- 'setCodeDsl',
45
- 'getCodeDsl',
46
42
  'getCodeContentById',
47
43
  'getCodeDslByIds',
48
44
  'getCurrentDsl',
@@ -62,7 +58,6 @@ class CodeBlock extends BaseService {
62
58
  */
63
59
  public async setCodeDsl(codeDsl: CodeBlockDSL): Promise<void> {
64
60
  this.state.codeDsl = codeDsl;
65
- await editorService.setCodeDsl(this.state.codeDsl);
66
61
  info('[code-block]:code-dsl-change', this.state.codeDsl);
67
62
  this.emit('code-dsl-change', this.state.codeDsl);
68
63
  }
@@ -73,14 +68,7 @@ class CodeBlock extends BaseService {
73
68
  * @param {boolean} forceRefresh 是否强制从活动dsl拉取刷新
74
69
  * @returns {CodeBlockDSL | null}
75
70
  */
76
- public async getCodeDsl(forceRefresh = false): Promise<CodeBlockDSL | null> {
77
- return this.getCodeDslSync(forceRefresh);
78
- }
79
-
80
- public getCodeDslSync(forceRefresh = false): CodeBlockDSL | null {
81
- if (!this.state.codeDsl || forceRefresh) {
82
- this.state.codeDsl = editorService.getCodeDslSync();
83
- }
71
+ public getCodeDsl(): CodeBlockDSL | null {
84
72
  return this.state.codeDsl;
85
73
  }
86
74
 
@@ -103,31 +91,27 @@ class CodeBlock extends BaseService {
103
91
  * @returns {void}
104
92
  */
105
93
  public async setCodeDslById(id: Id, codeConfig: CodeBlockContent): Promise<void> {
106
- let codeDsl = await this.getCodeDsl();
94
+ const codeDsl = await this.getCodeDsl();
95
+
96
+ if (!codeDsl) {
97
+ throw new Error('dsl中没有codeBlocks');
98
+ }
99
+
107
100
  const codeConfigProcessed = codeConfig;
108
101
  if (codeConfig.content) {
109
102
  // 在保存的时候转换代码内容
110
103
  // eslint-disable-next-line no-eval
111
104
  codeConfigProcessed.content = eval(codeConfig.content);
112
105
  }
113
- if (!codeDsl) {
114
- // dsl中无代码块字段
115
- codeDsl = {
116
- [id]: {
117
- ...codeConfigProcessed,
118
- },
119
- };
120
- } else {
121
- const existContent = codeDsl[id] || {};
122
- codeDsl = {
123
- ...codeDsl,
124
- [id]: {
125
- ...existContent,
126
- ...codeConfigProcessed,
127
- },
128
- };
129
- }
130
- await this.setCodeDsl(codeDsl);
106
+
107
+ const existContent = codeDsl[id] || {};
108
+
109
+ codeDsl[id] = {
110
+ ...existContent,
111
+ ...codeConfigProcessed,
112
+ };
113
+
114
+ this.emit('addOrUpdate', id, codeDsl[id]);
131
115
  }
132
116
 
133
117
  /**
@@ -187,7 +171,7 @@ class CodeBlock extends BaseService {
187
171
 
188
172
  /**
189
173
  * 设置编辑状态
190
- * @param {boolean} 是否可编辑
174
+ * @param {boolean} status 是否可编辑
191
175
  * @returns {void}
192
176
  */
193
177
  public async setEditStatus(status: boolean): Promise<void> {
@@ -229,56 +213,6 @@ class CodeBlock extends BaseService {
229
213
  return this.state.combineIds;
230
214
  }
231
215
 
232
- /**
233
- * 监听组件更新来更新代码块绑定关系
234
- * @returns {void}
235
- */
236
- public addCodeRelationListener(): void {
237
- // 监听组件更新
238
- editorService.on('update', (nodes: MNode[]) => {
239
- const relations: CodeRelation = cloneDeep(this.state.relations);
240
- nodes.forEach((node: MNode) => {
241
- if (node?.id) {
242
- relations[node.id] = [];
243
- this.getNodeRelation(node, relations);
244
- }
245
- });
246
- this.state.relations = { ...relations };
247
- });
248
- // 监听组件删除
249
- editorService.on('remove', (nodes: MNode[]) => {
250
- nodes.forEach((node: MNode) => {
251
- this.state.relations = this.deleteNodeRelation(node, this.state.relations);
252
- });
253
- });
254
- // 监听历史记录,历史快照为页面整体,需要深层遍历更新
255
- editorService.on('history-change', (page: MPage) => {
256
- const relations: CodeRelation = {};
257
- this.getNodeRelation(page, relations, true);
258
- this.state.relations = relations;
259
- });
260
- }
261
-
262
- /**
263
- * 更新全部绑定关系
264
- * @returns {void}
265
- */
266
- public refreshAllRelations(): void {
267
- const root = editorService.get('root');
268
- if (!root) return;
269
- const relations: CodeRelation = {};
270
- this.getNodeRelation(root, relations, true);
271
- this.state.relations = relations;
272
- }
273
-
274
- /**
275
- * 获取绑定关系
276
- * @returns {CodeRelation}
277
- */
278
- public getCombineInfo(): CodeRelation {
279
- return this.state.relations;
280
- }
281
-
282
216
  /**
283
217
  * 获取不可删除列表
284
218
  * @returns {Id[]}
@@ -320,13 +254,17 @@ class CodeBlock extends BaseService {
320
254
  /**
321
255
  * 在dsl数据源中删除指定id的代码块
322
256
  * @param {Id[]} codeIds 需要删除的代码块id数组
323
- * @returns {CodeBlockDSL} 删除后的code dsl
324
257
  */
325
- public async deleteCodeDslByIds(codeIds: Id[]): Promise<CodeBlockDSL> {
258
+ public async deleteCodeDslByIds(codeIds: Id[]): Promise<void> {
326
259
  const currentDsl = await this.getCodeDsl();
327
- const newDsl = omit(currentDsl, codeIds);
328
- await this.setCodeDsl(newDsl);
329
- return newDsl;
260
+
261
+ if (!currentDsl) return;
262
+
263
+ codeIds.forEach((id) => {
264
+ delete currentDsl[id];
265
+
266
+ this.emit('remove', id);
267
+ });
330
268
  }
331
269
 
332
270
  /**
@@ -356,65 +294,6 @@ class CodeBlock extends BaseService {
356
294
  this.removeAllListeners();
357
295
  this.removeAllPlugins();
358
296
  }
359
-
360
- /**
361
- * 递归遍历dsl中挂载了代码块的节点,并更新绑定关系数据
362
- * @param {MNode} node 节点信息
363
- * @param {CodeRelation} relation 关系数据
364
- * @param {boolean} deep 是否深层遍历
365
- * @returns void
366
- */
367
- private getNodeRelation(node: MNode, relation: CodeRelation, deep = false): void {
368
- forIn(node, (value, key) => {
369
- if (value?.hookType === HookType.CODE && !isEmpty(value.hookData)) {
370
- value.hookData.forEach((relationItem: HookData) => {
371
- if (relationItem.codeId) {
372
- relation[node.id] = union(relation[node.id], [relationItem.codeId]);
373
- }
374
- });
375
- // continue
376
- return;
377
- }
378
- let isContinue = false;
379
- try {
380
- // 只遍历更新当前组件的关系,不再深层遍历容器包含的组件
381
- isContinue = key !== 'items' && typeof value === 'object' && JSON.stringify(value).includes('hookType');
382
- } catch (error) {
383
- console.error(error);
384
- }
385
- if (isContinue) {
386
- // 检查value内部是否有嵌套
387
- const unConfirmedValue = {
388
- id: node.id,
389
- ...value,
390
- };
391
- this.getNodeRelation(unConfirmedValue, relation);
392
- }
393
- // 深层遍历用于代码列表初始化
394
- if (key === 'items' && !isEmpty(value) && deep) {
395
- value.forEach((item: MNode) => {
396
- this.getNodeRelation(item, relation, deep);
397
- });
398
- }
399
- });
400
- }
401
-
402
- /**
403
- * 删除组件关系
404
- * @param {MNode} node 节点信息
405
- * @param {CodeRelation} relations 关系数据
406
- * @returns CodeRelation
407
- */
408
- private deleteNodeRelation(node: MNode, relations: CodeRelation): CodeRelation {
409
- if (!node.id) return {};
410
- let newRelations = omit(relations, [node.id]);
411
- if (!isEmpty(node.items)) {
412
- node.items.forEach((item: MNode) => {
413
- newRelations = this.deleteNodeRelation(item, newRelations);
414
- });
415
- }
416
- return newRelations;
417
- }
418
297
  }
419
298
 
420
299
  export type CodeBlockService = CodeBlock;