2025-01-01 11:39:49 +08:00
|
|
|
|
import type { Sortable } from '@vben-core/composables';
|
2024-08-16 22:20:18 +08:00
|
|
|
|
import type { EmitType } from '@vben-core/typings';
|
|
|
|
|
|
|
|
|
|
import type { TabsProps } from './types';
|
|
|
|
|
|
|
|
|
|
import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
|
|
|
|
|
|
2025-01-01 11:39:49 +08:00
|
|
|
|
import { useIsMobile, useSortable } from '@vben-core/composables';
|
2024-08-16 22:20:18 +08:00
|
|
|
|
|
|
|
|
|
// 可能会找到拖拽的子元素,这里需要确保拖拽的dom时tab元素
|
|
|
|
|
function findParentElement(element: HTMLElement) {
|
|
|
|
|
const parentCls = 'group';
|
|
|
|
|
return element.classList.contains(parentCls)
|
|
|
|
|
? element
|
|
|
|
|
: element.closest(`.${parentCls}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function useTabsDrag(props: TabsProps, emit: EmitType) {
|
|
|
|
|
const sortableInstance = ref<null | Sortable>(null);
|
|
|
|
|
|
|
|
|
|
async function initTabsSortable() {
|
|
|
|
|
await nextTick();
|
|
|
|
|
|
|
|
|
|
const el = document.querySelectorAll(
|
|
|
|
|
`.${props.contentClass}`,
|
|
|
|
|
)?.[0] as HTMLElement;
|
|
|
|
|
|
|
|
|
|
if (!el) {
|
|
|
|
|
console.warn('Element not found for sortable initialization');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const resetElState = async () => {
|
|
|
|
|
el.style.cursor = 'default';
|
2024-08-17 21:11:07 +08:00
|
|
|
|
// el.classList.remove('dragging');
|
2024-08-16 22:20:18 +08:00
|
|
|
|
el.querySelector('.draggable')?.classList.remove('dragging');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const { initializeSortable } = useSortable(el, {
|
|
|
|
|
filter: (_evt, target: HTMLElement) => {
|
|
|
|
|
const parent = findParentElement(target);
|
2024-10-10 10:55:52 +08:00
|
|
|
|
const draggable = parent?.classList.contains('draggable');
|
|
|
|
|
return !draggable || !props.draggable;
|
2024-08-16 22:20:18 +08:00
|
|
|
|
},
|
|
|
|
|
onEnd(evt) {
|
|
|
|
|
const { newIndex, oldIndex } = evt;
|
|
|
|
|
// const fromElement = evt.item;
|
|
|
|
|
const { srcElement } = (evt as any).originalEvent;
|
|
|
|
|
|
|
|
|
|
if (!srcElement) {
|
|
|
|
|
resetElState();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const srcParent = findParentElement(srcElement);
|
|
|
|
|
|
|
|
|
|
if (!srcParent) {
|
|
|
|
|
resetElState();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-10 10:55:52 +08:00
|
|
|
|
if (!srcParent.classList.contains('draggable')) {
|
2024-08-16 22:20:18 +08:00
|
|
|
|
resetElState();
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
oldIndex !== undefined &&
|
|
|
|
|
newIndex !== undefined &&
|
|
|
|
|
!Number.isNaN(oldIndex) &&
|
|
|
|
|
!Number.isNaN(newIndex) &&
|
|
|
|
|
oldIndex !== newIndex
|
|
|
|
|
) {
|
|
|
|
|
emit('sortTabs', oldIndex, newIndex);
|
|
|
|
|
}
|
|
|
|
|
resetElState();
|
|
|
|
|
},
|
|
|
|
|
onMove(evt) {
|
|
|
|
|
const parent = findParentElement(evt.related);
|
2024-10-17 14:11:42 +08:00
|
|
|
|
if (parent?.classList.contains('draggable') && props.draggable) {
|
|
|
|
|
const isCurrentAffix = evt.dragged.classList.contains('affix-tab');
|
|
|
|
|
const isRelatedAffix = evt.related.classList.contains('affix-tab');
|
|
|
|
|
// 不允许在固定的tab和非固定的tab之间互相拖拽
|
|
|
|
|
return isCurrentAffix === isRelatedAffix;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2024-08-16 22:20:18 +08:00
|
|
|
|
},
|
|
|
|
|
onStart: () => {
|
|
|
|
|
el.style.cursor = 'grabbing';
|
|
|
|
|
el.querySelector('.draggable')?.classList.add('dragging');
|
|
|
|
|
// el.classList.add('dragging');
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
sortableInstance.value = await initializeSortable();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function init() {
|
2024-09-03 13:02:19 +08:00
|
|
|
|
const { isMobile } = useIsMobile();
|
|
|
|
|
|
|
|
|
|
// 移动端下tab不需要拖拽
|
|
|
|
|
if (isMobile.value) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-08-16 22:20:18 +08:00
|
|
|
|
await nextTick();
|
|
|
|
|
initTabsSortable();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(init);
|
|
|
|
|
|
|
|
|
|
watch(
|
|
|
|
|
() => props.styleType,
|
|
|
|
|
() => {
|
|
|
|
|
sortableInstance.value?.destroy();
|
|
|
|
|
init();
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
sortableInstance.value?.destroy();
|
|
|
|
|
});
|
|
|
|
|
}
|