20260827230636
This commit is contained in:
1 parent
3824af0f12
commit
35ca496b5f
15 files changed
+2313
-321
No files matched your search
@@ -31,6 +31,10 @@ const props = defineProps({
|
||||
disabled: { type: Boolean, default: false },
|
||||
lazy: { type: Boolean, default: false },
|
||||
draggable: { type: Boolean, default: false },
|
||||
// 外部拖拽源解析器:(dataTransfer, event) => dragKey | null。
|
||||
// 内部无节点拖拽(dragKey 为空)时,据此把树外拖入的源(如字段库)映射为拖拽键,
|
||||
// 使树内节点可作为外部拖拽的落点。返回 null 表示不识别该外部拖拽源。
|
||||
externalDragKey: { type: Function, default: null },
|
||||
// 过滤关键词:非空时高亮命中节点并自动展开其祖先路径
|
||||
filter: { type: String, default: '' },
|
||||
blockNode: { type: Boolean, default: false },
|
||||
@@ -302,6 +306,9 @@ function autoExpandParents(keys) {
|
||||
const dragKey = ref(null)
|
||||
const dropKey = ref(null)
|
||||
const dropPosition = ref(null)
|
||||
// 标记「树内节点发起的拖拽」:外部拖拽(如字段库)在 dragover 阶段会把占位键
|
||||
// 写入 dragKey,不能据此误判为内部拖拽 —— 用独立标记区分子孙循环校验与清理分支。
|
||||
const isInternalDrag = ref(false)
|
||||
|
||||
function isDescendant(parentKey, childKey) {
|
||||
const stack = [...(flat.value.childrenMap.get(parentKey) || [])]
|
||||
@@ -316,40 +323,63 @@ function isDescendant(parentKey, childKey) {
|
||||
function onDragStart(node, e) {
|
||||
const key = getNodeKey(node, fn.value)
|
||||
dragKey.value = key
|
||||
isInternalDrag.value = true
|
||||
if (e.dataTransfer) {
|
||||
e.dataTransfer.effectAllowed = 'move'
|
||||
e.dataTransfer.setData('text/plain', String(key))
|
||||
}
|
||||
emit('dragstart', { node, event: e })
|
||||
}
|
||||
// 解析本次拖拽键:外部源每次重新解析(dragover 阶段 Chrome 读不到 getData(),
|
||||
// 只能返回占位键;若被上阶段的占位值挡住,drop 阶段就拿不到真实字段 id 了);
|
||||
// 无外部源时用内部节点拖拽键。
|
||||
function resolveDragKey(e) {
|
||||
if (!isInternalDrag.value && typeof props.externalDragKey === 'function') {
|
||||
const key = props.externalDragKey(e?.dataTransfer, e)
|
||||
if (key != null) return key
|
||||
}
|
||||
return dragKey.value
|
||||
}
|
||||
function onDragOver(node, e) {
|
||||
e.preventDefault()
|
||||
if (e.dataTransfer) e.dataTransfer.dropEffect = 'move'
|
||||
if (e.dataTransfer) {
|
||||
// 外部源(如字段库)effectAllowed='copy' 时保持 copy,否则内部节点移动用 move
|
||||
e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed === 'copy' ? 'copy' : 'move'
|
||||
}
|
||||
const rect = e.currentTarget.getBoundingClientRect()
|
||||
dropPosition.value = calcDropPosition(e.clientY, rect)
|
||||
dropKey.value = getNodeKey(node, fn.value)
|
||||
// 外部拖拽源:临时写入 dragKey,让 drop 落点高亮/指示线可见
|
||||
const external = resolveDragKey(e)
|
||||
if (external != null && dragKey.value == null) dragKey.value = external
|
||||
emit('dragover', { node, event: e })
|
||||
}
|
||||
function onDragLeave(node, e) {
|
||||
emit('dragleave', { node, event: e })
|
||||
}
|
||||
// 根容器统一兜底:鼠标真正离开整棵树时清理 drop 指示(避免拖出后残留高亮/指示线)
|
||||
// 根容器统一兜底:鼠标真正离开整棵树时清理 drop 指示(避免拖出后残留高亮/指示线);
|
||||
// 外部拖拽未落放即拖离时同步清掉占位 dragKey,防止下次 hover 误判
|
||||
function onTreeDragLeave(e) {
|
||||
const root = e.currentTarget
|
||||
if (root && !root.contains(e.relatedTarget)) {
|
||||
dropKey.value = null
|
||||
dropPosition.value = null
|
||||
if (!isInternalDrag.value) dragKey.value = null
|
||||
}
|
||||
}
|
||||
function onDrop(node, e) {
|
||||
e.preventDefault()
|
||||
const drag = dragKey.value
|
||||
const drag = resolveDragKey(e)
|
||||
const drop = getNodeKey(node, fn.value)
|
||||
const wasInternal = isInternalDrag.value
|
||||
// 先快照落点位置:resetDragVisual 会把 dropPosition 清为 null,
|
||||
// 若在 emit 时才读取,before/inside/after 将全部丢失(永远为 null)
|
||||
const pos = dropPosition.value
|
||||
resetDragVisual()
|
||||
if (drag == null || drag === drop) return
|
||||
// 禁止拖入自身子孙,避免层级循环
|
||||
if (isDescendant(drag, drop)) return
|
||||
emit('drop', { dragKey: drag, dropKey: drop, dropPosition: dropPosition.value, node, event: e })
|
||||
// 禁止拖入自身子孙,避免层级循环(仅树内节点拖拽需要校验)
|
||||
if (wasInternal && isDescendant(drag, drop)) return
|
||||
emit('drop', { dragKey: drag, dropKey: drop, dropPosition: pos, node, event: e })
|
||||
}
|
||||
function onDragEnd(e) {
|
||||
const key = dragKey.value
|
||||
@@ -360,6 +390,7 @@ function resetDragVisual() {
|
||||
dragKey.value = null
|
||||
dropKey.value = null
|
||||
dropPosition.value = null
|
||||
isInternalDrag.value = false
|
||||
}
|
||||
|
||||
/* ---------- 键盘导航(roving tabindex) ---------- */
|
||||
|
||||
Reference in new issue
Block a user