@cicctencent/low-code-editor 0.0.0-stage → 1.1.9-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +2 -2
  2. package/api-extractor.json +36 -0
  3. package/dist/ActivityBar/index.vue.d.ts +29 -0
  4. package/dist/ComponentTree/components/List.vue.d.ts +29 -0
  5. package/dist/ComponentTree/index.vue.d.ts +32 -0
  6. package/dist/DataSource/index.vue.d.ts +34 -0
  7. package/dist/HeaderBar/components/BarTooltip.vue.d.ts +37 -0
  8. package/dist/HeaderBar/index.vue.d.ts +49 -0
  9. package/dist/NavTree/index.vue.d.ts +48 -0
  10. package/dist/SchemaConfig/index.vue.d.ts +36 -0
  11. package/dist/SidebarPane/index.vue.d.ts +38 -0
  12. package/dist/components/ComponentTreeFavorites/components/List.vue.d.ts +41 -0
  13. package/dist/components/ComponentTreeFavorites/components/Tips.vue.d.ts +16 -0
  14. package/dist/components/ComponentTreeFavorites/index.vue.d.ts +44 -0
  15. package/dist/components/ImageGuide/index.vue.d.ts +45 -0
  16. package/dist/components.d.ts +7 -0
  17. package/dist/composables/useComponentTree.d.ts +13 -0
  18. package/dist/config/ace.config.d.ts +3 -0
  19. package/dist/domain/EditorDomain.d.ts +89 -0
  20. package/dist/editor.d.ts +286 -0
  21. package/dist/editor.vue.d.ts +104 -0
  22. package/dist/formily/ActivityBar/index.d.ts +4 -0
  23. package/dist/formily/ComponentTree/index.d.ts +4 -0
  24. package/dist/formily/DataBinding/index.d.ts +4 -0
  25. package/dist/formily/DataBinding/index.vue.d.ts +26 -0
  26. package/dist/formily/DataSource/index.d.ts +4 -0
  27. package/dist/formily/MenuTable/PageList.vue.d.ts +28 -0
  28. package/dist/formily/MenuTable/index.d.ts +4 -0
  29. package/dist/formily/MenuTable/index.vue.d.ts +30 -0
  30. package/dist/formily/MenuTable/useMenuTable.d.ts +227 -0
  31. package/dist/formily/NavTree/index.d.ts +4 -0
  32. package/dist/formily/SidebarPane/index.d.ts +4 -0
  33. package/dist/formily/index.d.ts +2 -0
  34. package/dist/index.css +1167 -0
  35. package/dist/index.d.ts +250 -0
  36. package/dist/index.js +34171 -0
  37. package/dist/utils/base64.d.ts +4 -0
  38. package/dist/utils/index.d.ts +27 -0
  39. package/package.json +58 -4
  40. package/rollup.config.js +3 -0
  41. package/src/ActivityBar/index.vue +71 -0
  42. package/src/ComponentTree/components/List.vue +310 -0
  43. package/src/ComponentTree/index.vue +77 -0
  44. package/src/DataSource/index.vue +640 -0
  45. package/src/HeaderBar/components/BarDialog.vue +43 -0
  46. package/src/HeaderBar/components/BarTooltip.vue +33 -0
  47. package/src/HeaderBar/index.vue +513 -0
  48. package/src/NavTree/index.vue +345 -0
  49. package/src/SchemaConfig/index.vue +150 -0
  50. package/src/SidebarPane/index.vue +199 -0
  51. package/src/SynBarComponent/index.vue +31 -0
  52. package/src/assets/font/NotoSansSC.ttf +0 -0
  53. package/src/assets/img/header/edit.png +0 -0
  54. package/src/assets/img/header/link.png +0 -0
  55. package/src/assets/img/header/link.svg +9 -0
  56. package/src/assets/img/header/preview.png +0 -0
  57. package/src/assets/img/header/preview.svg +9 -0
  58. package/src/assets/img/header/redo.svg +1 -0
  59. package/src/assets/img/header/undo.svg +1 -0
  60. package/src/assets/img/tool/all-select.svg +9 -0
  61. package/src/assets/img/tool/all.svg +9 -0
  62. package/src/assets/img/tool/collect.svg +6 -0
  63. package/src/assets/img/tool/collection-select.svg +3 -0
  64. package/src/assets/img/tool/collection-tag.svg +5 -0
  65. package/src/assets/img/tool/info.svg +4 -0
  66. package/src/assets/img/tool/union.svg +4 -0
  67. package/src/components/ComponentTreeFavorites/components/List.vue +452 -0
  68. package/src/components/ComponentTreeFavorites/components/Tips.vue +108 -0
  69. package/src/components/ComponentTreeFavorites/index.vue +180 -0
  70. package/src/components/ImageGuide/index.vue +100 -0
  71. package/src/components.ts +23 -0
  72. package/src/composables/useComponentTree.ts +38 -0
  73. package/src/config/ace.config.ts +22 -0
  74. package/src/domain/EditorDomain.ts +265 -0
  75. package/src/editor.ts +645 -0
  76. package/src/editor.vue +380 -0
  77. package/src/formily/ActivityBar/index.ts +23 -0
  78. package/src/formily/ActivityBar/index.vue +121 -0
  79. package/src/formily/ComponentTree/index.ts +22 -0
  80. package/src/formily/DataBinding/index.ts +22 -0
  81. package/src/formily/DataBinding/index.vue +285 -0
  82. package/src/formily/DataSource/index.ts +22 -0
  83. package/src/formily/MenuTable/PageList.vue +132 -0
  84. package/src/formily/MenuTable/index.ts +19 -0
  85. package/src/formily/MenuTable/index.vue +218 -0
  86. package/src/formily/MenuTable/useMenuTable.ts +147 -0
  87. package/src/formily/NavTree/index.ts +20 -0
  88. package/src/formily/SidebarPane/index.ts +23 -0
  89. package/src/formily/SidebarPane/index.vue +174 -0
  90. package/src/formily/index.ts +12 -0
  91. package/src/index.ts +29 -0
  92. package/src/typings/vue-shim.d.ts +13 -0
  93. package/src/utils/base64.ts +5 -0
  94. package/src/utils/index.ts +57 -0
  95. package/test/case.ts +67 -0
  96. package/test/utils/index.test.ts +27 -0
  97. package/tsconfig.json +8 -0
@@ -0,0 +1,345 @@
1
+ <template>
2
+ <div class="nav-tree">
3
+ <div class="nav-tree__added">
4
+ <el-tree
5
+ ref="tree"
6
+ :allow-drop="allowDrop"
7
+ :allow-drag="allowDrag"
8
+ :data="navTree"
9
+ :expand-on-click-node="false"
10
+ default-expand-all
11
+ :draggable="!readOnly"
12
+ node-key="navKey"
13
+ :props="{ class: customNodeClass }"
14
+ :current-node-key="activeKey"
15
+ @node-drop="handleNodeDrop"
16
+ @node-click="handleNodeClick"
17
+ >
18
+ <template #default="{ node, data }">
19
+ <div
20
+ :key="data.navKey"
21
+ :ref="el => treeItems.set(data.navKey, el)"
22
+ :style="{
23
+ 'pointer-events': readOnly ? 'none' : 'auto',
24
+ }"
25
+ v-contextmenu:contextmenu
26
+ class="tree-item"
27
+ @contextmenu="openMenu(data)"
28
+ @mouseenter="data.isHover = data.key.action !== 'slot'"
29
+ @mouseleave="data.isHover = false"
30
+ >
31
+ <img v-if="data.icon" class="tree-icon" :src="data.icon" />
32
+ <template v-if="data.key?.path !== renameKey?.path">
33
+ <span>
34
+ {{ data.title }}
35
+ <span v-if="data?.componentId">
36
+ id: {{ data?.componentId }}
37
+ </span>
38
+ </span>
39
+ <el-icon
40
+ v-if="data.isHover && data.rightIcon"
41
+ class="delete-icon"
42
+ :size="16"
43
+ color="#c13c2d"
44
+ @click="onClickIcon($event, data.rightIcon.key)"
45
+ >
46
+ <component
47
+ :is="IconMap[data.rightIcon.icon]"
48
+ v-if="data.rightIcon.icon"
49
+ />
50
+ </el-icon>
51
+ </template>
52
+ <el-input
53
+ v-else
54
+ ref="input"
55
+ v-model="data.title"
56
+ @blur="handleHideInput"
57
+ @change="onSubmitEdit(data)"
58
+ clearable
59
+ />
60
+ </div>
61
+ </template>
62
+ </el-tree>
63
+ </div>
64
+ <Contextmenu ref="contextmenu">
65
+ <!-- 从模拟器拿到菜单数据 -->
66
+ <ContextmenuItem
67
+ v-for="menu in menuList"
68
+ :key="menu.key && menu.key.action"
69
+ @click="handleMenuClick(menu.key)"
70
+ :style="{ color: menu.color }"
71
+ :disabled="readOnly"
72
+ >
73
+ {{ menu.title }}
74
+ </ContextmenuItem>
75
+ </Contextmenu>
76
+ </div>
77
+ </template>
78
+
79
+ <script lang="ts" setup>
80
+ import { ref, nextTick, computed, ComputedRef, watch } from 'vue';
81
+ import type { ElTree, ElInput } from '@jt/jtui';
82
+ import * as IconMap from '@jt/icons-vue';
83
+ import type Node from '@jt/jtui/es/components/tree/src/model/node';
84
+ import type { DropType } from '@jt/jtui/es/components/tree/src/tree.type';
85
+ import {
86
+ directive as vContextmenu,
87
+ Contextmenu,
88
+ ContextmenuItem,
89
+ } from 'v-contextmenu';
90
+ import 'v-contextmenu/dist/themes/default.css';
91
+ import {
92
+ Menu,
93
+ MenuData,
94
+ NavTreeData,
95
+ LowCodeKey,
96
+ ComponentType,
97
+ } from '@jt/low-code-core';
98
+
99
+ interface NavTree extends NavTreeData {
100
+ navKey: string;
101
+ isHover: boolean;
102
+ }
103
+ // 这里用JSON不会缓解删动的情况,因为激活态需要重新渲染
104
+ const props = withDefaults(
105
+ defineProps<{
106
+ navTreeData?: {
107
+ key: LowCodeKey;
108
+ value: NavTreeData[];
109
+ };
110
+ menuData?: MenuData;
111
+ readOnly?: boolean;
112
+ }>(),
113
+ {
114
+ navTreeData: () => null,
115
+ menuData: () => [],
116
+ readOnly: false,
117
+ }
118
+ );
119
+
120
+ const emits = defineEmits([
121
+ 'select',
122
+ 'splice',
123
+ 'menuOpen',
124
+ 'menuClick',
125
+ 'submitEdit',
126
+ 'iconClick',
127
+ ]);
128
+
129
+ // 当前激活的导航树的key
130
+ const activeKey = ref('');
131
+ // 当前正在编辑的导航树的key
132
+ const renameKey = ref();
133
+ // 是否由点击导航树激活
134
+ let activateByClick = false;
135
+
136
+ const input = ref<InstanceType<typeof ElInput>>();
137
+ const tree = ref<InstanceType<typeof ElTree>>();
138
+ const treeItems = ref(new Map());
139
+
140
+ const menuList = computed(() =>
141
+ props.menuData.length ? props.menuData : []
142
+ ) as ComputedRef<Menu[]>;
143
+
144
+ const navTree = computed(() => {
145
+ return props.navTreeData ? expandNavTree(props.navTreeData.value) : [];
146
+ });
147
+
148
+ watch(activeKey, value => {
149
+ if (activateByClick) {
150
+ activateByClick = false;
151
+ return;
152
+ }
153
+ treeItems.value.get(value)?.scrollIntoView?.();
154
+ });
155
+
156
+ /* 自定义类名函数 */
157
+ const customNodeClass = (data: NavTree, node: Node) => {
158
+ if (data.activity) {
159
+ return 'activity';
160
+ }
161
+ return null;
162
+ };
163
+
164
+ /* 扩展导航树 扩展属性 isHover: 是否悬浮 navKey: 字符串键值 */
165
+ const expandNavTree = (tree: NavTreeData[]): NavTree[] =>
166
+ tree.map(o => {
167
+ const item = { ...o };
168
+ const navKey = item.key.module + item.key.action + item.key.path;
169
+ if (item.activity) {
170
+ activeKey.value = navKey;
171
+ }
172
+
173
+ if (item.children && item.children.length) {
174
+ item.children = expandNavTree(item.children);
175
+ }
176
+ return { ...item, navKey, isHover: false };
177
+ });
178
+
179
+ const allowDrag = (draggingNode: Node) => {
180
+ const {data} = draggingNode;
181
+ // 没有children才能拖拽
182
+ const draggingNodeChild = data?.children;
183
+ if (
184
+ draggingNodeChild &&
185
+ (draggingNodeChild?.length || Object.keys(draggingNodeChild).length)
186
+ )
187
+ return false;
188
+
189
+ return !data.immobile;
190
+ };
191
+ const allowDrop = (draggingNode: Node, dropNode: Node, type: DropType) => {
192
+ console.log('allowDrop', draggingNode, dropNode, type, draggingNode.level, dropNode.level, dropNode.data.type);
193
+ // 页面内的组件不允许拖到页面外
194
+ const ifOutside = draggingNode.level !== 1 && dropNode.level === 1;
195
+ // 同级的组件不允许拖到对方里面 如果dropNode是容器 则可以拖动
196
+ const ifInner =
197
+ draggingNode.level === dropNode.level &&
198
+ type === 'inner' &&
199
+ dropNode.data.type !== ComponentType.Container;
200
+ if(type !== 'inner' && dropNode.data.immobile){
201
+ // 不允许拖到与不可移动组件同级的情况(Tab内的容器间)
202
+ return false;
203
+ }
204
+ return !(ifOutside || ifInner);
205
+ };
206
+
207
+ /* 回车或失去焦点时提交修改 */
208
+ const onSubmitEdit = (data: NavTree) => {
209
+ emits('submitEdit', renameKey.value, { title: data.title });
210
+ renameKey.value = null;
211
+ };
212
+
213
+ const handleHideInput = () => {
214
+ renameKey.value = null;
215
+ };
216
+
217
+ /* 点击树节点 */
218
+ const handleNodeClick = (data: NavTree) => {
219
+ if(data.key !== renameKey.value){
220
+ handleHideInput()
221
+ }
222
+ activateByClick = true;
223
+ emits('select', data.key);
224
+ };
225
+ /* 打开右键菜单 */
226
+ const openMenu = (data: NavTree) => {
227
+ emits('menuOpen', data.key);
228
+ };
229
+ /* 点击右键菜单 */
230
+ const handleMenuClick = (key: LowCodeKey) => {
231
+ if (key.action === 'setPathData') {
232
+ renameKey.value = key;
233
+ nextTick(() => {
234
+ input.value.focus();
235
+ });
236
+ } else {
237
+ emits('menuClick', key);
238
+ }
239
+ };
240
+ /* 点击悬浮在树节点上显示的删除图标 */
241
+ const onClickIcon = (e, key) => {
242
+ e.stopPropagation();
243
+ emits('iconClick', key);
244
+ };
245
+
246
+ /* 拖动导航树 */
247
+ const handleNodeDrop = (
248
+ draggingNode: Node,
249
+ dropNode: Node,
250
+ dropType: DropType,
251
+ ev: DragEvent
252
+ ) => {
253
+ emits('splice', {
254
+ draggingNode: draggingNode?.data,
255
+ dropNode: dropNode.data,
256
+ dropType,
257
+ });
258
+ };
259
+
260
+
261
+ defineExpose({
262
+ handleMenuClick,
263
+ });
264
+ </script>
265
+
266
+ <style lang="scss" scoped>
267
+ .nav-tree {
268
+ flex: 0 0 30%;
269
+ padding: 16px 17px;
270
+ overflow: auto;
271
+ // border-bottom: 1px solid rgba(0, 0, 0, 0.1);
272
+ color: #020202;
273
+ height: 100%;
274
+
275
+ .tree-item {
276
+ display: flex;
277
+ flex: 1;
278
+ align-items: center;
279
+ position: relative;
280
+ span {
281
+ margin-left: 3px;
282
+ font-size: 14px;
283
+ line-height: 24px;
284
+ color: #020202;
285
+ }
286
+ .tree-icon {
287
+ width: 20px;
288
+ height: 18px;
289
+ object-fit: contain;
290
+ }
291
+ .el-input {
292
+ height: 26px;
293
+ width: fit-content;
294
+ }
295
+ .delete-icon {
296
+ position: absolute;
297
+ right: 0;
298
+ transition: all 0.2s ease;
299
+ }
300
+ .delete-icon:hover {
301
+ font-size: 18px !important;
302
+ }
303
+ }
304
+ }
305
+ </style>
306
+ <style lang="scss">
307
+ .nav-tree {
308
+ .el-tree-node.activity > .el-tree-node__content {
309
+ background-color: rgba(245, 219, 193, 0.5);
310
+ }
311
+ .el-tree-node {
312
+ padding: 8px 0;
313
+ .el-tree-node__content {
314
+ &:hover,
315
+ &:active {
316
+ background-color: rgba(245, 219, 193, 0.5);
317
+ }
318
+ .el-tree-node__label {
319
+ flex: 1;
320
+ }
321
+ .tree-item span {
322
+ font-weight: 500;
323
+ font-size: 16px;
324
+ line-height: 24px;
325
+ }
326
+ }
327
+ .el-tree-node__children {
328
+ .el-tree-node:nth-child(1) {
329
+ margin-top: 8px;
330
+ }
331
+ .tree-item span {
332
+ font-weight: 400;
333
+ font-size: 14px;
334
+ }
335
+ }
336
+ }
337
+ .tree-item .el-input__wrapper {
338
+ box-shadow: 0 0 0 1px #bf935f inset;
339
+ padding: 4px 5px 4px 4px;
340
+ .el-input__inner {
341
+ color: #50381f;
342
+ }
343
+ }
344
+ }
345
+ </style>
@@ -0,0 +1,150 @@
1
+ <template>
2
+ <div class="schema-config">
3
+ <div>
4
+ <!-- <p class="title">{{ title }}</p> -->
5
+ <template v-if="showForm">
6
+ <VueForm
7
+ :key="key"
8
+ :schema="config"
9
+ :modelValue="data"
10
+ :readOnly="readOnly"
11
+ @fileUpload="handleFileUpload"
12
+ @on-change="onChange($event, schemaData?.key)"
13
+ ref="vueForm"
14
+ @openCloud="onOpenCloud"
15
+ @getData="onGetData"
16
+ group
17
+ @preloadCloud="onPreloadCloud"
18
+ @initValidateRule="onInitValidateRule"
19
+ />
20
+ </template>
21
+ <template v-else>
22
+ <div class="notice">请在中间画布中选中节点</div>
23
+ </template>
24
+ </div>
25
+ </div>
26
+ </template>
27
+
28
+ <script lang="ts" setup>
29
+ // @ts-nocheck
30
+ import { VueForm } from '@jt/formily';
31
+
32
+ import {
33
+ ref,
34
+ computed,
35
+ withDefaults,
36
+ defineProps,
37
+ defineEmits,
38
+ watch,
39
+ nextTick,
40
+ } from 'vue';
41
+ import { SchemaData } from '@jt/low-code-core';
42
+ import { cloneDeep, isEqual } from '@jt/ark-util';
43
+
44
+ const props = withDefaults(
45
+ defineProps<{
46
+ schemaData: SchemaData;
47
+ readOnly?: boolean;
48
+ }>(),
49
+ {
50
+ schemaData: () => ({} as SchemaData),
51
+ readOnly: false,
52
+ }
53
+ );
54
+ const emits = defineEmits([
55
+ 'change',
56
+ 'upload',
57
+ 'getData',
58
+ 'openCloud',
59
+ 'preloadCloud',
60
+ 'initValidateRule',
61
+ ]);
62
+ const vueForm = ref();
63
+ const config = ref({});
64
+ const data = computed(() => {
65
+ return props.schemaData?.data ?? {};
66
+ });
67
+ // key 组件 + path
68
+ const key = computed(() => {
69
+ return props.schemaData?.componentId;
70
+ });
71
+ // 显示表单
72
+ const showForm = ref(false);
73
+ // 标题
74
+ const title = computed(() => props.schemaData?.title ?? '');
75
+ // 控制schema的更新
76
+ watch(
77
+ () => props.schemaData?.schema,
78
+ (newVal, oldVal) => {
79
+ if (!isEqual(newVal, oldVal)) {
80
+ showForm.value = false;
81
+ nextTick(() => {
82
+ config.value = cloneDeep(newVal);
83
+ showForm.value = true;
84
+ });
85
+ }
86
+ }
87
+ );
88
+
89
+ // 文件上传
90
+ const handleFileUpload = (file, done, config) => {
91
+ emits('upload', file, done, config);
92
+ };
93
+ /**
94
+ * 云组件预加载
95
+ * @param url
96
+ */
97
+ const onPreloadCloud = url => {
98
+ emits('preloadCloud', url);
99
+ };
100
+
101
+ const onInitValidateRule = rule => {
102
+ emits('initValidateRule', rule);
103
+ };
104
+ /**
105
+ * 打开云组件
106
+ * @param data
107
+ * @param done
108
+ */
109
+ const onOpenCloud = (data, done) => {
110
+ emits('openCloud', data, done);
111
+ };
112
+ /**
113
+ * 表单change
114
+ * @param value
115
+ * @param key
116
+ */
117
+ const onChange = (value, key) => {
118
+ emits('change', { key, value });
119
+ };
120
+ /**
121
+ * 远程数据获取
122
+ * @param cmdname
123
+ * @param done
124
+ * @param cmdParams
125
+ */
126
+ const onGetData = (cmdname: string, done, cmdParams) => {
127
+ emits('getData', cmdname, done, cmdParams);
128
+ };
129
+ </script>
130
+
131
+ <style lang="scss" scoped>
132
+ .schema-config {
133
+ .title {
134
+ font-family: 'Noto Sans SC';
135
+ font-style: normal;
136
+ font-weight: 600;
137
+ font-size: 16px;
138
+ line-height: 24px;
139
+ /* identical to box height, or 150% */
140
+ color: #020202;
141
+ margin-bottom: 16px;
142
+ }
143
+ .notice {
144
+ text-align: center;
145
+ font-size: 14px;
146
+ color: rgba(0, 0, 0, 0.6);
147
+ padding: 50px 15px 0;
148
+ }
149
+ }
150
+ </style>
@@ -0,0 +1,199 @@
1
+ <template>
2
+ <div>
3
+ <div
4
+ :ref="el => sidePane.set('float', el)"
5
+ :class="['float-pane', currentType === 'float' ? 'visible' : '']"
6
+ >
7
+ <div class="titled-panel">
8
+ <template v-if="showHeader">
9
+ <button class="icon-float" @click.stop="currentType = 'fixed'">
10
+ <svg
11
+ fill="currentColor"
12
+ preserveAspectRatio="xMidYMid meet"
13
+ width="16"
14
+ height="16"
15
+ viewBox="0 0 1024 1024"
16
+ >
17
+ <path
18
+ d="M160.256 816.64C116.224 872.448 102.4 921.6 102.4 921.6s49.152-13.824 104.96-57.856c22.016-17.408 128-112.64 200.704-174.08l-73.728-73.728c-61.44 72.704-157.184 178.688-174.08 200.704zM648.704 209.408L442.368 355.328l226.304 226.304 145.92-206.336 15.872 15.872c20.992 20.992 54.784 20.992 75.776 0s20.992-54.784 0-75.776l-197.12-197.12c-20.992-20.992-54.784-20.992-75.776 0-20.992 20.992-20.992 54.784 0 75.776l15.36 15.36zM247.808 334.848c-9.728 2.048-18.944 6.656-26.624 14.336-20.992 20.992-20.992 54.784 0 75.776l377.856 377.856c20.992 20.992 54.784 20.992 75.776 0 7.68-7.68 12.288-16.896 14.336-26.624L247.808 334.848z"
19
+ ></path>
20
+ <path
21
+ d="M840.704 879.104c-9.728 0-19.456-3.584-27.136-11.264L155.648 210.432c-14.848-14.848-14.848-39.424 0-54.272 14.848-14.848 39.424-14.848 54.272 0L867.84 814.08c14.848 14.848 14.848 39.424 0 54.272-7.168 7.168-16.896 10.752-27.136 10.752z"
22
+ ></path>
23
+ </svg>
24
+ </button>
25
+ <button class="icon-close" @click="emits('clearActiveBar')">
26
+ <el-icon :size="16"><Close /></el-icon>
27
+ </button>
28
+ <div class="panel-title">
29
+ <span>{{ name }}</span>
30
+ </div>
31
+ </template>
32
+ <div class="panel-body" :style="{ top: showHeader ? '48px' : 0 }">
33
+ <slot></slot>
34
+ </div>
35
+ </div>
36
+ </div>
37
+ <div
38
+ :ref="el => sidePane.set('fixed', el)"
39
+ :class="['fixed-pane', currentType === 'fixed' ? 'visible' : '']"
40
+ >
41
+ <div class="titled-panel">
42
+ <template v-if="showHeader">
43
+ <button class="icon-fixed" @click.stop="currentType = 'float'">
44
+ <svg
45
+ fill="currentColor"
46
+ preserveAspectRatio="xMidYMid meet"
47
+ width="16"
48
+ height="16"
49
+ viewBox="0 0 1024 1024"
50
+ >
51
+ <path
52
+ d="M750.848 28.928l245.248 242.944a66.048 66.048 0 1 1-93.184 93.184l-25.6-19.456-249.6 353.792 78.336 78.336a66.048 66.048 0 0 1-93.184 92.672l-460.8-464.64a66.048 66.048 0 0 1 93.184-93.184l76.8 78.336 354.048-249.856-18.176-18.944a66.048 66.048 0 1 1 93.184-93.184zM380.672 732.416l-91.904-90.88c-74.24 89.6-191.488 219.904-212.736 247.04a419.84 419.84 0 0 0-70.656 128 419.84 419.84 0 0 0 128-70.144c27.136-21.248 157.44-138.496 246.528-214.016z"
53
+ ></path>
54
+ </svg>
55
+ </button>
56
+ <button class="icon-close" @click="emits('clearActiveBar')">
57
+ <el-icon :size="16"><Close /></el-icon>
58
+ </button>
59
+ <div class="panel-title">
60
+ <span>{{ name }}</span>
61
+ </div>
62
+ </template>
63
+ <div class="panel-body" :style="{ top: showHeader ? '48px' : 0 }">
64
+ <slot></slot>
65
+ </div>
66
+ </div>
67
+ </div>
68
+ </div>
69
+ </template>
70
+
71
+ <script lang="ts" setup>
72
+ // @ts-nocheck
73
+ import { Close } from '@jt/icons-vue';
74
+ import { ref, watch, onMounted, defineEmits, withDefaults, defineProps } from 'vue';
75
+ const props = withDefaults(
76
+ defineProps<{
77
+ type: 'float' | 'fixed';
78
+ name: string;
79
+ showHeader: boolean;
80
+ }>(),
81
+ {
82
+ name: '默认标题',
83
+ showHeader: true,
84
+ }
85
+ );
86
+ const emits = defineEmits(['clearActiveBar']);
87
+ const currentType = ref('');
88
+ const sidePane = ref(new Map());
89
+ watch(
90
+ () => props.type,
91
+ val => {
92
+ currentType.value = val;
93
+ },
94
+ { immediate: true }
95
+ );
96
+ // 点击空白处隐藏面板
97
+ document.addEventListener(
98
+ 'click',
99
+ e => {
100
+ const el = sidePane.value.get(currentType.value);
101
+ if (el) {
102
+ const isSelf =
103
+ el.contains(e.target) ||
104
+ !(document.querySelector('#app') as any).contains(e.target);
105
+ if (!isSelf && currentType.value !== 'fixed') {
106
+ emits('clearActiveBar');
107
+ }
108
+ }
109
+ },
110
+ false
111
+ );
112
+ </script>
113
+
114
+ <style lang="scss" scoped>
115
+ .float-pane {
116
+ position: absolute;
117
+ width: 300px;
118
+ top: 0;
119
+ bottom: 0;
120
+ left: 46px;
121
+ z-index: 820;
122
+ border-left: 1px solid rgba(0, 0, 0, 0.1);
123
+ box-shadow: 4px 6px 6px 0 rgba(31, 50, 88, 0.08);
124
+ background-color: #fff;
125
+ display: none;
126
+ }
127
+ .fixed-pane {
128
+ position: relative;
129
+ margin-left: 1px;
130
+ border-left: 1px solid rgba(0, 0, 0, 0.1);
131
+ height: 100%;
132
+ width: 300px;
133
+ flex-shrink: 0;
134
+ background-color: #fff;
135
+ display: none;
136
+ }
137
+ .float-pane .titled-panel,
138
+ .fixed-pane .titled-panel {
139
+ position: relative;
140
+ width: 100%;
141
+ height: 100%;
142
+ .icon-float,
143
+ .icon-fixed {
144
+ position: absolute;
145
+ right: 38px;
146
+ top: 14px;
147
+ height: auto;
148
+ z-index: 2;
149
+ background: transparent;
150
+ border-color: transparent;
151
+ border-radius: 0;
152
+ padding: 0;
153
+ height: 20px;
154
+ font-size: 14px;
155
+ border-width: 0;
156
+ cursor: pointer;
157
+ }
158
+ .icon-close {
159
+ position: absolute;
160
+ right: 16px;
161
+ top: 14px;
162
+ height: auto;
163
+ z-index: 2;
164
+ background: transparent;
165
+ border-color: transparent;
166
+ border-radius: 0;
167
+ padding: 0;
168
+ height: 20px;
169
+ font-size: 14px;
170
+ border-width: 0;
171
+ cursor: pointer;
172
+ display: flex;
173
+ align-items: center;
174
+ justify-content: center;
175
+ }
176
+ .panel-title {
177
+ height: 48px;
178
+ font-size: 16px;
179
+ padding: 0 15px;
180
+ color: #0f1726;
181
+ font-weight: 700;
182
+ display: flex;
183
+ justify-content: flex-start;
184
+ align-items: center;
185
+ }
186
+ .panel-body {
187
+ position: absolute;
188
+ top: 0;
189
+ bottom: 0;
190
+ left: 0;
191
+ right: 0;
192
+ overflow: visible;
193
+ border-top: 1px solid rgba(31, 56, 88, 0.1);
194
+ }
195
+ }
196
+ .visible {
197
+ display: block;
198
+ }
199
+ </style>