@tmagic/editor 1.4.5 → 1.4.7
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/tmagic-editor.js +2293 -1984
- package/dist/tmagic-editor.umd.cjs +2294 -1982
- package/package.json +19 -14
- package/src/components/TreeNode.vue +3 -1
- package/src/editorProps.ts +0 -3
- package/src/fields/DataSourceInput.vue +7 -1
- package/src/fields/EventSelect.vue +90 -16
- package/src/hooks/use-code-block-edit.ts +0 -2
- package/src/hooks/use-stage.ts +1 -1
- package/src/index.ts +1 -0
- package/src/initService.ts +24 -18
- package/src/layouts/sidebar/code-block/CodeBlockList.vue +34 -16
- package/src/layouts/sidebar/data-source/DataSourceList.vue +17 -5
- package/src/services/codeBlock.ts +80 -9
- package/src/services/dataSource.ts +60 -4
- package/src/services/dep.ts +77 -5
- package/src/services/editor.ts +23 -14
- package/src/services/props.ts +19 -8
- package/src/services/stageOverlay.ts +1 -0
- package/src/type.ts +1 -0
- package/src/utils/data-source/index.ts +20 -13
- package/src/utils/editor.ts +2 -0
- package/src/utils/idle-task.ts +72 -0
- package/src/utils/operator.ts +6 -6
- package/types/editorProps.d.ts +0 -3
- package/types/index.d.ts +1 -0
- package/types/layouts/PropsPanel.vue.d.ts +33 -33
- package/types/layouts/sidebar/code-block/CodeBlockList.vue.d.ts +1 -1
- package/types/layouts/sidebar/data-source/DataSourceList.vue.d.ts +1 -1
- package/types/services/codeBlock.d.ts +23 -2
- package/types/services/dataSource.d.ts +13 -1
- package/types/services/dep.d.ts +12 -2
- package/types/services/editor.d.ts +3 -2
- package/types/services/props.d.ts +2 -1
- package/types/type.d.ts +1 -0
- package/types/utils/editor.d.ts +2 -0
- package/types/utils/idle-task.d.ts +14 -0
- package/types/utils/operator.d.ts +2 -2
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.4.
|
|
2
|
+
"version": "1.4.7",
|
|
3
3
|
"name": "@tmagic/editor",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": [
|
|
@@ -50,18 +50,8 @@
|
|
|
50
50
|
"lodash-es": "^4.17.21",
|
|
51
51
|
"moveable": "^0.53.0",
|
|
52
52
|
"serialize-javascript": "^6.0.0",
|
|
53
|
-
"@tmagic/dep": "1.4.
|
|
54
|
-
"@tmagic/table": "1.4.
|
|
55
|
-
},
|
|
56
|
-
"peerDependencies": {
|
|
57
|
-
"monaco-editor": "^0.48.0",
|
|
58
|
-
"vue": "^3.4.27",
|
|
59
|
-
"@tmagic/design": "1.4.5",
|
|
60
|
-
"@tmagic/form": "1.4.5",
|
|
61
|
-
"@tmagic/schema": "1.4.5",
|
|
62
|
-
"@tmagic/stage": "1.4.5",
|
|
63
|
-
"@tmagic/utils": "1.4.5",
|
|
64
|
-
"@tmagic/core": "1.4.5"
|
|
53
|
+
"@tmagic/dep": "1.4.7",
|
|
54
|
+
"@tmagic/table": "1.4.7"
|
|
65
55
|
},
|
|
66
56
|
"devDependencies": {
|
|
67
57
|
"@types/events": "^3.0.0",
|
|
@@ -75,10 +65,25 @@
|
|
|
75
65
|
"sass": "^1.77.0",
|
|
76
66
|
"tsc-alias": "^1.8.5",
|
|
77
67
|
"type-fest": "^4.10.3",
|
|
78
|
-
"typescript": "^5.4.2",
|
|
79
68
|
"vite": "^5.2.11",
|
|
80
69
|
"vue-tsc": "^2.0.16"
|
|
81
70
|
},
|
|
71
|
+
"peerDependencies": {
|
|
72
|
+
"monaco-editor": "^0.48.0",
|
|
73
|
+
"vue": "^3.4.27",
|
|
74
|
+
"typescript": "*",
|
|
75
|
+
"@tmagic/core": "1.4.7",
|
|
76
|
+
"@tmagic/design": "1.4.7",
|
|
77
|
+
"@tmagic/form": "1.4.7",
|
|
78
|
+
"@tmagic/schema": "1.4.7",
|
|
79
|
+
"@tmagic/stage": "1.4.7",
|
|
80
|
+
"@tmagic/utils": "1.4.7"
|
|
81
|
+
},
|
|
82
|
+
"peerDependenciesMeta": {
|
|
83
|
+
"typescript": {
|
|
84
|
+
"optional": true
|
|
85
|
+
}
|
|
86
|
+
},
|
|
82
87
|
"scripts": {
|
|
83
88
|
"build": "npm run build:type && vite build",
|
|
84
89
|
"build:type": "npm run clear:type && vue-tsc --declaration --emitDeclarationOnly --project tsconfig.build.json && tsc-alias -p tsconfig.build.json",
|
|
@@ -114,7 +114,9 @@ const selected = computed(() => nodeStatus.value.selected);
|
|
|
114
114
|
const visible = computed(() => nodeStatus.value.visible);
|
|
115
115
|
const draggable = computed(() => nodeStatus.value.draggable);
|
|
116
116
|
|
|
117
|
-
const hasChildren = computed(
|
|
117
|
+
const hasChildren = computed(
|
|
118
|
+
() => Array.isArray(props.data.items) && props.data.items.some((item) => props.nodeStatusMap.get(item.id)?.visible),
|
|
119
|
+
);
|
|
118
120
|
|
|
119
121
|
const handleDragStart = (event: DragEvent) => {
|
|
120
122
|
treeEmit?.('node-dragstart', event, props.data);
|
package/src/editorProps.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { EventOption } from '@tmagic/core';
|
|
2
|
-
import type { CustomTargetOptions } from '@tmagic/dep';
|
|
3
2
|
import type { FormConfig, FormState } from '@tmagic/form';
|
|
4
3
|
import type { DataSourceSchema, Id, MApp, MNode } from '@tmagic/schema';
|
|
5
4
|
import StageCore, {
|
|
@@ -70,8 +69,6 @@ export interface EditorProps {
|
|
|
70
69
|
codeOptions?: { [key: string]: any };
|
|
71
70
|
/** 禁用鼠标左键按下时就开始拖拽,需要先选中再可以拖拽 */
|
|
72
71
|
disabledDragStart?: boolean;
|
|
73
|
-
/** 自定义依赖收集器,复制组件时会将关联依赖一并复制 */
|
|
74
|
-
collectorOptions?: CustomTargetOptions;
|
|
75
72
|
/** 标尺配置 */
|
|
76
73
|
guidesOptions?: Partial<GuidesOptions>;
|
|
77
74
|
/** 禁止多选 */
|
|
@@ -54,6 +54,7 @@ import { Coin } from '@element-plus/icons-vue';
|
|
|
54
54
|
import { getConfig, TMagicAutocomplete, TMagicTag } from '@tmagic/design';
|
|
55
55
|
import type { FieldProps, FormItem } from '@tmagic/form';
|
|
56
56
|
import type { DataSchema, DataSourceSchema } from '@tmagic/schema';
|
|
57
|
+
import { isNumber } from '@tmagic/utils';
|
|
57
58
|
|
|
58
59
|
import Icon from '@editor/components/Icon.vue';
|
|
59
60
|
import type { Services } from '@editor/type';
|
|
@@ -209,7 +210,7 @@ const fieldQuerySearch = (
|
|
|
209
210
|
const dsKey = queryString.substring(leftAngleIndex + 1, dotIndex);
|
|
210
211
|
|
|
211
212
|
// 可能是xx.xx.xx,存在链式调用
|
|
212
|
-
const keys = dsKey.split('.');
|
|
213
|
+
const keys = dsKey.replaceAll(/\[(\d+)\]/g, '.$1').split('.');
|
|
213
214
|
|
|
214
215
|
// 最前的是数据源id
|
|
215
216
|
const dsId = keys.shift();
|
|
@@ -224,6 +225,11 @@ const fieldQuerySearch = (
|
|
|
224
225
|
// 后面这些是字段
|
|
225
226
|
let key = keys.shift();
|
|
226
227
|
while (key) {
|
|
228
|
+
if (isNumber(key)) {
|
|
229
|
+
key = keys.shift();
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
|
|
227
233
|
for (const field of fields) {
|
|
228
234
|
if (field.name === key) {
|
|
229
235
|
fields = field.fields || [];
|
|
@@ -26,14 +26,14 @@
|
|
|
26
26
|
@change="onChangeHandler"
|
|
27
27
|
>
|
|
28
28
|
<template #header>
|
|
29
|
-
<
|
|
29
|
+
<MFormContainer
|
|
30
30
|
class="fullWidth"
|
|
31
31
|
:config="eventNameConfig"
|
|
32
32
|
:model="cardItem"
|
|
33
33
|
:disabled="disabled"
|
|
34
34
|
:size="size"
|
|
35
35
|
@change="onChangeHandler"
|
|
36
|
-
></
|
|
36
|
+
></MFormContainer>
|
|
37
37
|
<TMagicButton
|
|
38
38
|
style="color: #f56c6c"
|
|
39
39
|
link
|
|
@@ -55,13 +55,13 @@ import { has } from 'lodash-es';
|
|
|
55
55
|
|
|
56
56
|
import type { EventOption } from '@tmagic/core';
|
|
57
57
|
import { TMagicButton } from '@tmagic/design';
|
|
58
|
-
import type { CascaderOption, FieldProps, FormState, PanelConfig } from '@tmagic/form';
|
|
59
|
-
import { MContainer, MPanel } from '@tmagic/form';
|
|
60
|
-
import { ActionType } from '@tmagic/schema';
|
|
58
|
+
import type { CascaderOption, ChildConfig, FieldProps, FormState, PanelConfig } from '@tmagic/form';
|
|
59
|
+
import { MContainer as MFormContainer, MPanel } from '@tmagic/form';
|
|
60
|
+
import { ActionType, type MComponent, type MContainer } from '@tmagic/schema';
|
|
61
61
|
import { DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX } from '@tmagic/utils';
|
|
62
62
|
|
|
63
63
|
import type { CodeSelectColConfig, DataSourceMethodSelectConfig, EventSelectConfig, Services } from '@editor/type';
|
|
64
|
-
import { getCascaderOptionsFromFields } from '@editor/utils';
|
|
64
|
+
import { getCascaderOptionsFromFields, traverseNode } from '@editor/utils';
|
|
65
65
|
|
|
66
66
|
defineOptions({
|
|
67
67
|
name: 'MFieldsEventSelect',
|
|
@@ -80,13 +80,20 @@ const codeBlockService = services?.codeBlockService;
|
|
|
80
80
|
|
|
81
81
|
// 事件名称下拉框表单配置
|
|
82
82
|
const eventNameConfig = computed(() => {
|
|
83
|
-
const
|
|
84
|
-
const defaultEventNameConfig = {
|
|
83
|
+
const defaultEventNameConfig: ChildConfig = {
|
|
85
84
|
name: 'name',
|
|
86
85
|
text: '事件',
|
|
87
|
-
type:
|
|
86
|
+
type: (mForm, { formValue }: any) => {
|
|
87
|
+
if (
|
|
88
|
+
props.config.src !== 'component' ||
|
|
89
|
+
(formValue.type === 'page-fragment-container' && formValue.pageFragmentId)
|
|
90
|
+
) {
|
|
91
|
+
return 'cascader';
|
|
92
|
+
}
|
|
93
|
+
return 'select';
|
|
94
|
+
},
|
|
88
95
|
labelWidth: '40px',
|
|
89
|
-
checkStrictly:
|
|
96
|
+
checkStrictly: () => props.config.src !== 'component',
|
|
90
97
|
valueSeparator: '.',
|
|
91
98
|
options: (mForm: FormState, { formValue }: any) => {
|
|
92
99
|
let events: EventOption[] | CascaderOption[] = [];
|
|
@@ -95,11 +102,39 @@ const eventNameConfig = computed(() => {
|
|
|
95
102
|
|
|
96
103
|
if (props.config.src === 'component') {
|
|
97
104
|
events = eventsService.getEvent(formValue.type);
|
|
105
|
+
|
|
106
|
+
if (formValue.type === 'page-fragment-container' && formValue.pageFragmentId) {
|
|
107
|
+
const pageFragment = editorService?.get('root')?.items?.find((page) => page.id === formValue.pageFragmentId);
|
|
108
|
+
if (pageFragment) {
|
|
109
|
+
events = [
|
|
110
|
+
{
|
|
111
|
+
label: pageFragment.name || '迭代器容器',
|
|
112
|
+
value: pageFragment.id,
|
|
113
|
+
children: events,
|
|
114
|
+
},
|
|
115
|
+
];
|
|
116
|
+
pageFragment.items.forEach((node) => {
|
|
117
|
+
traverseNode<MComponent | MContainer>(node, (node) => {
|
|
118
|
+
const nodeEvents = (node.type && eventsService.getEvent(node.type)) || [];
|
|
119
|
+
|
|
120
|
+
events.push({
|
|
121
|
+
label: `${node.name}_${node.id}`,
|
|
122
|
+
value: `${node.id}`,
|
|
123
|
+
children: nodeEvents,
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
return events;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
98
132
|
return events.map((option) => ({
|
|
99
133
|
text: option.label,
|
|
100
134
|
value: option.value,
|
|
101
135
|
}));
|
|
102
136
|
}
|
|
137
|
+
|
|
103
138
|
if (props.config.src === 'datasource') {
|
|
104
139
|
// 从数据源类型中获取到相关事件
|
|
105
140
|
events = dataSourceService.getFormEvent(formValue.type);
|
|
@@ -163,24 +198,63 @@ const targetCompConfig = computed(() => {
|
|
|
163
198
|
text: '联动组件',
|
|
164
199
|
type: 'ui-select',
|
|
165
200
|
display: (mForm: FormState, { model }: { model: Record<any, any> }) => model.actionType === ActionType.COMP,
|
|
201
|
+
onChange: (MForm: FormState, v: string, { model }: any) => {
|
|
202
|
+
model.method = '';
|
|
203
|
+
return v;
|
|
204
|
+
},
|
|
166
205
|
};
|
|
167
206
|
return { ...defaultTargetCompConfig, ...props.config.targetCompConfig };
|
|
168
207
|
});
|
|
169
208
|
|
|
170
209
|
// 联动组件动作配置
|
|
171
210
|
const compActionConfig = computed(() => {
|
|
172
|
-
const defaultCompActionConfig = {
|
|
211
|
+
const defaultCompActionConfig: ChildConfig = {
|
|
173
212
|
name: 'method',
|
|
174
213
|
text: '动作',
|
|
175
|
-
type:
|
|
176
|
-
|
|
214
|
+
type: (mForm, { model }: any) => {
|
|
215
|
+
const to = editorService?.getNodeById(model.to);
|
|
216
|
+
|
|
217
|
+
if (to && to.type === 'page-fragment-container' && to.pageFragmentId) {
|
|
218
|
+
return 'cascader';
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return 'select';
|
|
222
|
+
},
|
|
223
|
+
checkStrictly: () => props.config.src !== 'component',
|
|
224
|
+
display: (mForm, { model }: any) => model.actionType === ActionType.COMP,
|
|
177
225
|
options: (mForm: FormState, { model }: any) => {
|
|
178
226
|
const node = editorService?.getNodeById(model.to);
|
|
179
227
|
if (!node?.type) return [];
|
|
180
228
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
229
|
+
let methods: EventOption[] | CascaderOption[] = [];
|
|
230
|
+
|
|
231
|
+
methods = eventsService?.getMethod(node.type) || [];
|
|
232
|
+
|
|
233
|
+
if (node.type === 'page-fragment-container' && node.pageFragmentId) {
|
|
234
|
+
const pageFragment = editorService?.get('root')?.items?.find((page) => page.id === node.pageFragmentId);
|
|
235
|
+
if (pageFragment) {
|
|
236
|
+
methods = [];
|
|
237
|
+
pageFragment.items.forEach((node: MComponent | MContainer) => {
|
|
238
|
+
traverseNode<MComponent | MContainer>(node, (node) => {
|
|
239
|
+
const nodeMethods = (node.type && eventsService?.getMethod(node.type)) || [];
|
|
240
|
+
|
|
241
|
+
if (nodeMethods.length) {
|
|
242
|
+
methods.push({
|
|
243
|
+
label: `${node.name}_${node.id}`,
|
|
244
|
+
value: `${node.id}`,
|
|
245
|
+
children: nodeMethods,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
return methods;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return methods.map((method) => ({
|
|
256
|
+
text: method.label,
|
|
257
|
+
value: method.value,
|
|
184
258
|
}));
|
|
185
259
|
},
|
|
186
260
|
};
|
package/src/hooks/use-stage.ts
CHANGED
|
@@ -24,7 +24,7 @@ export const useStage = (stageOptions: StageOptions) => {
|
|
|
24
24
|
const stage = new StageCore({
|
|
25
25
|
render: stageOptions.render,
|
|
26
26
|
runtimeUrl: stageOptions.runtimeUrl,
|
|
27
|
-
zoom: zoom.value,
|
|
27
|
+
zoom: stageOptions.zoom ?? zoom.value,
|
|
28
28
|
autoScrollIntoView: stageOptions.autoScrollIntoView,
|
|
29
29
|
isContainer: stageOptions.isContainer,
|
|
30
30
|
containerHighlightClassName: stageOptions.containerHighlightClassName,
|
package/src/index.ts
CHANGED
|
@@ -82,6 +82,7 @@ export { default as LayoutContainer } from './components/SplitView.vue';
|
|
|
82
82
|
export { default as SplitView } from './components/SplitView.vue';
|
|
83
83
|
export { default as Resizer } from './components/Resizer.vue';
|
|
84
84
|
export { default as CodeBlockEditor } from './components/CodeBlockEditor.vue';
|
|
85
|
+
export { default as FloatingBox } from './components/FloatingBox.vue';
|
|
85
86
|
export { default as PageFragmentSelect } from './fields/PageFragmentSelect.vue';
|
|
86
87
|
|
|
87
88
|
const defaultInstallOpt: InstallOptions = {
|
package/src/initService.ts
CHANGED
|
@@ -7,12 +7,11 @@ import {
|
|
|
7
7
|
createDataSourceCondTarget,
|
|
8
8
|
createDataSourceMethodTarget,
|
|
9
9
|
createDataSourceTarget,
|
|
10
|
-
createRelatedCompTarget,
|
|
11
10
|
DepTargetType,
|
|
12
11
|
Target,
|
|
13
12
|
} from '@tmagic/dep';
|
|
14
13
|
import type { CodeBlockContent, DataSourceSchema, Id, MApp, MNode, MPage, MPageFragment } from '@tmagic/schema';
|
|
15
|
-
import { getNodes } from '@tmagic/utils';
|
|
14
|
+
import { getNodes, isPage } from '@tmagic/utils';
|
|
16
15
|
|
|
17
16
|
import PropsPanel from './layouts/PropsPanel.vue';
|
|
18
17
|
import { EditorProps } from './editorProps';
|
|
@@ -268,17 +267,12 @@ export const initServiceEvents = (
|
|
|
268
267
|
}
|
|
269
268
|
};
|
|
270
269
|
|
|
271
|
-
const depUpdateHandler = (node: MNode) => {
|
|
272
|
-
updateNodeWhenDataSourceChange([node]);
|
|
273
|
-
};
|
|
274
|
-
|
|
275
270
|
const collectedHandler = (nodes: MNode[]) => {
|
|
276
271
|
updateNodeWhenDataSourceChange(nodes);
|
|
277
272
|
};
|
|
278
273
|
|
|
279
274
|
depService.on('add-target', targetAddHandler);
|
|
280
275
|
depService.on('remove-target', targetRemoveHandler);
|
|
281
|
-
depService.on('dep-update', depUpdateHandler);
|
|
282
276
|
depService.on('collected', collectedHandler);
|
|
283
277
|
|
|
284
278
|
const initDataSourceDepTarget = (ds: DataSourceSchema) => {
|
|
@@ -307,7 +301,9 @@ export const initServiceEvents = (
|
|
|
307
301
|
});
|
|
308
302
|
|
|
309
303
|
if (Array.isArray(value.items)) {
|
|
310
|
-
|
|
304
|
+
value.items.forEach((page) => {
|
|
305
|
+
depService.collectIdle([page], { pageId: page.id }, true);
|
|
306
|
+
});
|
|
311
307
|
} else {
|
|
312
308
|
depService.clear();
|
|
313
309
|
delete value.dataSourceDeps;
|
|
@@ -334,14 +330,28 @@ export const initServiceEvents = (
|
|
|
334
330
|
}
|
|
335
331
|
};
|
|
336
332
|
|
|
333
|
+
const collectIdle = (nodes: MNode[], deep: boolean) => {
|
|
334
|
+
nodes.forEach((node) => {
|
|
335
|
+
let pageId: Id | undefined;
|
|
336
|
+
|
|
337
|
+
if (isPage(node)) {
|
|
338
|
+
pageId = node.id;
|
|
339
|
+
} else {
|
|
340
|
+
const info = editorService.getNodeInfo(node.id);
|
|
341
|
+
pageId = info.page?.id;
|
|
342
|
+
}
|
|
343
|
+
depService.collectIdle([node], { pageId }, deep);
|
|
344
|
+
});
|
|
345
|
+
};
|
|
346
|
+
|
|
337
347
|
// 新增节点,收集依赖
|
|
338
348
|
const nodeAddHandler = (nodes: MNode[]) => {
|
|
339
|
-
|
|
349
|
+
collectIdle(nodes, true);
|
|
340
350
|
};
|
|
341
351
|
|
|
342
352
|
// 节点更新,收集依赖
|
|
343
353
|
const nodeUpdateHandler = (nodes: MNode[]) => {
|
|
344
|
-
|
|
354
|
+
collectIdle(nodes, false);
|
|
345
355
|
};
|
|
346
356
|
|
|
347
357
|
// 节点删除,清除对齐的依赖收集
|
|
@@ -351,7 +361,7 @@ export const initServiceEvents = (
|
|
|
351
361
|
|
|
352
362
|
// 由于历史记录变化是更新整个page,所以历史记录变化时,需要重新收集依赖
|
|
353
363
|
const historyChangeHandler = (page: MPage | MPageFragment) => {
|
|
354
|
-
depService.
|
|
364
|
+
depService.collectIdle([page], { pageId: page.id }, true);
|
|
355
365
|
};
|
|
356
366
|
|
|
357
367
|
editorService.on('history-change', historyChangeHandler);
|
|
@@ -386,7 +396,9 @@ export const initServiceEvents = (
|
|
|
386
396
|
removeDataSourceTarget(config.id);
|
|
387
397
|
initDataSourceDepTarget(config);
|
|
388
398
|
|
|
389
|
-
|
|
399
|
+
(root?.items || []).forEach((page) => {
|
|
400
|
+
depService.collectIdle([page], { pageId: page.id }, true);
|
|
401
|
+
});
|
|
390
402
|
|
|
391
403
|
const targets = depService.getTargets(DepTargetType.DATA_SOURCE);
|
|
392
404
|
|
|
@@ -410,15 +422,9 @@ export const initServiceEvents = (
|
|
|
410
422
|
dataSourceService.on('update', dataSourceUpdateHandler);
|
|
411
423
|
dataSourceService.on('remove', dataSourceRemoveHandler);
|
|
412
424
|
|
|
413
|
-
// 初始化复制组件相关的依赖收集器
|
|
414
|
-
if (props.collectorOptions && !depService.hasTarget(DepTargetType.RELATED_COMP_WHEN_COPY)) {
|
|
415
|
-
depService.addTarget(createRelatedCompTarget(props.collectorOptions));
|
|
416
|
-
}
|
|
417
|
-
|
|
418
425
|
onBeforeUnmount(() => {
|
|
419
426
|
depService.off('add-target', targetAddHandler);
|
|
420
427
|
depService.off('remove-target', targetRemoveHandler);
|
|
421
|
-
depService.off('dep-update', depUpdateHandler);
|
|
422
428
|
depService.off('collected', collectedHandler);
|
|
423
429
|
|
|
424
430
|
editorService.off('history-change', historyChangeHandler);
|
|
@@ -58,26 +58,44 @@ const { codeBlockService, depService, editorService } = services || {};
|
|
|
58
58
|
|
|
59
59
|
// 代码块列表
|
|
60
60
|
const codeList = computed<TreeNodeData[]>(() =>
|
|
61
|
-
Object.
|
|
61
|
+
Object.entries(codeBlockService?.getCodeDsl() || {}).map(([codeId, code]) => {
|
|
62
|
+
const target = depService?.getTarget(codeId, DepTargetType.CODE_BLOCK);
|
|
63
|
+
|
|
64
|
+
// 按页面分类显示
|
|
65
|
+
const pageList: TreeNodeData[] =
|
|
66
|
+
editorService?.get('root')?.items.map((page) => ({
|
|
67
|
+
name: page.devconfig?.tabName || page.name,
|
|
68
|
+
type: 'node',
|
|
69
|
+
id: `${codeId}_${page.id}`,
|
|
70
|
+
key: page.id,
|
|
71
|
+
items: [],
|
|
72
|
+
})) || [];
|
|
73
|
+
|
|
62
74
|
// 组件节点
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
75
|
+
if (target) {
|
|
76
|
+
Object.entries(target.deps).forEach(([id, dep]) => {
|
|
77
|
+
const page = pageList.find((page) => page.key === dep.data?.pageId);
|
|
78
|
+
page?.items?.push({
|
|
79
|
+
name: dep.name,
|
|
80
|
+
type: 'node',
|
|
81
|
+
id: `${page.id}_${id}`,
|
|
82
|
+
key: id,
|
|
83
|
+
items: dep.keys.map((key) => {
|
|
84
|
+
const data: TreeNodeData = { name: `${key}`, id: `${target.id}_${id}_${key}`, type: 'key' };
|
|
85
|
+
return data;
|
|
86
|
+
}),
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
}
|
|
73
90
|
|
|
74
91
|
const data: TreeNodeData = {
|
|
75
|
-
id:
|
|
76
|
-
key:
|
|
77
|
-
name:
|
|
92
|
+
id: codeId,
|
|
93
|
+
key: codeId,
|
|
94
|
+
name: code.name,
|
|
78
95
|
type: 'code',
|
|
79
|
-
codeBlockContent: codeBlockService?.getCodeContentById(
|
|
80
|
-
|
|
96
|
+
codeBlockContent: codeBlockService?.getCodeContentById(codeId),
|
|
97
|
+
// 只有一个页面不显示页面分类
|
|
98
|
+
items: pageList.length > 1 ? pageList.filter((page) => page.items?.length) : pageList[0]?.items || [],
|
|
81
99
|
};
|
|
82
100
|
|
|
83
101
|
return data;
|
|
@@ -81,16 +81,19 @@ const getNodeTreeConfig = (id: string, dep: DepData[string], type?: string, pare
|
|
|
81
81
|
* @param deps 依赖
|
|
82
82
|
* @param type 依赖类型
|
|
83
83
|
*/
|
|
84
|
-
const mergeChildren = (dsId: Id,
|
|
84
|
+
const mergeChildren = (dsId: Id, pageItems: any[], deps: DepData, type?: string) => {
|
|
85
85
|
Object.entries(deps).forEach(([id, dep]) => {
|
|
86
|
+
// 按页面分类显示
|
|
87
|
+
const page = pageItems.find((page) => page.key === dep.data?.pageId);
|
|
88
|
+
|
|
86
89
|
// 已经生成过的节点
|
|
87
|
-
const nodeItem = items.find((item) => item.key === id);
|
|
90
|
+
const nodeItem = page?.items.find((item: any) => item.key === id);
|
|
88
91
|
// 节点存在,则追加依赖的key
|
|
89
92
|
if (nodeItem) {
|
|
90
93
|
nodeItem.items = nodeItem.items.concat(getKeyTreeConfig(dep, type, nodeItem.key));
|
|
91
94
|
} else {
|
|
92
95
|
// 节点不存在,则生成
|
|
93
|
-
items.push(getNodeTreeConfig(id, dep, type,
|
|
96
|
+
page?.items.push(getNodeTreeConfig(id, dep, type, page.id));
|
|
94
97
|
}
|
|
95
98
|
});
|
|
96
99
|
};
|
|
@@ -101,7 +104,15 @@ const list = computed(() =>
|
|
|
101
104
|
const dsMethodDeps = dsMethodDep.value[ds.id]?.deps || {};
|
|
102
105
|
const dsCondDeps = dsCondDep.value[ds.id]?.deps || {};
|
|
103
106
|
|
|
104
|
-
const items
|
|
107
|
+
const items =
|
|
108
|
+
editorService?.get('root')?.items.map((page) => ({
|
|
109
|
+
name: page.devconfig?.tabName || page.name,
|
|
110
|
+
type: 'node',
|
|
111
|
+
id: `${ds.id}_${page.id}`,
|
|
112
|
+
key: page.id,
|
|
113
|
+
items: [],
|
|
114
|
+
})) || [];
|
|
115
|
+
|
|
105
116
|
// 数据源依赖分为三种类型:key/node、method、cond,是分开存储,这里将其合并展示
|
|
106
117
|
mergeChildren(ds.id, items, dsDeps);
|
|
107
118
|
mergeChildren(ds.id, items, dsMethodDeps, 'method');
|
|
@@ -112,7 +123,8 @@ const list = computed(() =>
|
|
|
112
123
|
key: ds.id,
|
|
113
124
|
name: ds.title,
|
|
114
125
|
type: 'ds',
|
|
115
|
-
|
|
126
|
+
// 只有一个页面不显示页面分类
|
|
127
|
+
items: items.length > 1 ? items.filter((page) => page.items.length) : items[0]?.items || [],
|
|
116
128
|
};
|
|
117
129
|
}),
|
|
118
130
|
);
|
|
@@ -17,21 +17,25 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { reactive } from 'vue';
|
|
20
|
-
import { keys, pick } from 'lodash-es';
|
|
20
|
+
import { cloneDeep, get, keys, pick } from 'lodash-es';
|
|
21
21
|
import type { Writable } from 'type-fest';
|
|
22
22
|
|
|
23
|
+
import { Target, type TargetOptions, Watcher } from '@tmagic/dep';
|
|
23
24
|
import type { ColumnConfig } from '@tmagic/form';
|
|
24
|
-
import type { CodeBlockContent, CodeBlockDSL, Id } from '@tmagic/schema';
|
|
25
|
+
import type { CodeBlockContent, CodeBlockDSL, Id, MNode } from '@tmagic/schema';
|
|
25
26
|
|
|
27
|
+
import editorService from '@editor/services/editor';
|
|
28
|
+
import storageService, { Protocol } from '@editor/services/storage';
|
|
26
29
|
import type { AsyncHookPlugin, CodeState } from '@editor/type';
|
|
27
30
|
import { CODE_DRAFT_STORAGE_KEY } from '@editor/type';
|
|
28
31
|
import { getConfig } from '@editor/utils/config';
|
|
32
|
+
import { COPY_CODE_STORAGE_KEY } from '@editor/utils/editor';
|
|
29
33
|
|
|
30
34
|
import BaseService from './BaseService';
|
|
31
35
|
|
|
32
36
|
const canUsePluginMethods = {
|
|
33
37
|
async: ['setCodeDslById', 'setEditStatus', 'setCombineIds', 'setUndeleteableList', 'deleteCodeDslByIds'] as const,
|
|
34
|
-
sync: [],
|
|
38
|
+
sync: ['setCodeDslByIdSync'],
|
|
35
39
|
};
|
|
36
40
|
|
|
37
41
|
type AsyncMethodName = Writable<(typeof canUsePluginMethods)['async']>;
|
|
@@ -46,7 +50,10 @@ class CodeBlock extends BaseService {
|
|
|
46
50
|
});
|
|
47
51
|
|
|
48
52
|
constructor() {
|
|
49
|
-
super(
|
|
53
|
+
super([
|
|
54
|
+
...canUsePluginMethods.async.map((methodName) => ({ name: methodName, isAsync: true })),
|
|
55
|
+
...canUsePluginMethods.sync.map((methodName) => ({ name: methodName, isAsync: false })),
|
|
56
|
+
]);
|
|
50
57
|
}
|
|
51
58
|
|
|
52
59
|
/**
|
|
@@ -88,20 +95,32 @@ class CodeBlock extends BaseService {
|
|
|
88
95
|
* @returns {void}
|
|
89
96
|
*/
|
|
90
97
|
public async setCodeDslById(id: Id, codeConfig: Partial<CodeBlockContent>): Promise<void> {
|
|
98
|
+
this.setCodeDslByIdSync(id, codeConfig, true);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* 为了兼容历史原因
|
|
103
|
+
* 设置代码块ID和代码内容到源dsl
|
|
104
|
+
* @param {Id} id 代码块id
|
|
105
|
+
* @param {CodeBlockContent} codeConfig 代码块内容配置信息
|
|
106
|
+
* @param {boolean} force 是否强制写入,默认true
|
|
107
|
+
* @returns {void}
|
|
108
|
+
*/
|
|
109
|
+
public setCodeDslByIdSync(id: Id, codeConfig: Partial<CodeBlockContent>, force = true): void {
|
|
91
110
|
const codeDsl = this.getCodeDsl();
|
|
92
111
|
|
|
93
112
|
if (!codeDsl) {
|
|
94
113
|
throw new Error('dsl中没有codeBlocks');
|
|
95
114
|
}
|
|
115
|
+
if (codeDsl[id] && !force) return;
|
|
96
116
|
|
|
97
|
-
const codeConfigProcessed = codeConfig;
|
|
98
|
-
if (
|
|
117
|
+
const codeConfigProcessed = cloneDeep(codeConfig);
|
|
118
|
+
if (codeConfigProcessed.content) {
|
|
99
119
|
// 在保存的时候转换代码内容
|
|
100
120
|
const parseDSL = getConfig('parseDSL');
|
|
101
|
-
if (typeof
|
|
102
|
-
|
|
121
|
+
if (typeof codeConfigProcessed.content === 'string') {
|
|
122
|
+
codeConfigProcessed.content = parseDSL<(...args: any[]) => any>(codeConfigProcessed.content);
|
|
103
123
|
}
|
|
104
|
-
codeConfigProcessed.content = codeConfig.content;
|
|
105
124
|
}
|
|
106
125
|
|
|
107
126
|
const existContent = codeDsl[id] || {};
|
|
@@ -233,6 +252,58 @@ class CodeBlock extends BaseService {
|
|
|
233
252
|
return await this.getUniqueId();
|
|
234
253
|
}
|
|
235
254
|
|
|
255
|
+
/**
|
|
256
|
+
* 复制时会带上组件关联的代码块
|
|
257
|
+
* @param config 组件节点配置
|
|
258
|
+
* @returns
|
|
259
|
+
*/
|
|
260
|
+
public copyWithRelated(config: MNode | MNode[], collectorOptions?: TargetOptions): void {
|
|
261
|
+
const copyNodes: MNode[] = Array.isArray(config) ? config : [config];
|
|
262
|
+
const copyData: CodeBlockDSL = {};
|
|
263
|
+
|
|
264
|
+
if (collectorOptions && typeof collectorOptions.isTarget === 'function') {
|
|
265
|
+
const customTarget = new Target({
|
|
266
|
+
...collectorOptions,
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
const coperWatcher = new Watcher();
|
|
270
|
+
|
|
271
|
+
coperWatcher.addTarget(customTarget);
|
|
272
|
+
|
|
273
|
+
coperWatcher.collect(copyNodes, {}, true, collectorOptions.type);
|
|
274
|
+
|
|
275
|
+
Object.keys(customTarget.deps).forEach((nodeId: Id) => {
|
|
276
|
+
const node = editorService.getNodeById(nodeId);
|
|
277
|
+
if (!node) return;
|
|
278
|
+
customTarget!.deps[nodeId].keys.forEach((key) => {
|
|
279
|
+
const relateCodeId = get(node, key);
|
|
280
|
+
const isExist = Object.keys(copyData).find((codeId: Id) => codeId === relateCodeId);
|
|
281
|
+
if (!isExist) {
|
|
282
|
+
const relateCode = this.getCodeContentById(relateCodeId);
|
|
283
|
+
if (relateCode) {
|
|
284
|
+
copyData[relateCodeId] = relateCode;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
storageService.setItem(COPY_CODE_STORAGE_KEY, copyData, {
|
|
291
|
+
protocol: Protocol.OBJECT,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* 粘贴代码块
|
|
297
|
+
* @returns
|
|
298
|
+
*/
|
|
299
|
+
public paste() {
|
|
300
|
+
const codeDsl: CodeBlockDSL = storageService.getItem(COPY_CODE_STORAGE_KEY);
|
|
301
|
+
Object.keys(codeDsl).forEach((codeId: Id) => {
|
|
302
|
+
// 不覆盖同样id的代码块
|
|
303
|
+
this.setCodeDslByIdSync(codeId, codeDsl[codeId], false);
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
236
307
|
public resetState() {
|
|
237
308
|
this.state.codeDsl = null;
|
|
238
309
|
this.state.editable = true;
|