@tmagic/editor 1.2.0-beta.2 → 1.2.0-beta.4
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.mjs +415 -400
- package/dist/tmagic-editor.mjs.map +1 -1
- package/dist/tmagic-editor.umd.js +417 -402
- package/dist/tmagic-editor.umd.js.map +1 -1
- package/package.json +9 -9
- package/src/components/ContentMenu.vue +2 -2
- package/src/components/Icon.vue +6 -4
- package/src/components/ToolButton.vue +29 -14
- package/src/fields/CodeSelect.vue +6 -6
- package/src/fields/UISelect.vue +8 -7
- package/src/layouts/AddPageBox.vue +2 -1
- package/src/layouts/Framework.vue +3 -2
- package/src/layouts/PropsPanel.vue +3 -8
- package/src/layouts/sidebar/ComponentListPanel.vue +13 -11
- package/src/layouts/sidebar/LayerPanel.vue +252 -162
- package/src/layouts/sidebar/Sidebar.vue +4 -2
- package/src/layouts/sidebar/TabPane.vue +4 -2
- package/src/layouts/sidebar/code-block/CodeBlockEditor.vue +21 -16
- package/src/layouts/sidebar/code-block/CodeBlockList.vue +19 -19
- package/src/layouts/workspace/PageBar.vue +11 -10
- package/types/Editor.vue.d.ts +2 -2
- package/types/layouts/sidebar/LayerPanel.vue.d.ts +16 -883
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<TMagicScrollbar
|
|
3
|
+
class="magic-editor-layer-panel"
|
|
4
|
+
@mouseenter="addSelectModeListener"
|
|
5
|
+
@mouseleave="removeSelectModeListener"
|
|
6
|
+
>
|
|
3
7
|
<slot name="layer-panel-header"></slot>
|
|
4
8
|
|
|
5
|
-
<
|
|
9
|
+
<TMagicInput
|
|
6
10
|
class="filterInput"
|
|
7
11
|
size="small"
|
|
8
12
|
placeholder="输入关键字进行过滤"
|
|
9
13
|
clearable
|
|
10
14
|
v-model="filterText"
|
|
11
15
|
@change="filterTextChangeHandler"
|
|
12
|
-
></
|
|
16
|
+
></TMagicInput>
|
|
13
17
|
|
|
14
|
-
<
|
|
18
|
+
<TMagicTree
|
|
15
19
|
v-if="values.length"
|
|
16
20
|
ref="tree"
|
|
17
21
|
node-key="id"
|
|
18
22
|
empty-text="页面空荡荡的"
|
|
19
23
|
draggable
|
|
20
|
-
:default-expanded-keys="
|
|
24
|
+
:default-expanded-keys="defaultExpandedKeys"
|
|
21
25
|
:load="loadItems"
|
|
22
26
|
:data="values"
|
|
23
27
|
:expand-on-click-node="false"
|
|
@@ -27,20 +31,22 @@
|
|
|
27
31
|
}"
|
|
28
32
|
:filter-node-method="filterNode"
|
|
29
33
|
:allow-drop="allowDrop"
|
|
34
|
+
:show-checkbox="isMultiSelectStatus || selectedIds.length > 1"
|
|
30
35
|
@node-click="clickHandler"
|
|
31
36
|
@node-contextmenu="contextmenu"
|
|
32
37
|
@node-drag-end="handleDragEnd"
|
|
33
38
|
@node-collapse="handleCollapse"
|
|
34
39
|
@node-expand="handleExpand"
|
|
40
|
+
@check="multiClickHandler"
|
|
41
|
+
@mousedown="toggleClickFlag"
|
|
42
|
+
@mouseup="toggleClickFlag"
|
|
35
43
|
>
|
|
36
44
|
<template #default="{ node, data }">
|
|
37
45
|
<div
|
|
38
46
|
:id="data.id"
|
|
39
47
|
class="cus-tree-node"
|
|
40
|
-
@mousedown="toggleClickFlag"
|
|
41
|
-
@mouseup="toggleClickFlag"
|
|
42
48
|
@mouseenter="highlightHandler(data)"
|
|
43
|
-
:class="{ 'cus-tree-node-hover': canHighlight
|
|
49
|
+
:class="{ 'cus-tree-node-hover': canHighlight(data) }"
|
|
44
50
|
>
|
|
45
51
|
<slot name="layer-node-content" :node="node" :data="data">
|
|
46
52
|
<span>
|
|
@@ -49,32 +55,56 @@
|
|
|
49
55
|
</slot>
|
|
50
56
|
</div>
|
|
51
57
|
</template>
|
|
52
|
-
</
|
|
58
|
+
</TMagicTree>
|
|
53
59
|
|
|
54
60
|
<teleport to="body">
|
|
55
61
|
<layer-menu ref="menu"></layer-menu>
|
|
56
62
|
</teleport>
|
|
57
|
-
</
|
|
63
|
+
</TMagicScrollbar>
|
|
58
64
|
</template>
|
|
59
65
|
|
|
60
|
-
<script lang="ts">
|
|
61
|
-
import { computed,
|
|
62
|
-
import
|
|
66
|
+
<script lang="ts" setup>
|
|
67
|
+
import { computed, inject, ref, watch } from 'vue';
|
|
68
|
+
import KeyController from 'keycon';
|
|
63
69
|
import { throttle } from 'lodash-es';
|
|
64
70
|
|
|
71
|
+
import { TMagicInput, TMagicScrollbar, TMagicTree } from '@tmagic/design';
|
|
65
72
|
import type { Id, MNode, MPage } from '@tmagic/schema';
|
|
66
73
|
import { MContainer, NodeType } from '@tmagic/schema';
|
|
67
74
|
import StageCore from '@tmagic/stage';
|
|
68
75
|
|
|
69
|
-
import type { EditorService } from '../../services/editor';
|
|
70
76
|
import type { Services } from '../../type';
|
|
71
77
|
import { Layout } from '../../type';
|
|
72
78
|
|
|
73
79
|
import LayerMenu from './LayerMenu.vue';
|
|
74
80
|
|
|
75
81
|
const throttleTime = 150;
|
|
82
|
+
const services = inject<Services>('services');
|
|
83
|
+
const tree = ref<InstanceType<typeof TMagicTree>>();
|
|
84
|
+
const menu = ref<InstanceType<typeof LayerMenu>>();
|
|
85
|
+
const editorService = services?.editorService;
|
|
86
|
+
const page = computed(() => editorService?.get('page'));
|
|
87
|
+
const values = computed(() => (page.value ? [page.value] : []));
|
|
88
|
+
// 多选选中的节点数组
|
|
89
|
+
const selectedNodes = ref<MNode[]>([]);
|
|
90
|
+
// 多选选中的组件id数组
|
|
91
|
+
const selectedIds = computed(() => selectedNodes.value.map((node: MNode) => node.id));
|
|
92
|
+
// 是否多选
|
|
93
|
+
const isMultiSelectStatus = ref(false);
|
|
94
|
+
// 多选场景 取消选中的那个节点id
|
|
95
|
+
const spliceNodeKey = ref<Id>();
|
|
96
|
+
const filterText = ref('');
|
|
97
|
+
// 默认展开节点
|
|
98
|
+
const defaultExpandedKeys = computed(() => (selectedIds.value.length > 0 ? selectedIds.value : []));
|
|
99
|
+
|
|
100
|
+
editorService?.on('remove', () => {
|
|
101
|
+
setTimeout(() => {
|
|
102
|
+
tree.value?.getNode(editorService.get('node').id)?.updateChildren();
|
|
103
|
+
}, 0);
|
|
104
|
+
});
|
|
76
105
|
|
|
77
|
-
|
|
106
|
+
// 触发画布单选
|
|
107
|
+
const select = async (data: MNode) => {
|
|
78
108
|
if (!data.id) {
|
|
79
109
|
throw new Error('没有id');
|
|
80
110
|
}
|
|
@@ -83,7 +113,14 @@ const select = async (data: MNode, editorService?: EditorService) => {
|
|
|
83
113
|
editorService?.get<StageCore>('stage')?.select(data.id);
|
|
84
114
|
};
|
|
85
115
|
|
|
86
|
-
|
|
116
|
+
// 触发画布多选
|
|
117
|
+
const multiSelect = async (data: Id[]) => {
|
|
118
|
+
await editorService?.multiSelect(data);
|
|
119
|
+
editorService?.get<StageCore>('stage')?.multiSelect(data);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// 触发画布高亮
|
|
123
|
+
const highlight = (data: MNode) => {
|
|
87
124
|
if (!data?.id) {
|
|
88
125
|
throw new Error('没有id');
|
|
89
126
|
}
|
|
@@ -91,58 +128,97 @@ const highlight = (data: MNode, editorService?: EditorService) => {
|
|
|
91
128
|
editorService?.get<StageCore>('stage')?.highlight(data.id);
|
|
92
129
|
};
|
|
93
130
|
|
|
94
|
-
const
|
|
95
|
-
allowDrop: (draggingNode: any, dropNode: any, type: string): boolean => {
|
|
96
|
-
const { data } = dropNode || {};
|
|
97
|
-
const { data: ingData } = draggingNode;
|
|
131
|
+
const expandedKeys = new Map<Id, Id>();
|
|
98
132
|
|
|
99
|
-
|
|
133
|
+
// tree方法:拖拽时判定目标节点能否成为拖动目标位置
|
|
134
|
+
const allowDrop = (draggingNode: any, dropNode: any, type: string): boolean => {
|
|
135
|
+
const { data } = dropNode || {};
|
|
136
|
+
const { data: ingData } = draggingNode;
|
|
100
137
|
|
|
101
|
-
|
|
102
|
-
if (ingType === NodeType.PAGE && data.type !== NodeType.PAGE) return false;
|
|
103
|
-
if (!data || !data.type) return false;
|
|
104
|
-
if (['prev', 'next'].includes(type)) return true;
|
|
105
|
-
if (data.items || data.type === 'container') return true;
|
|
138
|
+
const { type: ingType } = ingData;
|
|
106
139
|
|
|
107
|
-
|
|
108
|
-
|
|
140
|
+
if (ingType !== NodeType.PAGE && data.type === NodeType.PAGE) return false;
|
|
141
|
+
if (ingType === NodeType.PAGE && data.type !== NodeType.PAGE) return false;
|
|
142
|
+
if (!data || !data.type) return false;
|
|
143
|
+
if (['prev', 'next'].includes(type)) return true;
|
|
144
|
+
if (data.items || data.type === 'container') return true;
|
|
109
145
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const { data: node } = e;
|
|
113
|
-
const parent = editorService?.getParentById(node.id, false) as MContainer;
|
|
114
|
-
const layout = await editorService?.getLayout(parent);
|
|
115
|
-
node.style.position = layout;
|
|
116
|
-
if (layout === Layout.RELATIVE) {
|
|
117
|
-
node.style.top = 0;
|
|
118
|
-
node.style.left = 0;
|
|
119
|
-
}
|
|
120
|
-
const { data } = tree.value;
|
|
121
|
-
const [page] = data as [MPage];
|
|
122
|
-
editorService?.update(page);
|
|
123
|
-
},
|
|
124
|
-
});
|
|
146
|
+
return false;
|
|
147
|
+
};
|
|
125
148
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const
|
|
149
|
+
// tree事件:拖拽结束时(可能未成功)触发的事件
|
|
150
|
+
const handleDragEnd = async (e: any) => {
|
|
151
|
+
if (!tree.value) return;
|
|
152
|
+
const { data: node } = e;
|
|
153
|
+
const parent = editorService?.getParentById(node.id, false) as MContainer;
|
|
154
|
+
const layout = await editorService?.getLayout(parent);
|
|
155
|
+
node.style.position = layout;
|
|
156
|
+
if (layout === Layout.RELATIVE) {
|
|
157
|
+
node.style.top = 0;
|
|
158
|
+
node.style.left = 0;
|
|
159
|
+
}
|
|
160
|
+
const data = tree.value.getData();
|
|
161
|
+
const [page] = data as [MPage];
|
|
162
|
+
editorService?.update(page);
|
|
163
|
+
};
|
|
130
164
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
165
|
+
// tree方法: 加载子树数据的方法
|
|
166
|
+
const loadItems = (node: any, resolve: Function) => {
|
|
167
|
+
if (Array.isArray(node.data)) {
|
|
168
|
+
return resolve(node.data);
|
|
169
|
+
}
|
|
170
|
+
if (Array.isArray(node.data?.items)) {
|
|
171
|
+
return resolve(node.data?.items);
|
|
172
|
+
}
|
|
173
|
+
resolve([]);
|
|
174
|
+
};
|
|
137
175
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
176
|
+
// tree事件:节点被关闭时触发的事件
|
|
177
|
+
const handleCollapse = (data: MNode) => {
|
|
178
|
+
expandedKeys.delete(data.id);
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
// tree事件:节点被展开时触发的事件
|
|
182
|
+
const handleExpand = (data: MNode) => {
|
|
183
|
+
const parent = editorService?.getParentById(data.id);
|
|
184
|
+
if (!parent?.id) return;
|
|
185
|
+
expandedKeys.set(parent.id, parent.id);
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
// tree方法:对树节点进行筛选时执行的方法
|
|
189
|
+
const filterNode = (value: string, data: MNode): boolean => {
|
|
190
|
+
if (!value) {
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
let name = '';
|
|
194
|
+
if (data.name) {
|
|
195
|
+
name = data.name;
|
|
196
|
+
} else if (data.items) {
|
|
197
|
+
name = 'container';
|
|
198
|
+
}
|
|
199
|
+
return `${data.id}${name}${data.type}`.indexOf(value) !== -1;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// 过滤关键字
|
|
203
|
+
const filterTextChangeHandler = (val: string) => {
|
|
204
|
+
tree.value?.filter(val);
|
|
205
|
+
};
|
|
142
206
|
|
|
143
|
-
|
|
207
|
+
// 展开树节点
|
|
208
|
+
const expandNodes = () => {
|
|
209
|
+
expandedKeys.forEach((key) => {
|
|
210
|
+
if (!tree.value) return;
|
|
211
|
+
tree.value.getNode(key)?.expand();
|
|
212
|
+
});
|
|
213
|
+
};
|
|
144
214
|
|
|
145
|
-
|
|
215
|
+
watch(
|
|
216
|
+
() => editorService?.get('nodes'),
|
|
217
|
+
(nodes) => {
|
|
218
|
+
if (!tree.value) return;
|
|
219
|
+
if (!editorService) return;
|
|
220
|
+
if (!nodes) return;
|
|
221
|
+
selectedNodes.value = nodes as unknown as MNode[];
|
|
146
222
|
|
|
147
223
|
const parent = editorService.get('parent');
|
|
148
224
|
if (!parent?.id) return;
|
|
@@ -152,121 +228,135 @@ const useStatus = (tree: Ref<InstanceType<typeof ElTree> | undefined>, editorSer
|
|
|
152
228
|
|
|
153
229
|
setTimeout(() => {
|
|
154
230
|
tree.value &&
|
|
155
|
-
Object.entries(tree.value.
|
|
231
|
+
Object.entries(tree.value.getStore().nodesMap).forEach(([id, node]: [string, any]) => {
|
|
156
232
|
if (node.expanded && node.data.items) {
|
|
157
233
|
expandedKeys.set(id, id);
|
|
158
234
|
}
|
|
159
235
|
});
|
|
160
236
|
expandNodes();
|
|
161
237
|
});
|
|
162
|
-
}
|
|
238
|
+
},
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
// 设置树节点选中状态
|
|
242
|
+
const setTreeKeyStatus = () => {
|
|
243
|
+
if (!tree.value) return;
|
|
244
|
+
if (selectedIds.value.length === 0) {
|
|
245
|
+
tree.value.setCheckedKeys([]);
|
|
246
|
+
tree.value.setCurrentKey();
|
|
247
|
+
} else if (selectedIds.value.length === 1 && !isMultiSelectStatus.value) {
|
|
248
|
+
// 选中1个
|
|
249
|
+
tree.value.setCurrentKey(selectedIds.value[0], true);
|
|
250
|
+
tree.value.setCheckedKeys([]);
|
|
251
|
+
} else {
|
|
252
|
+
// 多选框选中多个
|
|
253
|
+
tree.value.setCheckedKeys(selectedIds.value);
|
|
254
|
+
tree.value.setCurrentKey();
|
|
255
|
+
}
|
|
256
|
+
};
|
|
163
257
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
258
|
+
watch(selectedIds, () => {
|
|
259
|
+
setTreeKeyStatus();
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
const keycon = ref<KeyController>();
|
|
263
|
+
// 监听模式选择
|
|
264
|
+
const addSelectModeListener = () => {
|
|
265
|
+
const isMac = /mac os x/.test(navigator.userAgent.toLowerCase());
|
|
266
|
+
const ctrl = isMac ? 'meta' : 'ctrl';
|
|
267
|
+
keycon.value = new KeyController();
|
|
268
|
+
keycon.value.keydown(ctrl, (e) => {
|
|
269
|
+
e.inputEvent.preventDefault();
|
|
270
|
+
isMultiSelectStatus.value = true;
|
|
271
|
+
});
|
|
272
|
+
// ctrl+tab切到其他窗口,需要将多选状态置为false
|
|
273
|
+
keycon.value.on('blur', () => {
|
|
274
|
+
isMultiSelectStatus.value = false;
|
|
275
|
+
});
|
|
276
|
+
keycon.value.keyup(ctrl, (e) => {
|
|
277
|
+
e.inputEvent.preventDefault();
|
|
278
|
+
isMultiSelectStatus.value = false;
|
|
279
|
+
});
|
|
280
|
+
};
|
|
281
|
+
// 移除监听
|
|
282
|
+
const removeSelectModeListener = () => {
|
|
283
|
+
keycon.value?.destroy();
|
|
284
|
+
// 如果鼠标移出监听范围,且当前只选中了一个,置为单选模式(修复按住ctrl不放但鼠标移出的情况)
|
|
285
|
+
if (selectedIds.value.length === 1) isMultiSelectStatus.value = false;
|
|
191
286
|
};
|
|
192
287
|
|
|
193
|
-
|
|
194
|
-
|
|
288
|
+
// 鼠标是否按下标志,用于高亮状态互斥
|
|
289
|
+
const clicked = ref(false);
|
|
290
|
+
// 高亮的节点
|
|
291
|
+
const highlightNode = computed(() => editorService?.get('highlightNode'));
|
|
195
292
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
let name = '';
|
|
201
|
-
if (data.name) {
|
|
202
|
-
name = data.name;
|
|
203
|
-
} else if (data.items) {
|
|
204
|
-
name = 'container';
|
|
205
|
-
}
|
|
206
|
-
return `${data.id}${name}${data.type}`.indexOf(value) !== -1;
|
|
207
|
-
},
|
|
293
|
+
// 鼠标在组件树移动触发高亮
|
|
294
|
+
const highlightHandler = throttle((data: MNode) => {
|
|
295
|
+
highlight(data);
|
|
296
|
+
}, throttleTime);
|
|
208
297
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
});
|
|
298
|
+
const toggleClickFlag = () => {
|
|
299
|
+
clicked.value = !clicked.value;
|
|
300
|
+
};
|
|
213
301
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const tree = ref<InstanceType<typeof ElTree>>();
|
|
222
|
-
const menu = ref<InstanceType<typeof LayerMenu>>();
|
|
223
|
-
const clicked = ref(false);
|
|
224
|
-
const editorService = services?.editorService;
|
|
225
|
-
const highlightHandler = throttle((data: MNode) => {
|
|
226
|
-
highlight(data, editorService);
|
|
227
|
-
}, throttleTime);
|
|
228
|
-
|
|
229
|
-
const toggleClickFlag = () => {
|
|
230
|
-
clicked.value = !clicked.value;
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
const statusData = useStatus(tree, editorService);
|
|
234
|
-
const canHighlight = computed(
|
|
235
|
-
() => statusData.highlightNode.value?.id !== statusData.clickNode.value?.id && !clicked.value,
|
|
236
|
-
);
|
|
237
|
-
|
|
238
|
-
editorService?.on('remove', () => {
|
|
239
|
-
setTimeout(() => {
|
|
240
|
-
tree.value?.getNode(editorService.get('node').id)?.updateChildren();
|
|
241
|
-
}, 0);
|
|
242
|
-
});
|
|
302
|
+
// 是否满足展示高亮
|
|
303
|
+
const canHighlight = (data: MNode) => {
|
|
304
|
+
if (clicked.value) return false;
|
|
305
|
+
return (
|
|
306
|
+
data.id === highlightNode?.value?.id && !selectedIds.value.includes(data.id) && spliceNodeKey.value !== data.id
|
|
307
|
+
);
|
|
308
|
+
};
|
|
243
309
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
highlightHandler,
|
|
252
|
-
toggleClickFlag,
|
|
253
|
-
canHighlight,
|
|
254
|
-
|
|
255
|
-
clickHandler(data: MNode): void {
|
|
256
|
-
if (services?.uiService.get<boolean>('uiSelectMode')) {
|
|
257
|
-
document.dispatchEvent(new CustomEvent('ui-select', { detail: data }));
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
tree.value?.setCurrentKey(data.id);
|
|
261
|
-
select(data, editorService);
|
|
262
|
-
},
|
|
263
|
-
|
|
264
|
-
async contextmenu(event: MouseEvent, data: MNode) {
|
|
265
|
-
event.preventDefault();
|
|
266
|
-
await select(data, editorService);
|
|
267
|
-
menu.value?.show(event);
|
|
268
|
-
},
|
|
269
|
-
};
|
|
270
|
-
},
|
|
310
|
+
// 监听选择模式,针对多选情况做一些处理
|
|
311
|
+
watch(isMultiSelectStatus, () => {
|
|
312
|
+
// 多选模式如果已存在第一个选中的元素是页面(magic-ui-page) 剔除页面选中状态
|
|
313
|
+
if (isMultiSelectStatus.value && selectedNodes.value.length === 1 && selectedNodes.value[0].type === NodeType.PAGE) {
|
|
314
|
+
selectedNodes.value = [];
|
|
315
|
+
}
|
|
271
316
|
});
|
|
317
|
+
|
|
318
|
+
// 选择节点多选框
|
|
319
|
+
const multiClickHandler = (data: MNode): void => {
|
|
320
|
+
if (!data?.id) {
|
|
321
|
+
throw new Error('没有id');
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// 页面(magic-ui-page)不可选中
|
|
325
|
+
if (data.type === NodeType.PAGE) {
|
|
326
|
+
tree.value?.setCheckedKeys([]);
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const index = selectedNodes.value.findIndex((node) => node.id === data.id);
|
|
331
|
+
if (index !== -1) {
|
|
332
|
+
// 已经包含就移除掉
|
|
333
|
+
selectedNodes.value.splice(index, 1);
|
|
334
|
+
spliceNodeKey.value = data.id;
|
|
335
|
+
} else {
|
|
336
|
+
selectedNodes.value = [...selectedNodes.value, data];
|
|
337
|
+
}
|
|
338
|
+
tree.value?.setCheckedKeys(selectedIds.value);
|
|
339
|
+
multiSelect(selectedIds.value);
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
// 点击节点
|
|
343
|
+
const clickHandler = (data: MNode): void => {
|
|
344
|
+
if (!isMultiSelectStatus.value) {
|
|
345
|
+
if (services?.uiService.get<boolean>('uiSelectMode')) {
|
|
346
|
+
document.dispatchEvent(new CustomEvent('ui-select', { detail: data }));
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
tree.value?.setCurrentKey(data.id);
|
|
350
|
+
select(data);
|
|
351
|
+
} else {
|
|
352
|
+
multiClickHandler(data);
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
// 右键菜单
|
|
357
|
+
const contextmenu = async (event: MouseEvent, data: MNode): Promise<void> => {
|
|
358
|
+
event.preventDefault();
|
|
359
|
+
await select(data);
|
|
360
|
+
menu.value?.show(event);
|
|
361
|
+
};
|
|
272
362
|
</script>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<TMagicTabs
|
|
3
3
|
v-if="data.type === 'tabs' && data.items.length"
|
|
4
4
|
class="m-editor-sidebar"
|
|
5
5
|
v-model="activeTabName"
|
|
@@ -35,12 +35,14 @@
|
|
|
35
35
|
<slot name="code-block-edit-panel-header" :id="id"></slot>
|
|
36
36
|
</template>
|
|
37
37
|
</tab-pane>
|
|
38
|
-
</
|
|
38
|
+
</TMagicTabs>
|
|
39
39
|
</template>
|
|
40
40
|
|
|
41
41
|
<script lang="ts" setup>
|
|
42
42
|
import { ref, watch } from 'vue';
|
|
43
43
|
|
|
44
|
+
import { TMagicTabs } from '@tmagic/design';
|
|
45
|
+
|
|
44
46
|
import { SideBarData } from '../../type';
|
|
45
47
|
|
|
46
48
|
import TabPane from './TabPane.vue';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<TMagicTabPane v-if="config" :name="config.text">
|
|
3
3
|
<template #label>
|
|
4
4
|
<div :key="config.text">
|
|
5
5
|
<m-icon v-if="config.icon" :icon="config.icon"></m-icon>
|
|
@@ -61,13 +61,15 @@
|
|
|
61
61
|
/>
|
|
62
62
|
</template>
|
|
63
63
|
</component>
|
|
64
|
-
</
|
|
64
|
+
</TMagicTabPane>
|
|
65
65
|
</template>
|
|
66
66
|
|
|
67
67
|
<script lang="ts" setup>
|
|
68
68
|
import { computed } from 'vue';
|
|
69
69
|
import { Coin, EditPen, Files } from '@element-plus/icons-vue';
|
|
70
70
|
|
|
71
|
+
import { TMagicTabPane } from '@tmagic/design';
|
|
72
|
+
|
|
71
73
|
import MIcon from '../../components/Icon.vue';
|
|
72
74
|
import { SideComponent, SideItem } from '../../type';
|
|
73
75
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<TMagicDialog
|
|
3
3
|
v-model="isShowCodeBlockEditor"
|
|
4
4
|
class="code-editor-dialog"
|
|
5
5
|
:title="currentTitle"
|
|
@@ -10,16 +10,15 @@
|
|
|
10
10
|
<layout v-model:left="left" :min-left="45" class="code-editor-layout">
|
|
11
11
|
<!-- 左侧列表 -->
|
|
12
12
|
<template #left v-if="mode === CodeEditorMode.LIST">
|
|
13
|
-
<
|
|
13
|
+
<TMagicTree
|
|
14
14
|
v-if="!isEmpty(state.codeList)"
|
|
15
|
-
|
|
15
|
+
class="side-tree"
|
|
16
16
|
node-key="id"
|
|
17
17
|
empty-text="暂无代码块"
|
|
18
18
|
:data="state.codeList"
|
|
19
19
|
:highlight-current="true"
|
|
20
|
-
@node-click="selectHandler"
|
|
21
20
|
:current-node-key="state.codeList[0].id"
|
|
22
|
-
|
|
21
|
+
@node-click="selectHandler"
|
|
23
22
|
>
|
|
24
23
|
<template #default="{ data }">
|
|
25
24
|
<div :id="data.id" class="list-container">
|
|
@@ -28,7 +27,7 @@
|
|
|
28
27
|
</div>
|
|
29
28
|
</div>
|
|
30
29
|
</template>
|
|
31
|
-
</
|
|
30
|
+
</TMagicTree>
|
|
32
31
|
</template>
|
|
33
32
|
<!-- 右侧区域 -->
|
|
34
33
|
<template #center>
|
|
@@ -41,11 +40,16 @@
|
|
|
41
40
|
]"
|
|
42
41
|
>
|
|
43
42
|
<slot name="code-block-edit-panel-header" :id="id"></slot>
|
|
44
|
-
<
|
|
43
|
+
<TMagicCard shadow="never">
|
|
45
44
|
<template #header>
|
|
46
45
|
<div class="code-name-wrapper">
|
|
47
46
|
<div class="code-name-label">代码块名称</div>
|
|
48
|
-
<
|
|
47
|
+
<TMagicInput
|
|
48
|
+
v-if="codeConfig"
|
|
49
|
+
class="code-name-input"
|
|
50
|
+
v-model="codeConfig.name"
|
|
51
|
+
:disabled="!editable"
|
|
52
|
+
/>
|
|
49
53
|
</div>
|
|
50
54
|
</template>
|
|
51
55
|
<div class="m-editor-wrapper">
|
|
@@ -63,26 +67,27 @@
|
|
|
63
67
|
}"
|
|
64
68
|
></magic-code-editor>
|
|
65
69
|
<div class="m-editor-content-bottom" v-if="editable">
|
|
66
|
-
<
|
|
67
|
-
<
|
|
70
|
+
<TMagicButton type="primary" class="button" @click="saveCode">保存</TMagicButton>
|
|
71
|
+
<TMagicButton type="primary" class="button" @click="saveAndClose">关闭</TMagicButton>
|
|
68
72
|
</div>
|
|
69
73
|
<div class="m-editor-content-bottom" v-else>
|
|
70
|
-
<
|
|
74
|
+
<TMagicButton type="primary" class="button" @click="saveAndClose">关闭</TMagicButton>
|
|
71
75
|
</div>
|
|
72
76
|
</div>
|
|
73
|
-
</
|
|
77
|
+
</TMagicCard>
|
|
74
78
|
</div>
|
|
75
79
|
</template>
|
|
76
80
|
</layout>
|
|
77
|
-
</
|
|
81
|
+
</TMagicDialog>
|
|
78
82
|
</template>
|
|
79
83
|
|
|
80
84
|
<script lang="ts" setup>
|
|
81
85
|
import { computed, inject, reactive, ref, watchEffect } from 'vue';
|
|
82
|
-
import { ElMessage } from 'element-plus';
|
|
83
86
|
import { forIn, isEmpty } from 'lodash-es';
|
|
84
87
|
import type * as monaco from 'monaco-editor';
|
|
85
88
|
|
|
89
|
+
import { TMagicButton, TMagicCard, TMagicDialog, TMagicInput, tMagicMessage, TMagicTree } from '@tmagic/design';
|
|
90
|
+
|
|
86
91
|
import type { CodeBlockContent, CodeDslList, ListState, Services } from '../../../type';
|
|
87
92
|
import { CodeEditorMode } from '../../../type';
|
|
88
93
|
import MagicCodeEditor from '../../CodeEditor.vue';
|
|
@@ -137,7 +142,7 @@ const saveCode = async (): Promise<boolean> => {
|
|
|
137
142
|
/* eslint no-eval: "off" */
|
|
138
143
|
codeConfig.value.content = eval(codeContent);
|
|
139
144
|
} catch (e: any) {
|
|
140
|
-
|
|
145
|
+
tMagicMessage.error(e.stack);
|
|
141
146
|
return false;
|
|
142
147
|
}
|
|
143
148
|
// 存入dsl
|
|
@@ -145,7 +150,7 @@ const saveCode = async (): Promise<boolean> => {
|
|
|
145
150
|
name: codeConfig.value.name,
|
|
146
151
|
content: codeConfig.value.content,
|
|
147
152
|
});
|
|
148
|
-
|
|
153
|
+
tMagicMessage.success('代码保存成功');
|
|
149
154
|
return true;
|
|
150
155
|
};
|
|
151
156
|
|