@tmagic/editor 1.2.10 → 1.2.12

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.
@@ -0,0 +1,328 @@
1
+ /*
2
+ * Tencent is pleased to support the open source community by making TMagicEditor available.
3
+ *
4
+ * Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+ import { EventEmitter } from 'events';
19
+
20
+ import { reactive } from 'vue';
21
+
22
+ import { MNode } from '@tmagic/schema';
23
+
24
+ type IsTarget = (key: string | number, value: any) => boolean;
25
+
26
+ interface TargetOptions {
27
+ isTarget: IsTarget;
28
+ id: string | number;
29
+ type?: string;
30
+ name: string;
31
+ }
32
+
33
+ interface Dep {
34
+ [key: string | number]: {
35
+ name: string;
36
+ keys: (string | number)[];
37
+ };
38
+ }
39
+
40
+ interface TargetList {
41
+ [key: string]: {
42
+ [key: string | number]: Target;
43
+ };
44
+ }
45
+
46
+ /**
47
+ * 需要收集依赖的目标
48
+ * 例如:一个代码块可以为一个目标
49
+ */
50
+ export class Target extends EventEmitter {
51
+ /**
52
+ * 如何识别目标
53
+ */
54
+ public isTarget: IsTarget;
55
+ /**
56
+ * 目标id,不可重复
57
+ * 例如目标是代码块,则为代码块id
58
+ */
59
+ public id: string | number;
60
+ /**
61
+ * 目标名称,用于显示在依赖列表中
62
+ */
63
+ public name: string;
64
+ /**
65
+ * 不同的目标可以进行分类,例如代码块,数据源可以为两个不同的type
66
+ */
67
+ public type = 'default';
68
+ /**
69
+ * 依赖详情
70
+ * 实例:{ 'node_id': { name: 'node_name', keys: [ created, mounted ] } }
71
+ */
72
+ public deps = reactive<Dep>({});
73
+
74
+ constructor(options: TargetOptions) {
75
+ super();
76
+ this.isTarget = options.isTarget;
77
+ this.id = options.id;
78
+ this.name = options.name;
79
+ if (options.type) {
80
+ this.type = options.type;
81
+ }
82
+ }
83
+
84
+ /**
85
+ * 更新依赖
86
+ * @param node 节点配置
87
+ * @param key 哪个key配置了这个目标的id
88
+ */
89
+ public updateDep(node: MNode, key: string | number) {
90
+ const dep = this.deps[node.id] || {
91
+ name: node.name,
92
+ keys: [],
93
+ };
94
+
95
+ if (node.name) {
96
+ dep.name = node.name;
97
+ }
98
+
99
+ this.deps[node.id] = dep;
100
+
101
+ if (dep.keys.indexOf(key) === -1) {
102
+ dep.keys.push(key);
103
+ }
104
+
105
+ this.emit('change');
106
+ }
107
+
108
+ /**
109
+ * 删除依赖
110
+ * @param node 哪个节点的依赖需要移除,如果为空,则移除所有依赖
111
+ * @param key 节点下哪个key需要移除,如果为空,则移除改节点下的所有依赖key
112
+ * @returns void
113
+ */
114
+ public removeDep(node?: MNode, key?: string | number) {
115
+ if (!node) {
116
+ Object.keys(this.deps).forEach((depKey) => {
117
+ delete this.deps[depKey];
118
+ });
119
+ this.emit('change');
120
+ return;
121
+ }
122
+
123
+ const dep = this.deps[node.id];
124
+
125
+ if (!dep) return;
126
+
127
+ if (key) {
128
+ const index = dep.keys.indexOf(key);
129
+ dep.keys.splice(index, 1);
130
+
131
+ if (dep.keys.length === 0) {
132
+ delete this.deps[node.id];
133
+ }
134
+ } else {
135
+ delete this.deps[node.id];
136
+ }
137
+
138
+ this.emit('change');
139
+ }
140
+
141
+ /**
142
+ * 判断指定节点下的指定key是否存在在依赖列表中
143
+ * @param node 哪个节点
144
+ * @param key 哪个key
145
+ * @returns boolean
146
+ */
147
+ public hasDep(node: MNode, key: string | number) {
148
+ const dep = this.deps[node.id];
149
+
150
+ return Boolean(dep?.keys.find((d) => d === key));
151
+ }
152
+
153
+ public destroy() {
154
+ this.removeAllListeners();
155
+ }
156
+ }
157
+
158
+ export class Watcher extends EventEmitter {
159
+ public targets = reactive<TargetList>({});
160
+
161
+ /**
162
+ * 获取指定类型中的所有target
163
+ * @param type 分类
164
+ * @returns Target[]
165
+ */
166
+ public getTargets(type = 'default') {
167
+ return this.targets[type] || {};
168
+ }
169
+
170
+ /**
171
+ * 添加新的目标
172
+ * @param target Target
173
+ */
174
+ public addTarget(target: Target) {
175
+ const targets = this.getTargets(target.type) || {};
176
+ this.targets[target.type] = targets;
177
+ targets[target.id] = target;
178
+
179
+ this.emit('add-target', target);
180
+ }
181
+
182
+ /**
183
+ * 获取指定id的target
184
+ * @param id target id
185
+ * @returns Target
186
+ */
187
+ public getTarget(id: string | number) {
188
+ const allTargets = Object.values(this.targets);
189
+ for (const targets of allTargets) {
190
+ if (targets[id]) {
191
+ return targets[id];
192
+ }
193
+ }
194
+ }
195
+
196
+ /**
197
+ * 判断是否存在指定id的target
198
+ * @param id target id
199
+ * @returns boolean
200
+ */
201
+ public hasTarget(id: string | number) {
202
+ const allTargets = Object.values(this.targets);
203
+ for (const targets of allTargets) {
204
+ if (targets[id]) {
205
+ return true;
206
+ }
207
+ }
208
+
209
+ return false;
210
+ }
211
+
212
+ /**
213
+ * 删除指定id的target
214
+ * @param id target id
215
+ */
216
+ public removeTarget(id: string | number) {
217
+ const allTargets = Object.values(this.targets);
218
+ for (const targets of allTargets) {
219
+ if (targets[id]) {
220
+ targets[id].destroy();
221
+ delete targets[id];
222
+ }
223
+ }
224
+
225
+ this.emit('remove-target');
226
+ }
227
+
228
+ /**
229
+ * 删除指定分类的所有target
230
+ * @param type 分类
231
+ * @returns void
232
+ */
233
+ public removeTargets(type = 'default') {
234
+ const targets = this.targets[type];
235
+
236
+ if (!targets) return;
237
+
238
+ for (const target of Object.values(targets)) {
239
+ target.destroy();
240
+ }
241
+
242
+ delete this.targets[type];
243
+
244
+ this.emit('remove-target');
245
+ }
246
+
247
+ /**
248
+ * 删除所有target
249
+ */
250
+ public clearTargets() {
251
+ Object.keys(this.targets).forEach((key) => {
252
+ delete this.targets[key];
253
+ });
254
+ }
255
+
256
+ /**
257
+ * 收集依赖
258
+ * @param nodes 需要收集的节点
259
+ * @param deep 是否需要收集子节点
260
+ */
261
+ public collect(nodes: MNode[], deep = false) {
262
+ Object.values(this.targets).forEach((targets) => {
263
+ Object.values(targets).forEach((target) => {
264
+ nodes.forEach((node) => {
265
+ target.removeDep(node);
266
+ this.collectItem(node, target, deep);
267
+ });
268
+ });
269
+ });
270
+ }
271
+
272
+ /**
273
+ * 清除依赖
274
+ * @param nodes 需要清除依赖的节点
275
+ */
276
+ public clear(nodes?: MNode[]) {
277
+ Object.values(this.targets).forEach((targets) => {
278
+ Object.values(targets).forEach((target) => {
279
+ if (nodes) {
280
+ nodes.forEach((node) => {
281
+ target.removeDep(node);
282
+
283
+ if (Array.isArray(node.items)) {
284
+ this.clear(node.items);
285
+ }
286
+ });
287
+ } else {
288
+ target.removeDep();
289
+ }
290
+ });
291
+ });
292
+ }
293
+
294
+ private collectItem(node: MNode, target: Target, deep = false) {
295
+ const collectTarget = (config: Record<string | number, any>, prop = '') => {
296
+ const doCollect = (key: string, value: any) => {
297
+ const keyIsItems = key === 'items';
298
+ const fullKey = prop ? `${prop}.${key}` : key;
299
+
300
+ if (target.isTarget(key, value)) {
301
+ target.updateDep(node, fullKey);
302
+ } else if (!keyIsItems && Array.isArray(value)) {
303
+ value.forEach((item, index) => {
304
+ collectTarget(item, `${fullKey}.${index}`);
305
+ });
306
+ } else if (Object.prototype.toString.call(value) === '[object Object]') {
307
+ collectTarget(value, fullKey);
308
+ }
309
+
310
+ if (keyIsItems && deep && Array.isArray(value)) {
311
+ value.forEach((child) => {
312
+ this.collectItem(child, target, deep);
313
+ });
314
+ }
315
+ };
316
+
317
+ Object.entries(config).forEach(([key, value]) => {
318
+ doCollect(key, value);
319
+ });
320
+ };
321
+
322
+ collectTarget(node);
323
+ }
324
+ }
325
+
326
+ export type DepService = Watcher;
327
+
328
+ export default new Watcher();
@@ -228,7 +228,7 @@ class Editor extends BaseService {
228
228
  if (node?.id) {
229
229
  this.get('stage')
230
230
  ?.renderer.runtime?.getApp?.()
231
- .page?.emit(
231
+ ?.page?.emit(
232
232
  'editor:select',
233
233
  {
234
234
  node,
@@ -1,13 +1,6 @@
1
1
  .m-editor-code-block-list {
2
2
  height: 100%;
3
3
  margin-top: 5px;
4
-
5
- .el-tree-node__content {
6
- height: auto;
7
- }
8
- .el-tree-node__label {
9
- width: 100%;
10
- }
11
4
  .code-header-wrapper {
12
5
  display: flex;
13
6
  align-items: center;
@@ -32,45 +25,24 @@
32
25
  .list-container {
33
26
  width: 100%;
34
27
  overflow: hidden;
35
- margin-left: -25px;
36
28
  .list-item {
37
29
  display: flex;
38
- align-items: center;
30
+ width: 100%;
31
+
39
32
  .right-tool {
40
- width: fit-content !important;
41
33
  display: flex;
42
- align-items: center;
34
+ width: fit-content !important;
43
35
  .edit-icon {
44
36
  margin: 0 5px;
45
37
  }
46
38
  }
47
39
  .code-name {
48
- font-size: 14px;
49
- overflow: hidden;
50
40
  white-space: nowrap;
41
+ overflow: hidden;
51
42
  text-overflow: ellipsis;
52
- padding: 10px 15px;
53
43
  width: 0 !important;
54
44
  flex: 1;
55
- }
56
- }
57
- .code-comp-map-wrapper {
58
- display: flex;
59
- align-items: center;
60
- flex-wrap: wrap;
61
- margin-left: 20px;
62
- margin-bottom: 5px;
63
- .arrow-left {
64
- transform: rotate(-45deg);
65
- width: 20px;
66
- height: 20px;
67
- }
68
- .code-comp {
69
- margin-left: 5px;
70
- padding: 5px;
71
- .comp-delete-icon {
72
- margin-left: 3px;
73
- }
45
+ line-height: 18px;
74
46
  }
75
47
  }
76
48
  }
@@ -0,0 +1,24 @@
1
+ .m-fields-event-select {
2
+ width: 100%;
3
+ .fullWidth {
4
+ width: 100%;
5
+ }
6
+ .m-form-panel .el-card__body {
7
+ padding: 10px 25px;
8
+ }
9
+
10
+ .event-select-code {
11
+ margin-left: 20px;
12
+ width: auto;
13
+ }
14
+ .m-form-panel {
15
+ margin: 10px 0px;
16
+ }
17
+
18
+ .el-card.is-always-shadow {
19
+ box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.12);
20
+ }
21
+ }
22
+ .m-fields-code-select-col {
23
+ width: 100%;
24
+ }
@@ -12,5 +12,6 @@
12
12
  @import "./code-editor.scss";
13
13
  @import "./icon.scss";
14
14
  @import "./code-block.scss";
15
+ @import "./event.scss";
15
16
  @import "./layout.scss";
16
17
  @import "./breadcrumb.scss";
package/src/type.ts CHANGED
@@ -18,7 +18,7 @@
18
18
 
19
19
  import type { Component } from 'vue';
20
20
 
21
- import type { FormConfig } from '@tmagic/form';
21
+ import type { FormConfig, FormItem } from '@tmagic/form';
22
22
  import type { CodeBlockContent, CodeBlockDSL, Id, MApp, MContainer, MNode, MPage } from '@tmagic/schema';
23
23
  import type StageCore from '@tmagic/stage';
24
24
  import type {
@@ -30,6 +30,7 @@ import type {
30
30
 
31
31
  import type { CodeBlockService } from './services/codeBlock';
32
32
  import type { ComponentListService } from './services/componentList';
33
+ import type { DepService } from './services/dep';
33
34
  import type { EditorService } from './services/editor';
34
35
  import type { EventsService } from './services/events';
35
36
  import type { HistoryService } from './services/history';
@@ -54,6 +55,7 @@ export interface Services {
54
55
  componentListService: ComponentListService;
55
56
  uiService: UiService;
56
57
  codeBlockService: CodeBlockService;
58
+ depService: DepService;
57
59
  }
58
60
 
59
61
  export interface StageOptions {
@@ -339,8 +341,6 @@ export type CodeState = {
339
341
  combineIds: string[];
340
342
  /** 为业务逻辑预留的不可删除的代码块列表,由业务逻辑维护(如代码块上线后不可删除) */
341
343
  undeletableList: Id[];
342
- /** 代码块和组件的绑定关系 */
343
- relations: CodeRelation;
344
344
  };
345
345
 
346
346
  export type HookData = {
@@ -410,3 +410,18 @@ export interface HistoryState {
410
410
  canRedo: boolean;
411
411
  canUndo: boolean;
412
412
  }
413
+
414
+ export interface EventSelectConfig {
415
+ name: string;
416
+ type: 'event-select';
417
+ /** 事件名称表单配置 */
418
+ eventNameConfig?: FormItem;
419
+ /** 动作类型配置 */
420
+ actionTypeConfig?: FormItem;
421
+ /** 联动组件配置 */
422
+ targetCompConfig?: FormItem;
423
+ /** 联动组件动作配置 */
424
+ compActionConfig?: FormItem;
425
+ /** 联动代码配置 */
426
+ codeActionConfig?: FormItem;
427
+ }
@@ -0,0 +1,21 @@
1
+ import { isEmpty } from 'lodash-es';
2
+
3
+ import { CodeBlockContent, HookType, Id } from '@tmagic/schema';
4
+
5
+ import { Target } from '../services/dep';
6
+ import { HookData } from '../type';
7
+
8
+ export const createCodeBlockTarget = (id: Id, codeBlock: CodeBlockContent) =>
9
+ new Target({
10
+ type: 'code-block',
11
+ id,
12
+ name: codeBlock.name,
13
+ isTarget: (key: string | number, value: any) => {
14
+ if (value?.hookType === HookType.CODE && !isEmpty(value.hookData)) {
15
+ const index = value.hookData.findIndex((item: HookData) => item.codeId === id);
16
+ return Boolean(index > -1);
17
+ }
18
+
19
+ return false;
20
+ },
21
+ });
@@ -18,9 +18,6 @@
18
18
 
19
19
  import { FormConfig, FormState } from '@tmagic/form';
20
20
 
21
- import editorService from '../services/editor';
22
- import eventsService from '../services/events';
23
-
24
21
  /**
25
22
  * 统一为组件属性表单加上事件、高级、样式配置
26
23
  * @param config 组件属性配置
@@ -183,39 +180,8 @@ export const fillConfig = (config: FormConfig = []) => [
183
180
  title: '事件',
184
181
  items: [
185
182
  {
186
- type: 'table',
187
183
  name: 'events',
188
- items: [
189
- {
190
- name: 'name',
191
- label: '事件名',
192
- type: 'select',
193
- options: (mForm: FormState, { formValue }: any) =>
194
- eventsService.getEvent(formValue.type).map((option) => ({
195
- text: option.label,
196
- value: option.value,
197
- })),
198
- },
199
- {
200
- name: 'to',
201
- label: '联动组件',
202
- type: 'ui-select',
203
- },
204
- {
205
- name: 'method',
206
- label: '动作',
207
- type: 'select',
208
- options: (mForm: FormState, { model }: any) => {
209
- const node = editorService.getNodeById(model.to);
210
- if (!node?.type) return [];
211
-
212
- return eventsService.getMethod(node.type).map((option) => ({
213
- text: option.label,
214
- value: option.value,
215
- }));
216
- },
217
- },
218
- ],
184
+ type: 'event-select',
219
185
  },
220
186
  ],
221
187
  },