@tmagic/editor 1.3.0-alpha.7 → 1.3.0-alpha.9
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 +19 -0
- package/dist/tmagic-editor.js +353 -165
- package/dist/tmagic-editor.js.map +1 -1
- package/dist/tmagic-editor.umd.cjs +367 -177
- package/dist/tmagic-editor.umd.cjs.map +1 -1
- package/package.json +12 -12
- package/src/components/FunctionEditor.vue +1 -1
- package/src/components/SplitView.vue +2 -2
- package/src/fields/DataSourceInput.vue +11 -10
- package/src/fields/DataSourceSelect.vue +75 -0
- package/src/index.ts +4 -0
- package/src/initService.ts +4 -0
- package/src/layouts/CodeEditor.vue +27 -7
- package/src/layouts/Framework.vue +1 -2
- package/src/layouts/PropsPanel.vue +2 -1
- package/src/layouts/sidebar/ComponentListPanel.vue +3 -4
- package/src/layouts/sidebar/LayerMenu.vue +4 -3
- package/src/layouts/sidebar/LayerNode.vue +40 -0
- package/src/layouts/sidebar/LayerPanel.vue +7 -4
- package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +18 -2
- package/src/layouts/workspace/Stage.vue +11 -4
- package/src/services/dep.ts +1 -0
- package/src/theme/data-source-input.scss +18 -0
- package/src/theme/layer-panel.scss +6 -0
- package/src/theme/theme.scss +1 -0
- package/src/utils/dep.ts +7 -1
- package/src/utils/props.ts +34 -0
- package/types/components/FunctionEditor.vue.d.ts +2 -0
- package/types/fields/DataSourceSelect.vue.d.ts +56 -0
- package/types/index.d.ts +2 -0
- package/types/layouts/CodeEditor.vue.d.ts +3 -0
- package/types/layouts/sidebar/LayerNode.vue.d.ts +16 -0
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.3.0-alpha.
|
|
2
|
+
"version": "1.3.0-alpha.9",
|
|
3
3
|
"name": "@tmagic/editor",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": [
|
|
@@ -46,27 +46,27 @@
|
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@babel/core": "^7.18.0",
|
|
48
48
|
"@element-plus/icons-vue": "^2.0.9",
|
|
49
|
-
"@tmagic/core": "1.3.0-alpha.
|
|
50
|
-
"@tmagic/design": "1.3.0-alpha.
|
|
51
|
-
"@tmagic/form": "1.3.0-alpha.
|
|
52
|
-
"@tmagic/schema": "1.3.0-alpha.
|
|
53
|
-
"@tmagic/stage": "1.3.0-alpha.
|
|
54
|
-
"@tmagic/table": "1.3.0-alpha.
|
|
55
|
-
"@tmagic/utils": "1.3.0-alpha.
|
|
49
|
+
"@tmagic/core": "1.3.0-alpha.9",
|
|
50
|
+
"@tmagic/design": "1.3.0-alpha.9",
|
|
51
|
+
"@tmagic/form": "1.3.0-alpha.9",
|
|
52
|
+
"@tmagic/schema": "1.3.0-alpha.9",
|
|
53
|
+
"@tmagic/stage": "1.3.0-alpha.9",
|
|
54
|
+
"@tmagic/table": "1.3.0-alpha.9",
|
|
55
|
+
"@tmagic/utils": "1.3.0-alpha.9",
|
|
56
56
|
"buffer": "^6.0.3",
|
|
57
57
|
"color": "^3.1.3",
|
|
58
58
|
"events": "^3.3.0",
|
|
59
59
|
"gesto": "^1.7.0",
|
|
60
60
|
"keycon": "^1.1.2",
|
|
61
61
|
"lodash-es": "^4.17.21",
|
|
62
|
-
"monaco-editor": "^0.
|
|
62
|
+
"monaco-editor": "^0.39.0",
|
|
63
63
|
"serialize-javascript": "^6.0.0",
|
|
64
64
|
"vue": "^3.3.4"
|
|
65
65
|
},
|
|
66
66
|
"peerDependencies": {
|
|
67
|
-
"@tmagic/design": "1.3.0-alpha.
|
|
68
|
-
"@tmagic/form": "1.3.0-alpha.
|
|
69
|
-
"monaco-editor": "^0.
|
|
67
|
+
"@tmagic/design": "1.3.0-alpha.9",
|
|
68
|
+
"@tmagic/form": "1.3.0-alpha.9",
|
|
69
|
+
"monaco-editor": "^0.39.0",
|
|
70
70
|
"vue": "^3.3.4"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div ref="el" class="m-editor-layout">
|
|
3
|
-
<template v-if="hasLeft">
|
|
3
|
+
<template v-if="hasLeft && $slots.left">
|
|
4
4
|
<div class="m-editor-layout-left" :class="leftClass" :style="`width: ${left}px`">
|
|
5
5
|
<slot name="left"></slot>
|
|
6
6
|
</div>
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<slot name="center"></slot>
|
|
12
12
|
</div>
|
|
13
13
|
|
|
14
|
-
<template v-if="hasRight">
|
|
14
|
+
<template v-if="hasRight && $slots.right">
|
|
15
15
|
<Resizer @change="changeRight"></Resizer>
|
|
16
16
|
<div class="m-editor-layout-right" :class="rightClass" :style="`width: ${right}px`">
|
|
17
17
|
<slot name="right"></slot>
|
|
@@ -30,13 +30,15 @@
|
|
|
30
30
|
</div>
|
|
31
31
|
</template>
|
|
32
32
|
</component>
|
|
33
|
-
<div :class="`el-input el-input--${size}`" @mouseup="mouseupHandler" v-else>
|
|
34
|
-
<div :class="`el-input__wrapper ${isFocused ? ' is-focus' : ''}`">
|
|
33
|
+
<div :class="`tmagic-data-source-input-text el-input el-input--${size}`" @mouseup="mouseupHandler" v-else>
|
|
34
|
+
<div :class="`tmagic-data-source-input-text-wrapper el-input__wrapper ${isFocused ? ' is-focus' : ''}`">
|
|
35
35
|
<div class="el-input__inner">
|
|
36
36
|
<template v-for="(item, index) in displayState">
|
|
37
37
|
<span :key="index" v-if="item.type === 'text'" style="margin-right: 2px">{{ item.value }}</span>
|
|
38
38
|
<TMagicTag :key="index" :size="size" v-if="item.type === 'var'">{{ item.value }}</TMagicTag>
|
|
39
39
|
</template>
|
|
40
|
+
|
|
41
|
+
<Icon class="tmagic-data-source-input-icon" :icon="Coin" />
|
|
40
42
|
</div>
|
|
41
43
|
</div>
|
|
42
44
|
</div>
|
|
@@ -90,7 +92,7 @@ const dataSources = computed(() => dataSourceService?.get('dataSources') || []);
|
|
|
90
92
|
const setDisplayState = () => {
|
|
91
93
|
displayState.value = [];
|
|
92
94
|
|
|
93
|
-
const matches = state.value.matchAll(
|
|
95
|
+
const matches = state.value.matchAll(/\$\{([\s\S]+?)\}/g);
|
|
94
96
|
let index = 0;
|
|
95
97
|
for (const match of matches) {
|
|
96
98
|
if (typeof match.index === 'undefined') break;
|
|
@@ -195,7 +197,7 @@ const getSelectionStart = () => {
|
|
|
195
197
|
* @param leftCurlyBracketIndex {字符索引
|
|
196
198
|
*/
|
|
197
199
|
const curCharIsLeftCurlyBracket = (leftCurlyBracketIndex: number) =>
|
|
198
|
-
leftCurlyBracketIndex >
|
|
200
|
+
leftCurlyBracketIndex > 0 && leftCurlyBracketIndex === getSelectionStart() - 1;
|
|
199
201
|
|
|
200
202
|
/**
|
|
201
203
|
* 当前输入的是.
|
|
@@ -213,7 +215,7 @@ const dsQuerySearch = (queryString: string, leftCurlyBracketIndex: number, cb: (
|
|
|
213
215
|
if (curCharIsLeftCurlyBracket(leftCurlyBracketIndex)) {
|
|
214
216
|
// 当前输入的是{
|
|
215
217
|
result = dataSources.value;
|
|
216
|
-
} else if (leftCurlyBracketIndex >
|
|
218
|
+
} else if (leftCurlyBracketIndex > 0) {
|
|
217
219
|
// 当前输入的是{xx
|
|
218
220
|
const queryName = queryString.substring(leftCurlyBracketIndex + 1).toLowerCase();
|
|
219
221
|
result = dataSources.value.filter((ds) => ds.title?.toLowerCase().includes(queryName) || ds.id.includes(queryName));
|
|
@@ -299,7 +301,7 @@ const querySearch = (queryString: string, cb: (data: { value: string }[]) => voi
|
|
|
299
301
|
const curQueryString = queryString.substring(0, selectionStart);
|
|
300
302
|
|
|
301
303
|
const fieldKeyStringLastIndex = curQueryString.lastIndexOf('.');
|
|
302
|
-
const dsKeyStringLastIndex = curQueryString.lastIndexOf('{');
|
|
304
|
+
const dsKeyStringLastIndex = curQueryString.lastIndexOf('${') + 1;
|
|
303
305
|
|
|
304
306
|
const isFieldTip = fieldKeyStringLastIndex > dsKeyStringLastIndex;
|
|
305
307
|
|
|
@@ -321,7 +323,7 @@ const selectHandler = async ({ value, type }: { value: string; type: 'dataSource
|
|
|
321
323
|
let startText = inputText.substring(0, selectionStart);
|
|
322
324
|
|
|
323
325
|
const dotIndex = startText.lastIndexOf('.');
|
|
324
|
-
const leftCurlyBracketIndex = startText.lastIndexOf('{');
|
|
326
|
+
const leftCurlyBracketIndex = startText.lastIndexOf('${') + 1;
|
|
325
327
|
|
|
326
328
|
const endText = inputText.substring(selectionStart);
|
|
327
329
|
|
|
@@ -336,7 +338,6 @@ const selectHandler = async ({ value, type }: { value: string; type: 'dataSource
|
|
|
336
338
|
if (!isRightCurlyBracket(selectionStart + 1)) {
|
|
337
339
|
suggestText = `${suggestText}}`;
|
|
338
340
|
}
|
|
339
|
-
suggestText = `{${suggestText}}`;
|
|
340
341
|
} else if (!curCharIsDot(dotIndex)) {
|
|
341
342
|
startText = startText.substring(0, dotIndex + 1);
|
|
342
343
|
}
|
|
@@ -345,10 +346,10 @@ const selectHandler = async ({ value, type }: { value: string; type: 'dataSource
|
|
|
345
346
|
|
|
346
347
|
await nextTick();
|
|
347
348
|
|
|
348
|
-
// 由于选择数据源时会在后面补全}
|
|
349
|
+
// 由于选择数据源时会在后面补全}, 所以光标要前移2位
|
|
349
350
|
let newSelectionStart = 0;
|
|
350
351
|
if (isDataSource) {
|
|
351
|
-
newSelectionStart = leftCurlyBracketIndex + suggestText.length
|
|
352
|
+
newSelectionStart = leftCurlyBracketIndex + suggestText.length;
|
|
352
353
|
} else {
|
|
353
354
|
newSelectionStart = dotIndex + suggestText.length + 1;
|
|
354
355
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<MSelect
|
|
3
|
+
:model="model"
|
|
4
|
+
:name="name"
|
|
5
|
+
:size="size"
|
|
6
|
+
:prop="prop"
|
|
7
|
+
:disabled="disabled"
|
|
8
|
+
:config="selectConfig"
|
|
9
|
+
:last-values="lastValues"
|
|
10
|
+
@change="changeHandler"
|
|
11
|
+
></MSelect>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script setup lang="ts">
|
|
15
|
+
import { computed, inject } from 'vue';
|
|
16
|
+
|
|
17
|
+
import { FieldSize } from '@tmagic/design';
|
|
18
|
+
import { MSelect, SelectConfig } from '@tmagic/form';
|
|
19
|
+
|
|
20
|
+
import { Services } from '../type';
|
|
21
|
+
|
|
22
|
+
defineOptions({
|
|
23
|
+
name: 'MEditorDataSourceSelect',
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const emit = defineEmits(['change']);
|
|
27
|
+
|
|
28
|
+
const props = withDefaults(
|
|
29
|
+
defineProps<{
|
|
30
|
+
config: {
|
|
31
|
+
type: 'data-source-select';
|
|
32
|
+
name: string;
|
|
33
|
+
text: string;
|
|
34
|
+
placeholder: string;
|
|
35
|
+
dataSourceType?: string;
|
|
36
|
+
};
|
|
37
|
+
model: Record<string, any>;
|
|
38
|
+
name: string;
|
|
39
|
+
prop: string;
|
|
40
|
+
disabled: boolean;
|
|
41
|
+
lastValues?: Record<string, any>;
|
|
42
|
+
size?: FieldSize;
|
|
43
|
+
}>(),
|
|
44
|
+
{
|
|
45
|
+
disabled: false,
|
|
46
|
+
},
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const { dataSourceService } = inject<Services>('services') || {};
|
|
50
|
+
|
|
51
|
+
const dataSources = computed(() => dataSourceService?.get('dataSources') || []);
|
|
52
|
+
|
|
53
|
+
const selectConfig = computed<SelectConfig>(() => {
|
|
54
|
+
const { type, dataSourceType, ...config } = props.config;
|
|
55
|
+
return {
|
|
56
|
+
...config,
|
|
57
|
+
type: 'select',
|
|
58
|
+
valueKey: 'dataSourceId',
|
|
59
|
+
options: dataSources.value
|
|
60
|
+
.filter((ds) => !dataSourceType || ds.type === dataSourceType)
|
|
61
|
+
.map((ds) => ({
|
|
62
|
+
value: {
|
|
63
|
+
isBindDataSource: true,
|
|
64
|
+
dataSourceType: ds.type,
|
|
65
|
+
dataSourceId: ds.id,
|
|
66
|
+
},
|
|
67
|
+
text: ds.title || ds.id,
|
|
68
|
+
})),
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const changeHandler = (value: any) => {
|
|
73
|
+
emit('change', value);
|
|
74
|
+
};
|
|
75
|
+
</script>
|
package/src/index.ts
CHANGED
|
@@ -23,6 +23,7 @@ import CodeSelect from './fields/CodeSelect.vue';
|
|
|
23
23
|
import CodeSelectCol from './fields/CodeSelectCol.vue';
|
|
24
24
|
import DataSourceFields from './fields/DataSourceFields.vue';
|
|
25
25
|
import DataSourceInput from './fields/DataSourceInput.vue';
|
|
26
|
+
import DataSourceSelect from './fields/DataSourceSelect.vue';
|
|
26
27
|
import EventSelect from './fields/EventSelect.vue';
|
|
27
28
|
import KeyValue from './fields/KeyValue.vue';
|
|
28
29
|
import uiSelect from './fields/UISelect.vue';
|
|
@@ -53,6 +54,7 @@ export { default as CodeSelect } from './fields/CodeSelect.vue';
|
|
|
53
54
|
export { default as CodeSelectCol } from './fields/CodeSelectCol.vue';
|
|
54
55
|
export { default as DataSourceFields } from './fields/DataSourceFields.vue';
|
|
55
56
|
export { default as DataSourceInput } from './fields/DataSourceInput.vue';
|
|
57
|
+
export { default as DataSourceSelect } from './fields/DataSourceSelect.vue';
|
|
56
58
|
export { default as EventSelect } from './fields/EventSelect.vue';
|
|
57
59
|
export { default as KeyValue } from './fields/KeyValue.vue';
|
|
58
60
|
export { default as CodeBlockList } from './layouts/sidebar/code-block/CodeBlockList.vue';
|
|
@@ -62,6 +64,7 @@ export { default as ContentMenu } from './components/ContentMenu.vue';
|
|
|
62
64
|
export { default as Icon } from './components/Icon.vue';
|
|
63
65
|
export { default as LayoutContainer } from './components/SplitView.vue';
|
|
64
66
|
export { default as SplitView } from './components/SplitView.vue';
|
|
67
|
+
export { default as Resizer } from './components/Resizer.vue';
|
|
65
68
|
|
|
66
69
|
const defaultInstallOpt: InstallOptions = {
|
|
67
70
|
// eslint-disable-next-line no-eval
|
|
@@ -86,5 +89,6 @@ export default {
|
|
|
86
89
|
app.component('m-fields-data-source-fields', DataSourceFields);
|
|
87
90
|
app.component('m-fields-key-value', KeyValue);
|
|
88
91
|
app.component('m-fields-data-source-input', DataSourceInput);
|
|
92
|
+
app.component('m-fields-data-source-select', DataSourceSelect);
|
|
89
93
|
},
|
|
90
94
|
};
|
package/src/initService.ts
CHANGED
|
@@ -236,18 +236,22 @@ export const initServiceEvents = (
|
|
|
236
236
|
}
|
|
237
237
|
};
|
|
238
238
|
|
|
239
|
+
// 新增节点,收集依赖
|
|
239
240
|
const nodeAddHandler = (nodes: MNode[]) => {
|
|
240
241
|
depService.collect(nodes);
|
|
241
242
|
};
|
|
242
243
|
|
|
244
|
+
// 节点更新,收集依赖
|
|
243
245
|
const nodeUpdateHandler = (nodes: MNode[]) => {
|
|
244
246
|
depService.collect(nodes);
|
|
245
247
|
};
|
|
246
248
|
|
|
249
|
+
// 节点删除,清除对齐的依赖收集
|
|
247
250
|
const nodeRemoveHandler = (nodes: MNode[]) => {
|
|
248
251
|
depService.clear(nodes);
|
|
249
252
|
};
|
|
250
253
|
|
|
254
|
+
// 由于历史记录变化是更新整个page,所以历史记录变化时,需要重新收集依赖
|
|
251
255
|
const historyChangeHandler = (page: MPage) => {
|
|
252
256
|
depService.collect([page], true);
|
|
253
257
|
};
|
|
@@ -37,10 +37,14 @@ const emit = defineEmits(['initd', 'save']);
|
|
|
37
37
|
const toString = (v: string | any, language: string): string => {
|
|
38
38
|
let value = '';
|
|
39
39
|
if (typeof v !== 'string') {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
if (props.language.toLocaleLowerCase() === 'json') {
|
|
41
|
+
value = JSON.stringify(v, null, 2);
|
|
42
|
+
} else {
|
|
43
|
+
value = serialize(v, {
|
|
44
|
+
space: 2,
|
|
45
|
+
unsafe: true,
|
|
46
|
+
}).replace(/"(\w+)":\s/g, '$1: ');
|
|
47
|
+
}
|
|
44
48
|
} else {
|
|
45
49
|
value = v;
|
|
46
50
|
}
|
|
@@ -81,7 +85,7 @@ const setEditorValue = (v: string | any, m: string | any) => {
|
|
|
81
85
|
};
|
|
82
86
|
|
|
83
87
|
const getEditorValue = () =>
|
|
84
|
-
props.type === 'diff' ? vsDiffEditor?.getModifiedEditor().getValue() : vsEditor?.getValue();
|
|
88
|
+
(props.type === 'diff' ? vsDiffEditor?.getModifiedEditor().getValue() : vsEditor?.getValue()) || '';
|
|
85
89
|
|
|
86
90
|
const init = async () => {
|
|
87
91
|
if (!codeEditor.value) return;
|
|
@@ -109,13 +113,19 @@ const init = async () => {
|
|
|
109
113
|
if (e.keyCode === 83 && (navigator.platform.match('Mac') ? e.metaKey : e.ctrlKey)) {
|
|
110
114
|
e.preventDefault();
|
|
111
115
|
e.stopPropagation();
|
|
112
|
-
|
|
116
|
+
const newValue = getEditorValue();
|
|
117
|
+
values.value = newValue;
|
|
118
|
+
emit('save', newValue);
|
|
113
119
|
}
|
|
114
120
|
});
|
|
115
121
|
|
|
116
122
|
if (props.type !== 'diff' && props.autoSave) {
|
|
117
123
|
vsEditor?.onDidBlurEditorWidget(() => {
|
|
118
|
-
|
|
124
|
+
const newValue = getEditorValue();
|
|
125
|
+
if (values.value !== newValue) {
|
|
126
|
+
values.value = newValue;
|
|
127
|
+
emit('save', newValue);
|
|
128
|
+
}
|
|
119
129
|
});
|
|
120
130
|
}
|
|
121
131
|
|
|
@@ -146,10 +156,20 @@ onUnmounted(() => {
|
|
|
146
156
|
});
|
|
147
157
|
|
|
148
158
|
defineExpose({
|
|
159
|
+
values,
|
|
160
|
+
|
|
149
161
|
getEditor() {
|
|
150
162
|
return vsEditor || vsDiffEditor;
|
|
151
163
|
},
|
|
152
164
|
|
|
165
|
+
getVsEditor() {
|
|
166
|
+
return vsEditor;
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
getVsDiffEditor() {
|
|
170
|
+
return vsDiffEditor;
|
|
171
|
+
},
|
|
172
|
+
|
|
153
173
|
setEditorValue,
|
|
154
174
|
|
|
155
175
|
focus() {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
</slot>
|
|
34
34
|
</template>
|
|
35
35
|
|
|
36
|
-
<template v-if="pageLength > 0
|
|
36
|
+
<template v-if="pageLength > 0" #right>
|
|
37
37
|
<TMagicScrollbar>
|
|
38
38
|
<slot name="props-panel"></slot>
|
|
39
39
|
</TMagicScrollbar>
|
|
@@ -68,7 +68,6 @@ const codeOptions = inject('codeOptions', {});
|
|
|
68
68
|
const { editorService, uiService } = inject<Services>('services') || {};
|
|
69
69
|
|
|
70
70
|
const root = computed(() => editorService?.get('root'));
|
|
71
|
-
const nodes = computed(() => editorService?.get('nodes') || []);
|
|
72
71
|
|
|
73
72
|
const pageLength = computed(() => editorService?.get('pageLength') || 0);
|
|
74
73
|
const showSrc = computed(() => uiService?.get('showSrc'));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="m-editor-props-panel">
|
|
2
|
+
<div class="m-editor-props-panel" v-if="nodes.length === 1">
|
|
3
3
|
<slot name="props-panel-header"></slot>
|
|
4
4
|
<MForm
|
|
5
5
|
ref="configForm"
|
|
@@ -35,6 +35,7 @@ const configForm = ref<InstanceType<typeof MForm>>();
|
|
|
35
35
|
const curFormConfig = ref<any>([]);
|
|
36
36
|
const services = inject<Services>('services');
|
|
37
37
|
const node = computed(() => services?.editorService.get('node'));
|
|
38
|
+
const nodes = computed(() => services?.editorService.get('nodes') || []);
|
|
38
39
|
const propsPanelSize = computed(() => services?.uiService.get('propsPanelSize') || 'small');
|
|
39
40
|
const stage = computed(() => services?.editorService.get('stage'));
|
|
40
41
|
|
|
@@ -83,14 +83,13 @@ const appendComponent = ({ text, type, data = {} }: ComponentItem): void => {
|
|
|
83
83
|
|
|
84
84
|
const dragstartHandler = ({ text, type, data = {} }: ComponentItem, e: DragEvent) => {
|
|
85
85
|
if (e.dataTransfer) {
|
|
86
|
-
e.dataTransfer.effectAllowed = 'move';
|
|
87
86
|
e.dataTransfer.setData(
|
|
88
|
-
'
|
|
87
|
+
'text/html',
|
|
89
88
|
serialize({
|
|
90
89
|
name: text,
|
|
91
90
|
type,
|
|
92
91
|
...data,
|
|
93
|
-
})
|
|
92
|
+
}),
|
|
94
93
|
);
|
|
95
94
|
}
|
|
96
95
|
};
|
|
@@ -121,6 +120,6 @@ const dragHandler = (e: DragEvent) => {
|
|
|
121
120
|
|
|
122
121
|
if (timeout || !stage.value) return;
|
|
123
122
|
|
|
124
|
-
timeout = stage.value.
|
|
123
|
+
timeout = stage.value.delayedMarkContainer(e);
|
|
125
124
|
};
|
|
126
125
|
</script>
|
|
@@ -22,6 +22,7 @@ const props = defineProps<{
|
|
|
22
22
|
const services = inject<Services>('services');
|
|
23
23
|
const menu = ref<InstanceType<typeof ContentMenu>>();
|
|
24
24
|
const node = computed(() => services?.editorService.get('node'));
|
|
25
|
+
const nodes = computed(() => services?.editorService.get('nodes'));
|
|
25
26
|
const isRoot = computed(() => node.value?.type === NodeType.ROOT);
|
|
26
27
|
const isPage = computed(() => node.value?.type === NodeType.PAGE);
|
|
27
28
|
const componentList = computed(() => services?.componentListService.getList() || []);
|
|
@@ -82,7 +83,7 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() => [
|
|
|
82
83
|
type: 'button',
|
|
83
84
|
text: '新增',
|
|
84
85
|
icon: markRaw(Plus),
|
|
85
|
-
display: () => node.value?.items,
|
|
86
|
+
display: () => node.value?.items && nodes.value?.length === 1,
|
|
86
87
|
items: getSubMenuData.value,
|
|
87
88
|
},
|
|
88
89
|
{
|
|
@@ -91,7 +92,7 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() => [
|
|
|
91
92
|
icon: markRaw(CopyDocument),
|
|
92
93
|
display: () => !isRoot.value,
|
|
93
94
|
handler: () => {
|
|
94
|
-
node.value && services?.editorService.copy(
|
|
95
|
+
node.value && services?.editorService.copy(nodes.value || []);
|
|
95
96
|
},
|
|
96
97
|
},
|
|
97
98
|
{
|
|
@@ -100,7 +101,7 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() => [
|
|
|
100
101
|
icon: markRaw(Delete),
|
|
101
102
|
display: () => !isRoot.value && !isPage.value,
|
|
102
103
|
handler: () => {
|
|
103
|
-
node.value && services?.editorService.remove(
|
|
104
|
+
node.value && services?.editorService.remove(nodes.value || []);
|
|
104
105
|
},
|
|
105
106
|
},
|
|
106
107
|
...props.layerContentMenu,
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
{{ `${data.name} (${data.id})` }}
|
|
4
|
+
</div>
|
|
5
|
+
<div class="layer-node-tool">
|
|
6
|
+
<template v-if="data.type !== 'page'">
|
|
7
|
+
<el-icon v-if="data.visible === false" @click.stop="setNodeVisible(true)" title="点击显示">
|
|
8
|
+
<Hide />
|
|
9
|
+
</el-icon>
|
|
10
|
+
<el-icon v-else @click.stop="setNodeVisible(false)" class="node-lock" title="点击隐藏">
|
|
11
|
+
<View />
|
|
12
|
+
</el-icon>
|
|
13
|
+
</template>
|
|
14
|
+
</div>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<script lang="ts" setup>
|
|
18
|
+
import { inject } from 'vue';
|
|
19
|
+
import { Hide, View } from '@element-plus/icons-vue';
|
|
20
|
+
|
|
21
|
+
import { MNode } from '@tmagic/schema';
|
|
22
|
+
|
|
23
|
+
import { Services } from '@editor/type';
|
|
24
|
+
|
|
25
|
+
const props = defineProps<{
|
|
26
|
+
data: MNode;
|
|
27
|
+
}>();
|
|
28
|
+
|
|
29
|
+
const services = inject<Services>('services');
|
|
30
|
+
const editorService = services?.editorService;
|
|
31
|
+
|
|
32
|
+
const setNodeVisible = (visible: boolean) => {
|
|
33
|
+
if (!editorService) return;
|
|
34
|
+
|
|
35
|
+
editorService.update({
|
|
36
|
+
id: props.data.id,
|
|
37
|
+
visible,
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
</script>
|
|
@@ -35,9 +35,7 @@
|
|
|
35
35
|
<template #default="{ node, data }">
|
|
36
36
|
<div class="cus-tree-node" :id="data.id" @mouseenter="highlightHandler(data)">
|
|
37
37
|
<slot name="layer-node-content" :node="node" :data="data">
|
|
38
|
-
<
|
|
39
|
-
{{ `${data.name} (${data.id})` }}
|
|
40
|
-
</span>
|
|
38
|
+
<LayerNode :data="data"></LayerNode>
|
|
41
39
|
</slot>
|
|
42
40
|
</div>
|
|
43
41
|
</template>
|
|
@@ -63,6 +61,7 @@ import type { MenuButton, MenuComponent, Services } from '@editor/type';
|
|
|
63
61
|
import { Layout } from '@editor/type';
|
|
64
62
|
|
|
65
63
|
import LayerMenu from './LayerMenu.vue';
|
|
64
|
+
import LayerNode from './LayerNode.vue';
|
|
66
65
|
|
|
67
66
|
defineOptions({
|
|
68
67
|
name: 'MEditorLayerPanel',
|
|
@@ -350,7 +349,11 @@ const clickHandler = (data: MNode): void => {
|
|
|
350
349
|
// 右键菜单
|
|
351
350
|
const contextmenu = async (event: MouseEvent, data: MNode): Promise<void> => {
|
|
352
351
|
event.preventDefault();
|
|
353
|
-
|
|
352
|
+
|
|
353
|
+
if (nodes.value.length < 2) {
|
|
354
|
+
await select(data);
|
|
355
|
+
}
|
|
356
|
+
|
|
354
357
|
menu.value?.show(event);
|
|
355
358
|
};
|
|
356
359
|
</script>
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
:expand-on-click-node="false"
|
|
16
16
|
:data="list"
|
|
17
17
|
:highlight-current="true"
|
|
18
|
+
@node-click="clickHandler"
|
|
18
19
|
>
|
|
19
20
|
<template #default="{ data }">
|
|
20
21
|
<div :id="data.id" class="list-container">
|
|
@@ -53,7 +54,7 @@ import { computed, inject, ref } from 'vue';
|
|
|
53
54
|
import { Aim, Close, Coin, Edit } from '@element-plus/icons-vue';
|
|
54
55
|
|
|
55
56
|
import { TMagicButton, tMagicMessageBox, TMagicScrollbar, TMagicTooltip, TMagicTree } from '@tmagic/design';
|
|
56
|
-
import { DataSourceSchema } from '@tmagic/schema';
|
|
57
|
+
import { DataSourceSchema, Id } from '@tmagic/schema';
|
|
57
58
|
|
|
58
59
|
import Icon from '@editor/components/Icon.vue';
|
|
59
60
|
import SearchInput from '@editor/components/SearchInput.vue';
|
|
@@ -66,7 +67,7 @@ defineOptions({
|
|
|
66
67
|
});
|
|
67
68
|
|
|
68
69
|
const services = inject<Partial<Services>>('services', {});
|
|
69
|
-
const { dataSourceService, depService } = inject<Services>('services') || {};
|
|
70
|
+
const { dataSourceService, depService, editorService } = inject<Services>('services') || {};
|
|
70
71
|
|
|
71
72
|
const list = computed(() =>
|
|
72
73
|
Object.values(depService?.targets['data-source'] || {}).map((target) => ({
|
|
@@ -131,4 +132,19 @@ const tree = ref<InstanceType<typeof TMagicTree>>();
|
|
|
131
132
|
const filterTextChangeHandler = (val: string) => {
|
|
132
133
|
tree.value?.filter(val);
|
|
133
134
|
};
|
|
135
|
+
|
|
136
|
+
// 选中组件
|
|
137
|
+
const selectComp = (compId: Id) => {
|
|
138
|
+
const stage = editorService?.get('stage');
|
|
139
|
+
editorService?.select(compId);
|
|
140
|
+
stage?.select(compId);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const clickHandler = (data: any, node: any) => {
|
|
144
|
+
if (data.type === 'node') {
|
|
145
|
+
selectComp(data.id);
|
|
146
|
+
} else if (data.type === 'key') {
|
|
147
|
+
selectComp(node.parent.data.id);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
134
150
|
</script>
|
|
@@ -146,9 +146,8 @@ const contextmenuHandler = (e: MouseEvent) => {
|
|
|
146
146
|
|
|
147
147
|
const dragoverHandler = (e: DragEvent) => {
|
|
148
148
|
e.preventDefault();
|
|
149
|
-
if (e.dataTransfer)
|
|
150
|
-
|
|
151
|
-
}
|
|
149
|
+
if (!e.dataTransfer) return;
|
|
150
|
+
e.dataTransfer.dropEffect = 'move';
|
|
152
151
|
};
|
|
153
152
|
|
|
154
153
|
const dropHandler = async (e: DragEvent) => {
|
|
@@ -164,7 +163,15 @@ const dropHandler = async (e: DragEvent) => {
|
|
|
164
163
|
|
|
165
164
|
if (e.dataTransfer && parent && stageContainer.value && stage) {
|
|
166
165
|
const parseDSL = getConfig('parseDSL');
|
|
167
|
-
|
|
166
|
+
|
|
167
|
+
const data = e.dataTransfer.getData('text/html');
|
|
168
|
+
|
|
169
|
+
if (!data) return;
|
|
170
|
+
|
|
171
|
+
const config = parseDSL(`(${data})`);
|
|
172
|
+
|
|
173
|
+
if (!config) return;
|
|
174
|
+
|
|
168
175
|
const layout = await services?.editorService.getLayout(parent);
|
|
169
176
|
|
|
170
177
|
const containerRect = stageContainer.value.getBoundingClientRect();
|
package/src/services/dep.ts
CHANGED
|
@@ -262,6 +262,7 @@ export class Watcher extends EventEmitter {
|
|
|
262
262
|
Object.values(this.targets).forEach((targets) => {
|
|
263
263
|
Object.values(targets).forEach((target) => {
|
|
264
264
|
nodes.forEach((node) => {
|
|
265
|
+
// 先删除原有依赖,重新收集
|
|
265
266
|
target.removeDep(node);
|
|
266
267
|
this.collectItem(node, target, deep);
|
|
267
268
|
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
.tmagic-data-source-input-text {
|
|
2
|
+
.el-input__wrapper.tmagic-data-source-input-text-wrapper {
|
|
3
|
+
overflow: hidden;
|
|
4
|
+
padding-right: 30px;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.el-input__inner {
|
|
8
|
+
display: flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
overflow: hidden;
|
|
11
|
+
white-space: nowrap;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.tmagic-data-source-input-icon {
|
|
15
|
+
position: absolute;
|
|
16
|
+
right: 7px;
|
|
17
|
+
}
|
|
18
|
+
}
|
package/src/theme/theme.scss
CHANGED
package/src/utils/dep.ts
CHANGED
|
@@ -11,6 +11,10 @@ export const createCodeBlockTarget = (id: Id, codeBlock: CodeBlockContent) =>
|
|
|
11
11
|
id,
|
|
12
12
|
name: codeBlock.name,
|
|
13
13
|
isTarget: (key: string | number, value: any) => {
|
|
14
|
+
if (id === value) {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
|
|
14
18
|
if (value?.hookType === HookType.CODE && !isEmpty(value.hookData)) {
|
|
15
19
|
const index = value.hookData.findIndex((item: HookData) => item.codeId === id);
|
|
16
20
|
return Boolean(index > -1);
|
|
@@ -25,5 +29,7 @@ export const createDataSourceTarget = (id: Id, ds: DataSourceSchema) =>
|
|
|
25
29
|
type: 'data-source',
|
|
26
30
|
id,
|
|
27
31
|
name: ds.title || `${id}`,
|
|
28
|
-
isTarget: (key: string | number, value: any) =>
|
|
32
|
+
isTarget: (key: string | number, value: any) =>
|
|
33
|
+
// 关联数据源对象或者在模板在使用数据源
|
|
34
|
+
(value.isBindDataSource && value.dataSourceId) || (typeof value === 'string' && value.includes(`${id}`)),
|
|
29
35
|
});
|