@houaoran/designer 1.0.0

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 (126) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +98 -0
  3. package/dist/components.es.js +11424 -0
  4. package/dist/components.umd.js +878 -0
  5. package/dist/index.es.js +39113 -0
  6. package/dist/index.umd.js +1187 -0
  7. package/package.json +96 -0
  8. package/src/components/DragBox.vue +49 -0
  9. package/src/components/DragTool.vue +235 -0
  10. package/src/components/EventConfig.vue +557 -0
  11. package/src/components/FcDesigner.vue +2569 -0
  12. package/src/components/FcTitle.vue +69 -0
  13. package/src/components/FetchConfig.vue +415 -0
  14. package/src/components/FieldInput.vue +371 -0
  15. package/src/components/FnConfig.vue +315 -0
  16. package/src/components/FnEditor.vue +327 -0
  17. package/src/components/FnInput.vue +103 -0
  18. package/src/components/FormLabel.vue +47 -0
  19. package/src/components/HtmlEditor.vue +125 -0
  20. package/src/components/JsonPreview.vue +146 -0
  21. package/src/components/OptionsTextInput.vue +151 -0
  22. package/src/components/PropsInput.vue +72 -0
  23. package/src/components/Required.vue +75 -0
  24. package/src/components/Row.vue +26 -0
  25. package/src/components/SignaturePad.vue +176 -0
  26. package/src/components/Struct.vue +153 -0
  27. package/src/components/StructEditor.vue +121 -0
  28. package/src/components/StructTree.vue +209 -0
  29. package/src/components/TableOptions.vue +164 -0
  30. package/src/components/TreeOptions.vue +167 -0
  31. package/src/components/TypeSelect.vue +144 -0
  32. package/src/components/Validate.vue +302 -0
  33. package/src/components/ValueInput.vue +89 -0
  34. package/src/components/Warning.vue +46 -0
  35. package/src/components/ai/AiPanel.vue +1122 -0
  36. package/src/components/ai/MarkdownRenderer.vue +548 -0
  37. package/src/components/language/LanguageConfig.vue +174 -0
  38. package/src/components/language/LanguageInput.vue +191 -0
  39. package/src/components/style/BackgroundInput.vue +315 -0
  40. package/src/components/style/BorderInput.vue +242 -0
  41. package/src/components/style/BoxSizeInput.vue +166 -0
  42. package/src/components/style/BoxSpaceInput.vue +269 -0
  43. package/src/components/style/ColorInput.vue +90 -0
  44. package/src/components/style/ConfigItem.vue +118 -0
  45. package/src/components/style/FontInput.vue +197 -0
  46. package/src/components/style/PositionInput.vue +146 -0
  47. package/src/components/style/RadiusInput.vue +164 -0
  48. package/src/components/style/ShadowContent.vue +335 -0
  49. package/src/components/style/ShadowInput.vue +91 -0
  50. package/src/components/style/SizeInput.vue +118 -0
  51. package/src/components/style/StyleConfig.vue +307 -0
  52. package/src/components/table/Table.vue +252 -0
  53. package/src/components/table/TableView.vue +1058 -0
  54. package/src/components/tableForm/TableForm.vue +471 -0
  55. package/src/components/tableForm/TableFormColumnView.vue +103 -0
  56. package/src/components/tableForm/TableFormView.vue +46 -0
  57. package/src/components/tree/FcTree.vue +713 -0
  58. package/src/components/tree/FcTreeNode.vue +216 -0
  59. package/src/config/base/field.js +43 -0
  60. package/src/config/base/form.js +132 -0
  61. package/src/config/base/style.js +26 -0
  62. package/src/config/base/validate.js +15 -0
  63. package/src/config/index.js +70 -0
  64. package/src/config/menu.js +24 -0
  65. package/src/config/rule/alert.js +45 -0
  66. package/src/config/rule/button.js +49 -0
  67. package/src/config/rule/card.js +40 -0
  68. package/src/config/rule/cascader.js +121 -0
  69. package/src/config/rule/checkbox.js +68 -0
  70. package/src/config/rule/col.js +86 -0
  71. package/src/config/rule/collapse.js +30 -0
  72. package/src/config/rule/collapseItem.js +36 -0
  73. package/src/config/rule/color.js +53 -0
  74. package/src/config/rule/date.js +66 -0
  75. package/src/config/rule/dateRange.js +60 -0
  76. package/src/config/rule/divider.js +31 -0
  77. package/src/config/rule/editor.js +31 -0
  78. package/src/config/rule/group.js +86 -0
  79. package/src/config/rule/html.js +43 -0
  80. package/src/config/rule/image.js +32 -0
  81. package/src/config/rule/input.js +62 -0
  82. package/src/config/rule/number.js +49 -0
  83. package/src/config/rule/password.js +52 -0
  84. package/src/config/rule/radio.js +43 -0
  85. package/src/config/rule/rate.js +44 -0
  86. package/src/config/rule/row.js +46 -0
  87. package/src/config/rule/select.js +70 -0
  88. package/src/config/rule/signaturePad.js +59 -0
  89. package/src/config/rule/slider.js +53 -0
  90. package/src/config/rule/space.js +44 -0
  91. package/src/config/rule/subForm.js +47 -0
  92. package/src/config/rule/switch.js +46 -0
  93. package/src/config/rule/tabPane.js +29 -0
  94. package/src/config/rule/table.js +37 -0
  95. package/src/config/rule/tableForm.js +115 -0
  96. package/src/config/rule/tableFormColumn.js +55 -0
  97. package/src/config/rule/tabs.js +38 -0
  98. package/src/config/rule/tag.js +69 -0
  99. package/src/config/rule/text.js +41 -0
  100. package/src/config/rule/textarea.js +63 -0
  101. package/src/config/rule/time.js +58 -0
  102. package/src/config/rule/timeRange.js +49 -0
  103. package/src/config/rule/title.js +37 -0
  104. package/src/config/rule/transfer.js +59 -0
  105. package/src/config/rule/tree.js +70 -0
  106. package/src/config/rule/treeSelect.js +77 -0
  107. package/src/config/rule/upload.js +107 -0
  108. package/src/form/index.js +19 -0
  109. package/src/index.js +173 -0
  110. package/src/locale/en.js +981 -0
  111. package/src/locale/zh-cn.js +983 -0
  112. package/src/style/fonts/fc-icons.woff +0 -0
  113. package/src/style/icon.css +1052 -0
  114. package/src/style/index.css +836 -0
  115. package/src/utils/form.js +9 -0
  116. package/src/utils/highlight/highlight.min.js +307 -0
  117. package/src/utils/highlight/javascript.min.js +80 -0
  118. package/src/utils/highlight/style.css +1 -0
  119. package/src/utils/highlight/xml.min.js +29 -0
  120. package/src/utils/hintStubs.js +120 -0
  121. package/src/utils/index.js +544 -0
  122. package/src/utils/jsonDiff.js +173 -0
  123. package/src/utils/locale.js +23 -0
  124. package/src/utils/message.js +19 -0
  125. package/src/utils/template.js +105 -0
  126. package/types/index.d.ts +575 -0
@@ -0,0 +1,713 @@
1
+ <template>
2
+ <div class="_fd-tree-block">
3
+ <div class="_fd-tree__search">
4
+ <el-input class="_fc-search-input" v-model="rawKeyword" :placeholder="placeholder" clearable @input="onInput">
5
+ <template #suffix>
6
+ <i class="fc-icon icon-search"></i>
7
+ </template>
8
+ </el-input>
9
+ </div>
10
+
11
+ <div class="_fd-tree">
12
+ <ul class="_fd-tree__list" role="tree" ref="rootList" data-parent-key="__root__">
13
+ <FcTreeNode
14
+ v-for="item in viewTree"
15
+ :key="getKey(item)"
16
+ :item="item"
17
+ :level="1"
18
+ :indent="indent"
19
+ :expanded-keys="expandedKeys"
20
+ :get-key="getKey"
21
+ :get-children="getChildren"
22
+ :drag-state="dragState"
23
+ @toggle="toggle"
24
+ @select="select"
25
+ >
26
+ <template #default="{ node, data }">
27
+ <slot :node="node" :data="data" />
28
+ </template>
29
+ </FcTreeNode>
30
+ </ul>
31
+ <div v-if="showRootDropLine" class="_fd-tree__drop-line"></div>
32
+ </div>
33
+ </div>
34
+ </template>
35
+
36
+ <script>
37
+ import Sortable from 'sortablejs';
38
+ import FcTreeNode from './FcTreeNode.vue';
39
+
40
+ function defaultGetKey(item) {
41
+ return item?.id ?? item?.key ?? item;
42
+ }
43
+
44
+ function defaultGetChildren(item) {
45
+ return item?.children;
46
+ }
47
+
48
+ function debounce(fn, wait = 300) {
49
+ let t = null;
50
+ return function (...args) {
51
+ if (t) clearTimeout(t);
52
+ t = setTimeout(() => fn.apply(this, args), wait);
53
+ };
54
+ }
55
+
56
+ function runOptional(fn, arg) {
57
+ if (!fn) return true;
58
+ try {
59
+ return fn(arg) !== false;
60
+ } catch (e) {
61
+ return true;
62
+ }
63
+ }
64
+
65
+ export default {
66
+ name: 'FcTree',
67
+ components: { FcTreeNode },
68
+ props: {
69
+ data: { type: Array, default: () => [] },
70
+ indent: { type: Number, default: 10 },
71
+ defaultExpandAll: { type: Boolean, default: true },
72
+ placeholder: { type: String, default: '' },
73
+ filterNodeMethod: { type: Function, default: null },
74
+ getKey: { type: Function, default: defaultGetKey },
75
+ getChildren: { type: Function, default: defaultGetChildren },
76
+
77
+ draggable: { type: Boolean, default: false },
78
+ canInnerDrop: { type: Function, default: null },
79
+ canDrop: { type: Function, default: null },
80
+ beforeDrag: { type: Function, default: null },
81
+ beforeDrop: { type: Function, default: null },
82
+ ensureChildren: { type: Boolean, default: false },
83
+ },
84
+ emits: ['select', 'toggle', 'drop'],
85
+ data() {
86
+ return {
87
+ rawKeyword: '',
88
+ debouncedKeyword: '',
89
+ expandedKeys: new Set(),
90
+ dragState: this.createEmptyDragState(),
91
+ debounceKeywordFn: null,
92
+ sortableInstances: [],
93
+ debounceInitSortableFn: null,
94
+ expandBootstrapped: false,
95
+ activeSortable: null,
96
+ keydownEscHandler: null,
97
+ dragPointerMoveHandler: null,
98
+ pointerMoveRaf: null,
99
+ pendingDragMoveEvent: null,
100
+ cancelingDrag: false,
101
+ sortableGroupName: '',
102
+ };
103
+ },
104
+ computed: {
105
+ viewTree() {
106
+ const val = this.debouncedKeyword;
107
+ const nodes = this.data || [];
108
+ return this.filterTree(nodes, val);
109
+ },
110
+ showRootDropLine() {
111
+ return (
112
+ this.dragState.active &&
113
+ this.dragState.allowed !== false &&
114
+ !this.dragState.hoverKey &&
115
+ this.dragState.resolvedParentKey === '__root__'
116
+ );
117
+ },
118
+ },
119
+ watch: {
120
+ data: {
121
+ immediate: true,
122
+ deep: false,
123
+ handler(nodes) {
124
+ nodes = nodes || [];
125
+ this.pruneExpandedKeys(nodes);
126
+
127
+ const hasNodes = nodes.length > 0;
128
+ if (!this.expandBootstrapped && hasNodes) {
129
+ if (!this.debouncedKeyword) {
130
+ if (this.defaultExpandAll) this.applyExpandAll(nodes);
131
+ } else {
132
+ this.$nextTick(() => this.applyExpandAll(this.viewTree));
133
+ }
134
+ this.expandBootstrapped = true;
135
+ } else if (this.expandBootstrapped && this.debouncedKeyword) {
136
+ this.$nextTick(() => this.applyExpandAll(this.viewTree));
137
+ }
138
+
139
+ this.queueInitSortable();
140
+ },
141
+ },
142
+ draggable() {
143
+ this.queueInitSortable();
144
+ },
145
+ debouncedKeyword() {
146
+ this.queueInitSortable();
147
+ },
148
+ },
149
+ created() {
150
+ this.sortableGroupName = `fc-tree-${this._uid || Math.random().toString(36).slice(2)}`;
151
+ this.debounceKeywordFn = debounce(v => {
152
+ this.debouncedKeyword = (v || '').trim();
153
+ this.syncExpandOnKeyword(this.debouncedKeyword);
154
+ }, 500);
155
+
156
+ this.debounceInitSortableFn = debounce(() => {
157
+ this.initSortable();
158
+ }, 60);
159
+ },
160
+ mounted() {
161
+ this.keydownEscHandler = e => {
162
+ const key = e?.key || '';
163
+ if (key !== 'Escape' && key !== 'Esc') return;
164
+ if (!this.dragState?.active) return;
165
+ e?.preventDefault?.();
166
+ e?.stopPropagation?.();
167
+ this.cancelDrag(e);
168
+ };
169
+ window.addEventListener('keydown', this.keydownEscHandler);
170
+ this.queueInitSortable();
171
+ },
172
+ beforeUnmount() {
173
+ if (this.keydownEscHandler) window.removeEventListener('keydown', this.keydownEscHandler);
174
+ this.keydownEscHandler = null;
175
+ this.destroySortable();
176
+ },
177
+ methods: {
178
+ createEmptyDragState() {
179
+ return {
180
+ active: false,
181
+ draggingKey: '',
182
+ sourceParentKey: '__root__',
183
+ sourceIndex: -1,
184
+ hoverKey: '',
185
+ dropItem: null,
186
+ dropType: 'none',
187
+ resolvedParentKey: '',
188
+ resolvedIndex: -1,
189
+ allowed: false,
190
+ };
191
+ },
192
+ cleanupDragState({ reinitSortable = true } = {}) {
193
+ this.dragState = this.createEmptyDragState();
194
+ this.activeSortable = null;
195
+ this.detachPointerTracking();
196
+ if (reinitSortable) this.$nextTick(() => this.queueInitSortable());
197
+ },
198
+ attachPointerTracking() {
199
+ if (this.dragPointerMoveHandler) return;
200
+ this.pointerMoveRaf = null;
201
+ this.pendingDragMoveEvent = null;
202
+ const handler = e => {
203
+ if (!this.dragState.active) return;
204
+ this.pendingDragMoveEvent = e;
205
+ if (this.pointerMoveRaf != null) return;
206
+ this.pointerMoveRaf = requestAnimationFrame(() => {
207
+ this.pointerMoveRaf = null;
208
+ const ev = this.pendingDragMoveEvent;
209
+ this.pendingDragMoveEvent = null;
210
+ if (!ev || !this.dragState.active) return;
211
+ this.handleDragMove({ originalEvent: ev });
212
+ });
213
+ };
214
+ this.dragPointerMoveHandler = handler;
215
+ if (typeof window !== 'undefined' && window.PointerEvent) {
216
+ document.addEventListener('pointermove', handler, true);
217
+ } else {
218
+ document.addEventListener('mousemove', handler, true);
219
+ document.addEventListener('touchmove', handler, { capture: true, passive: true });
220
+ }
221
+ },
222
+ detachPointerTracking() {
223
+ const handler = this.dragPointerMoveHandler;
224
+ if (handler) {
225
+ if (typeof window !== 'undefined' && window.PointerEvent) {
226
+ document.removeEventListener('pointermove', handler, true);
227
+ } else {
228
+ document.removeEventListener('mousemove', handler, true);
229
+ document.removeEventListener('touchmove', handler, { capture: true, passive: true });
230
+ }
231
+ this.dragPointerMoveHandler = null;
232
+ }
233
+ if (this.pointerMoveRaf != null) {
234
+ cancelAnimationFrame(this.pointerMoveRaf);
235
+ this.pointerMoveRaf = null;
236
+ }
237
+ this.pendingDragMoveEvent = null;
238
+ },
239
+ filter(val) {
240
+ this.rawKeyword = val || '';
241
+ this.debounceKeywordFn?.(this.rawKeyword);
242
+ },
243
+ onInput() {
244
+ this.debounceKeywordFn?.(this.rawKeyword);
245
+ },
246
+ clear() {
247
+ this.rawKeyword = '';
248
+ this.debouncedKeyword = '';
249
+ this.syncExpandOnKeyword('');
250
+ },
251
+ toggle(item) {
252
+ const k = this.getKey(item);
253
+ const next = new Set(this.expandedKeys);
254
+ if (next.has(k)) next.delete(k);
255
+ else next.add(k);
256
+ this.expandedKeys = next;
257
+ this.$emit('toggle', item);
258
+ this.queueInitSortable();
259
+ },
260
+ select(item) {
261
+ this.$emit('select', item);
262
+ },
263
+ getNodeKey(item) {
264
+ return String(this.getKey(item));
265
+ },
266
+ applyExpandAll(nodes) {
267
+ const keys = new Set();
268
+ const walk = list => {
269
+ (list || []).forEach(n => {
270
+ const children = this.getChildren(n);
271
+ if (Array.isArray(children) && children.length) {
272
+ keys.add(this.getKey(n));
273
+ walk(children);
274
+ }
275
+ });
276
+ };
277
+ walk(nodes);
278
+ this.expandedKeys = keys;
279
+ },
280
+ applyCollapseAll() {
281
+ this.expandedKeys = new Set();
282
+ },
283
+ pruneExpandedKeys(nodes) {
284
+ const alive = new Set();
285
+ const walk = list => {
286
+ (list || []).forEach(n => {
287
+ alive.add(this.getKey(n));
288
+ const children = this.getChildren(n);
289
+ if (Array.isArray(children) && children.length) walk(children);
290
+ });
291
+ };
292
+ walk(nodes || []);
293
+ const prev = this.expandedKeys;
294
+ const next = new Set();
295
+ prev.forEach(k => {
296
+ if (alive.has(k)) next.add(k);
297
+ });
298
+ if (next.size !== prev.size) this.expandedKeys = next;
299
+ },
300
+ syncExpandOnKeyword(val) {
301
+ if (!val) {
302
+ if (this.defaultExpandAll) this.applyExpandAll(this.data);
303
+ else this.applyCollapseAll();
304
+ return;
305
+ }
306
+ this.$nextTick(() => this.applyExpandAll(this.viewTree));
307
+ },
308
+ matchSelf(val, item) {
309
+ if (!val) return true;
310
+ if (this.filterNodeMethod) {
311
+ try {
312
+ return !!this.filterNodeMethod(val, item);
313
+ } catch (e) {
314
+ return true;
315
+ }
316
+ }
317
+ try {
318
+ return JSON.stringify(item).indexOf(val) > -1;
319
+ } catch (e) {
320
+ return true;
321
+ }
322
+ },
323
+ filterTree(list, val) {
324
+ if (!val) return list || [];
325
+ const out = [];
326
+ (list || []).forEach(item => {
327
+ const children = this.getChildren(item);
328
+ const filteredChildren = this.filterTree(Array.isArray(children) ? children : [], val);
329
+ const selfOk = this.matchSelf(val, item);
330
+ if (selfOk || filteredChildren.length) {
331
+ out.push({
332
+ ...item,
333
+ children: filteredChildren,
334
+ });
335
+ }
336
+ });
337
+ return out;
338
+ },
339
+
340
+ queueInitSortable() {
341
+ this.$nextTick(() => this.debounceInitSortableFn?.());
342
+ },
343
+
344
+ destroySortable() {
345
+ this.sortableInstances.forEach(ins => {
346
+ try {
347
+ ins?.destroy?.();
348
+ } catch (e) {}
349
+ });
350
+ this.sortableInstances = [];
351
+ this.cancelingDrag = false;
352
+ this.cleanupDragState({ reinitSortable: false });
353
+ },
354
+
355
+ initSortable() {
356
+ if (this.dragState?.active) return;
357
+ this.destroySortable();
358
+ if (!this.draggable) return;
359
+ if (this.debouncedKeyword) return;
360
+
361
+ this.$nextTick(() => {
362
+ this.$nextTick(() => {
363
+ const root = this.$refs.rootList;
364
+ if (!root) return;
365
+
366
+ const containers = [root, ...Array.from(root.querySelectorAll('ul._fd-tree-node__children'))];
367
+ containers.forEach(el => {
368
+ this.sortableInstances.push(this.createSortable(el));
369
+ });
370
+ });
371
+ });
372
+ },
373
+
374
+ createSortable(el) {
375
+ const self = this;
376
+ return new Sortable(el, {
377
+ animation: 0,
378
+ sort: false,
379
+ forceFallback: true,
380
+ fallbackOnBody: true,
381
+ fallbackTolerance: 0,
382
+ filter: '._fd-tree-node__expand',
383
+ preventOnFilter: true,
384
+ group: {
385
+ name: this.sortableGroupName,
386
+ pull: true,
387
+ put: [this.sortableGroupName],
388
+ },
389
+ onStart(evt) {
390
+ self.handleDragStart(evt, this);
391
+ },
392
+ onMove: () => false,
393
+ onEnd() {
394
+ self.handleDragEnd();
395
+ },
396
+ });
397
+ },
398
+
399
+ getParentKeyByEl(el) {
400
+ const k = el?.dataset?.parentKey;
401
+ return k || '__root__';
402
+ },
403
+ getContainerParentKey(el) {
404
+ return this.getParentKeyByEl(el?.closest?.('[data-parent-key]') || el);
405
+ },
406
+ getClosestNodeEl(target) {
407
+ return target?.closest?.('li._fd-tree-node') || null;
408
+ },
409
+ getClientPoint(evt) {
410
+ const oe = evt?.originalEvent ?? evt;
411
+ const t = oe?.touches?.[0] || oe?.changedTouches?.[0] || oe;
412
+ const x = Number(t?.clientX);
413
+ const y = Number(t?.clientY);
414
+ if (!Number.isFinite(x) || !Number.isFinite(y)) return null;
415
+ return { x, y };
416
+ },
417
+ getItemByKey(key, list = this.data || []) {
418
+ if (!key) return null;
419
+ for (const item of list) {
420
+ if (this.getNodeKey(item) === String(key)) return item;
421
+ const children = this.getChildren(item);
422
+ const found = this.getItemByKey(key, Array.isArray(children) ? children : []);
423
+ if (found) return found;
424
+ }
425
+ return null;
426
+ },
427
+ getListByParentKey(parentKey) {
428
+ if (!parentKey || parentKey === '__root__') return this.data || [];
429
+ const parentNode = this.getItemByKey(parentKey);
430
+ if (!parentNode) return null;
431
+ if (this.ensureChildren && !Array.isArray(parentNode.children)) parentNode.children = [];
432
+ const children = this.getChildren(parentNode);
433
+ return Array.isArray(children) ? children : null;
434
+ },
435
+ getIndexInList(list, item) {
436
+ return (list || []).findIndex(v => this.getNodeKey(v) === this.getNodeKey(item));
437
+ },
438
+ canDropInner(item) {
439
+ if (this.canInnerDrop) {
440
+ return runOptional(this.canInnerDrop, item);
441
+ }
442
+ const children = this.getChildren(item);
443
+ if (Array.isArray(children)) return true;
444
+ return !!this.ensureChildren;
445
+ },
446
+ ensureExpandedForInnerDrop(hoverKey) {
447
+ if (!hoverKey) return;
448
+ const item = this.getItemByKey(hoverKey);
449
+ if (!item || !this.canDropInner(item)) return;
450
+ const k = this.getKey(item);
451
+ if (this.expandedKeys?.has?.(k)) return;
452
+ const next = new Set(this.expandedKeys);
453
+ next.add(k);
454
+ this.expandedKeys = next;
455
+ },
456
+ isDescendantKey(parentKey, childKey) {
457
+ const parentItem = this.getItemByKey(parentKey);
458
+ if (!parentItem) return false;
459
+ const walk = list => {
460
+ for (const item of list || []) {
461
+ if (this.getNodeKey(item) === String(childKey)) return true;
462
+ const children = this.getChildren(item);
463
+ if (walk(Array.isArray(children) ? children : [])) return true;
464
+ }
465
+ return false;
466
+ };
467
+ const children = this.getChildren(parentItem);
468
+ return walk(Array.isArray(children) ? children : []);
469
+ },
470
+ standardizeCanDrop(payload) {
471
+ let toParent = null;
472
+ if (payload.dropType === 'inner') {
473
+ toParent = payload.dropItem || null;
474
+ } else if (payload.resolvedParentKey !== '__root__') {
475
+ toParent = this.getItemByKey(payload.resolvedParentKey) || null;
476
+ }
477
+ return {
478
+ dragItem: payload.dragItem,
479
+ dropItem: payload.dropItem || null,
480
+ dropType: payload.dropType,
481
+ fromParent: payload.fromParent,
482
+ toParent,
483
+ toIndex: payload.resolvedIndex,
484
+ fromKey: payload.sourceParentKey,
485
+ toKey: payload.resolvedParentKey,
486
+ };
487
+ },
488
+ cancelDrag(originalEvent) {
489
+ const inst = this.activeSortable;
490
+ const evt = (() => {
491
+ try {
492
+ return new MouseEvent('mouseup', { bubbles: true, cancelable: true });
493
+ } catch (e) {
494
+ return originalEvent || { cancelable: false, preventDefault() {}, stopPropagation() {} };
495
+ }
496
+ })();
497
+ if (inst && typeof inst._onDrop === 'function') {
498
+ this.cancelingDrag = true;
499
+ inst._onDrop(evt);
500
+ return;
501
+ }
502
+ this.cancelingDrag = false;
503
+ this.cleanupDragState();
504
+ },
505
+ handleDragStart(evt, sortableInst) {
506
+ const dragKey = evt?.item?.dataset?.key;
507
+ const sourceParentKey = this.getContainerParentKey(evt?.from);
508
+ const sourceList = this.getListByParentKey(sourceParentKey) || [];
509
+ const sourceIndex = evt?.oldIndex ?? sourceList.findIndex(item => this.getNodeKey(item) === String(dragKey));
510
+ const dragTreeItem = this.getItemByKey(dragKey);
511
+ if (!runOptional(this.beforeDrag, { dragItem: dragTreeItem, sourceParentKey, sourceIndex })) {
512
+ const inst = sortableInst || (evt?.from && Sortable.get(evt.from));
513
+ if (inst && typeof inst._onDrop === 'function') inst._onDrop(evt?.originalEvent || evt);
514
+ return;
515
+ }
516
+ this.activeSortable = sortableInst || (evt?.from && Sortable.get(evt.from)) || null;
517
+ this.attachPointerTracking();
518
+ this.dragState = {
519
+ active: true,
520
+ draggingKey: String(dragKey || ''),
521
+ sourceParentKey,
522
+ sourceIndex,
523
+ hoverKey: '',
524
+ dropItem: null,
525
+ dropType: 'none',
526
+ resolvedParentKey: sourceParentKey,
527
+ resolvedIndex: sourceIndex,
528
+ allowed: false,
529
+ };
530
+ },
531
+ resolveDropState(evt) {
532
+ const dragKey = this.dragState.draggingKey;
533
+ const dragItem = this.getItemByKey(dragKey);
534
+ if (!dragItem) return this.createEmptyDragState();
535
+
536
+ const pt = this.getClientPoint(evt);
537
+ if (!pt) {
538
+ return {
539
+ ...this.createEmptyDragState(),
540
+ active: true,
541
+ draggingKey: dragKey,
542
+ sourceParentKey: this.dragState.sourceParentKey,
543
+ sourceIndex: this.dragState.sourceIndex,
544
+ allowed: false,
545
+ };
546
+ }
547
+
548
+ const pointerTarget = document.elementFromPoint(pt.x, pt.y);
549
+ const nodeEl = this.getClosestNodeEl(pointerTarget);
550
+ const fromParent = this.dragState.sourceParentKey === '__root__' ? null : this.getItemByKey(this.dragState.sourceParentKey);
551
+
552
+ let nextState = {
553
+ ...this.createEmptyDragState(),
554
+ active: true,
555
+ draggingKey: dragKey,
556
+ sourceParentKey: this.dragState.sourceParentKey,
557
+ sourceIndex: this.dragState.sourceIndex,
558
+ };
559
+
560
+ if (nodeEl) {
561
+ const hoverKey = String(nodeEl.dataset.key || '');
562
+ const hoverItem = this.getItemByKey(hoverKey);
563
+ const contentEl = nodeEl.querySelector('._fd-tree-node__content') || nodeEl;
564
+ const rect = contentEl.getBoundingClientRect();
565
+ const offsetY = pt.y - rect.top;
566
+ const quarter = rect.height * 0.25;
567
+ let dropType = offsetY <= quarter ? 'before' : offsetY >= rect.height - quarter ? 'after' : 'inner';
568
+ let innerPrepend = false;
569
+
570
+ if (dropType === 'inner' && !this.canDropInner(hoverItem)) {
571
+ dropType = offsetY < rect.height / 2 ? 'before' : 'after';
572
+ }
573
+ if (
574
+ dropType === 'after' &&
575
+ hoverItem &&
576
+ (this.getChildren(hoverItem) || []).length > 0 &&
577
+ this.expandedKeys?.has?.(this.getKey(hoverItem)) &&
578
+ this.canDropInner(hoverItem)
579
+ ) {
580
+ dropType = 'inner';
581
+ innerPrepend = true;
582
+ }
583
+
584
+ let resolvedParentKey = '__root__';
585
+ let resolvedIndex = 0;
586
+ if (dropType === 'inner') {
587
+ resolvedParentKey = hoverKey;
588
+ const children = this.getChildren(hoverItem);
589
+ resolvedIndex = innerPrepend ? 0 : Array.isArray(children) ? children.length : 0;
590
+ } else {
591
+ const container = nodeEl.closest('[data-parent-key]');
592
+ resolvedParentKey = this.getParentKeyByEl(container);
593
+ const siblings = this.getListByParentKey(resolvedParentKey) || [];
594
+ const targetIndex = this.getIndexInList(siblings, hoverItem);
595
+ resolvedIndex = targetIndex + (dropType === 'after' ? 1 : 0);
596
+ }
597
+
598
+ nextState.hoverKey = hoverKey;
599
+ nextState.dropItem = hoverItem;
600
+ nextState.dropType = dropType;
601
+ nextState.resolvedParentKey = resolvedParentKey;
602
+ nextState.resolvedIndex = resolvedIndex;
603
+ } else {
604
+ const container = pointerTarget?.closest?.('[data-parent-key]');
605
+ const resolvedParentKey = this.getParentKeyByEl(container);
606
+ const targetList = this.getListByParentKey(resolvedParentKey) || [];
607
+ nextState.hoverKey = '';
608
+ nextState.dropItem = null;
609
+ nextState.dropType = resolvedParentKey === '__root__' ? 'after' : 'inner';
610
+ nextState.resolvedParentKey = resolvedParentKey;
611
+ nextState.resolvedIndex = targetList.length;
612
+ }
613
+
614
+ if (nextState.hoverKey === dragKey && nextState.dropType !== 'inner') {
615
+ nextState.allowed = false;
616
+ return nextState;
617
+ }
618
+
619
+ if (
620
+ (nextState.dropType === 'inner' &&
621
+ (nextState.resolvedParentKey === dragKey || this.isDescendantKey(dragKey, nextState.resolvedParentKey))) ||
622
+ (nextState.dropItem && this.isDescendantKey(dragKey, this.getNodeKey(nextState.dropItem)))
623
+ ) {
624
+ nextState.allowed = false;
625
+ return nextState;
626
+ }
627
+
628
+ const standardPayload = this.standardizeCanDrop({
629
+ dragItem,
630
+ dropItem: nextState.dropItem,
631
+ dropType: nextState.dropType,
632
+ resolvedParentKey: nextState.resolvedParentKey,
633
+ resolvedIndex: nextState.resolvedIndex,
634
+ sourceParentKey: nextState.sourceParentKey,
635
+ fromParent,
636
+ });
637
+ nextState.allowed = runOptional(this.canDrop, standardPayload);
638
+ return nextState;
639
+ },
640
+ handleDragMove(evt) {
641
+ if (!this.dragState.active || !evt?.originalEvent) return;
642
+ const next = this.resolveDropState(evt);
643
+ if (next.dropType === 'inner' && next.hoverKey && next.allowed !== false) {
644
+ this.ensureExpandedForInnerDrop(next.hoverKey);
645
+ }
646
+ const prev = this.dragState;
647
+ if (
648
+ prev.hoverKey === next.hoverKey &&
649
+ prev.dropType === next.dropType &&
650
+ prev.resolvedParentKey === next.resolvedParentKey &&
651
+ prev.resolvedIndex === next.resolvedIndex &&
652
+ prev.allowed === next.allowed
653
+ ) {
654
+ return;
655
+ }
656
+ this.dragState = next;
657
+ },
658
+ emitDropFromState(state) {
659
+ const dragItem = this.getItemByKey(state.draggingKey);
660
+ if (!dragItem) return false;
661
+ const fromParent = state.sourceParentKey === '__root__' ? null : this.getItemByKey(state.sourceParentKey);
662
+
663
+ const dropPayload = this.standardizeCanDrop({
664
+ dragItem,
665
+ dropItem: state.dropItem,
666
+ dropType: state.dropType,
667
+ resolvedParentKey: state.resolvedParentKey,
668
+ resolvedIndex: state.resolvedIndex,
669
+ sourceParentKey: state.sourceParentKey,
670
+ fromParent,
671
+ });
672
+ if (!runOptional(this.beforeDrop, dropPayload)) return false;
673
+
674
+ this.$emit('drop', {
675
+ ...dropPayload,
676
+ fromIndex: state.sourceIndex,
677
+ });
678
+ return true;
679
+ },
680
+ handleDragEnd() {
681
+ if (this.cancelingDrag) {
682
+ this.cancelingDrag = false;
683
+ this.cleanupDragState();
684
+ return;
685
+ }
686
+ const state = { ...this.dragState };
687
+ if (state.active && state.allowed !== false && state.resolvedIndex > -1 && state.dropType !== 'none') {
688
+ this.emitDropFromState(state);
689
+ }
690
+ this.cleanupDragState();
691
+ },
692
+ },
693
+ };
694
+ </script>
695
+
696
+ <style scoped>
697
+ ._fd-tree__search :deep(.el-input__wrapper) {
698
+ margin-bottom: 8px;
699
+ }
700
+
701
+ ._fd-tree__list {
702
+ list-style: none;
703
+ padding: 0;
704
+ margin: 0;
705
+ }
706
+
707
+ ._fd-tree__drop-line {
708
+ height: 2px;
709
+ background: #2e73ff;
710
+ margin-left: 18px;
711
+ border-radius: 1px;
712
+ }
713
+ </style>