<template> <div class="ps-tree component" draggable="true" :class="{ aaa: isDrag }" @dragover.prevent="onDragover2" @dragleave.stop="onDragleave2" @drop.stop="onDrop2"> <template v-for="(item, index) in sortedList" :key="item.key"> <node @onDragstart="onDragstart" @expand="onExpand" @onDragEnd="onDragEnd" @onDrop="onDrop" :data-source-key="dataSourceKey" :data="item" :list="sortedList" :level="level" @click="(item) => clickNode(item)"> <template #default="{ data, deep, dataSourceKey, status }: { data: INiuTreeData, deep: number, dataSourceKey: INiuTreeKey, status: ENiuTreeStatus }"> <slot :data="data" :deep="deep" :dataSourceKey="dataSourceKey" :status="status"></slot> </template> </node> </template> </div> </template> <script lang="ts" setup> import { provide, ref, renderSlot, useSlots, getCurrentInstance } from "vue" import node from './node.vue' import type { INiuTreeData, INiuTreeKey } from './type' import { ENiuTreeStatus } from './type' import { findByKey, findByKeyParent, forEachTree, insertAfterByKey, insertBeforeByKey, isChild, isChildOf, removeByKey, } from './util' import { betterDirectorySort } from "./better-directory-sort" import { computed } from "@vue/reactivity" const sortedList = computed(() => { if (props.sort) { return props.list.sort((a, b) => { return betterDirectorySort( { name: a.title, isDirectory: a.isFolder }, { name: b.title, isDirectory: b.isFolder }, ); }); } return props.list }) const isDrag = ref(false) function onDragover2() { if (!props.sort) return if (!dataSourceKey.value) return if (!isChild(dataSourceKey.value, props.list)) { isDrag.value = true } } function onDragleave2() { if (!props.sort) return isDrag.value = false } async function onDrop2(ev) { if (!props.sort) return if (!dataSourceKey.value) return if (!isDrag.value) return isDrag.value = false let data = findByKey(dataSourceKey.value, props.list) const sourceKey = dataSourceKey.value; dataSourceKey.value = undefined if ( data ) { const isSuccess = (await props.dropFn?.(ENiuTreeStatus.DragIn, data, props.list)) if (isSuccess == undefined || isSuccess) { removeByKey(sourceKey, props.list) props.list.push(data) emit("change") } } } const props = withDefaults( defineProps<{ list: INiuTreeData[] justOpen?: boolean autoExpand?: boolean justOpenOne?: boolean sort?: boolean level?: number dropFn?(status: ENiuTreeStatus, data: INiuTreeData, targetDataList: INiuTreeData[]): boolean | Promise<boolean> }>(), { justOpenOne: false, justOpen: false, autoExpand: false, sort: false, level: 0, } ) const slot = useSlots() provide("tree:opts", props) function onExpand(item: INiuTreeData) { emit("expand", item) } function clickNode(item: INiuTreeData) { if (props.justOpenOne) { forEachTree(props.list, (node: INiuTreeData) => { node.isExpand = false }) } if (item.isFolder) { item.isExpand = !item.isExpand emit("change") } } const emit = defineEmits<{ (e: 'change'): void (e: 'expand', data: INiuTreeData): void (e: 'itemDragstart'): void }>() const dataSourceKey = ref() function onDragstart(key: INiuTreeKey) { dataSourceKey.value = key emit("itemDragstart") } function onDragEnd(key: INiuTreeKey) { dataSourceKey.value = undefined } async function onDrop(key: INiuTreeKey, status?: ENiuTreeStatus) { if (!dataSourceKey.value) return if (!key) return let data = findByKey(dataSourceKey.value, props.list) let targetData = findByKey(key, props.list) const sourceKey = dataSourceKey.value; dataSourceKey.value = undefined switch (status) { case ENiuTreeStatus.DragInner: const parentData = findByKeyParent(key, props.list) if ( data && targetData && sourceKey != key && !isChildOf(key, sourceKey, props.list) ) { const isSuccess = (await props.dropFn?.(status, data, parentData?.children ?? props.list)) if (isSuccess == undefined || isSuccess) { removeByKey(sourceKey, props.list) if(parentData){ parentData.children.push(data) if (props.autoExpand) { parentData.isExpand = true } }else{ props.list.push(data) } emit("change") } } break case ENiuTreeStatus.DragIn: if ( data && targetData && sourceKey != key && !isChildOf(key, sourceKey, props.list) && targetData.children ) { const isSuccess = (await props.dropFn?.(status, data, targetData.children)) if (isSuccess == undefined || isSuccess) { removeByKey(sourceKey, props.list) targetData.children.push(data) if (props.autoExpand) { targetData.isExpand = true } emit("change") } } break case ENiuTreeStatus.DragDown: // 按索引往列表添加节点 if ( data && targetData && sourceKey != key && !isChildOf(key, sourceKey, props.list) ) { const isSuccess = (await props.dropFn?.(status, data, targetData.children)) if (isSuccess == undefined || isSuccess) { removeByKey(sourceKey, props.list) insertAfterByKey(key, data, props.list) emit("change") } } break case ENiuTreeStatus.DragUp: // 按索引往列表添加节点 if ( data && targetData && sourceKey != key && !isChildOf(key, sourceKey, props.list) ) { const isSuccess = (await props.dropFn?.(status, data, targetData.children)) if (isSuccess == undefined || isSuccess) { removeByKey(sourceKey, props.list) insertBeforeByKey(key, data, props.list) emit("change") } } break } } </script>