use-tabs-drag.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import type { EmitType } from '@vben-core/typings';
  2. import type { TabsProps } from './types';
  3. import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
  4. import {
  5. type Sortable,
  6. useIsMobile,
  7. useSortable,
  8. } from '@vben-core/composables';
  9. // 可能会找到拖拽的子元素,这里需要确保拖拽的dom时tab元素
  10. function findParentElement(element: HTMLElement) {
  11. const parentCls = 'group';
  12. return element.classList.contains(parentCls)
  13. ? element
  14. : element.closest(`.${parentCls}`);
  15. }
  16. export function useTabsDrag(props: TabsProps, emit: EmitType) {
  17. const sortableInstance = ref<null | Sortable>(null);
  18. async function initTabsSortable() {
  19. await nextTick();
  20. const el = document.querySelectorAll(
  21. `.${props.contentClass}`,
  22. )?.[0] as HTMLElement;
  23. if (!el) {
  24. console.warn('Element not found for sortable initialization');
  25. return;
  26. }
  27. const resetElState = async () => {
  28. el.style.cursor = 'default';
  29. // el.classList.remove('dragging');
  30. el.querySelector('.draggable')?.classList.remove('dragging');
  31. };
  32. const { initializeSortable } = useSortable(el, {
  33. filter: (_evt, target: HTMLElement) => {
  34. const parent = findParentElement(target);
  35. const draggable = parent?.classList.contains('draggable');
  36. return !draggable || !props.draggable;
  37. },
  38. onEnd(evt) {
  39. const { newIndex, oldIndex } = evt;
  40. // const fromElement = evt.item;
  41. const { srcElement } = (evt as any).originalEvent;
  42. if (!srcElement) {
  43. resetElState();
  44. return;
  45. }
  46. const srcParent = findParentElement(srcElement);
  47. if (!srcParent) {
  48. resetElState();
  49. return;
  50. }
  51. if (!srcParent.classList.contains('draggable')) {
  52. resetElState();
  53. return;
  54. }
  55. if (
  56. oldIndex !== undefined &&
  57. newIndex !== undefined &&
  58. !Number.isNaN(oldIndex) &&
  59. !Number.isNaN(newIndex) &&
  60. oldIndex !== newIndex
  61. ) {
  62. emit('sortTabs', oldIndex, newIndex);
  63. }
  64. resetElState();
  65. },
  66. onMove(evt) {
  67. const parent = findParentElement(evt.related);
  68. if (parent?.classList.contains('draggable') && props.draggable) {
  69. const isCurrentAffix = evt.dragged.classList.contains('affix-tab');
  70. const isRelatedAffix = evt.related.classList.contains('affix-tab');
  71. // 不允许在固定的tab和非固定的tab之间互相拖拽
  72. return isCurrentAffix === isRelatedAffix;
  73. } else {
  74. return false;
  75. }
  76. },
  77. onStart: () => {
  78. el.style.cursor = 'grabbing';
  79. el.querySelector('.draggable')?.classList.add('dragging');
  80. // el.classList.add('dragging');
  81. },
  82. });
  83. sortableInstance.value = await initializeSortable();
  84. }
  85. async function init() {
  86. const { isMobile } = useIsMobile();
  87. // 移动端下tab不需要拖拽
  88. if (isMobile.value) {
  89. return;
  90. }
  91. await nextTick();
  92. initTabsSortable();
  93. }
  94. onMounted(init);
  95. watch(
  96. () => props.styleType,
  97. () => {
  98. sortableInstance.value?.destroy();
  99. init();
  100. },
  101. );
  102. onUnmounted(() => {
  103. sortableInstance.value?.destroy();
  104. });
  105. }