tabs-view.vue 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. <script setup lang="ts">
  2. import type { Sortable } from '@vben-core/composables';
  3. import type { TabDefinition } from '@vben-core/typings';
  4. import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
  5. import { useForwardPropsEmits, useSortable } from '@vben-core/composables';
  6. import { Tabs, TabsChrome } from './components';
  7. import { TabsProps } from './types';
  8. interface Props extends TabsProps {}
  9. defineOptions({
  10. name: 'TabsView',
  11. });
  12. const props = withDefaults(defineProps<Props>(), {
  13. contentClass: 'vben-tabs-content',
  14. dragable: true,
  15. styleType: 'chrome',
  16. });
  17. const emit = defineEmits<{
  18. close: [string];
  19. sortTabs: [number, number];
  20. unpin: [TabDefinition];
  21. }>();
  22. const forward = useForwardPropsEmits(props, emit);
  23. const sortableInstance = ref<null | Sortable>(null);
  24. // 可能会找到拖拽的子元素,这里需要确保拖拽的dom时tab元素
  25. function findParentElement(element: HTMLElement) {
  26. const parentCls = 'group';
  27. return element.classList.contains(parentCls)
  28. ? element
  29. : element.closest(`.${parentCls}`);
  30. }
  31. async function initTabsSortable() {
  32. await nextTick();
  33. const { contentClass } = props;
  34. const el = document.querySelectorAll(`.${contentClass}`)?.[0] as HTMLElement;
  35. const resetElState = () => {
  36. el.style.cursor = 'default';
  37. el.classList.remove('dragging');
  38. };
  39. const { initializeSortable } = useSortable(el, {
  40. filter: (_evt, target: HTMLElement) => {
  41. const parent = findParentElement(target);
  42. const dragable = parent?.classList.contains('dragable');
  43. return !dragable || !props.dragable;
  44. },
  45. onEnd(evt) {
  46. const { newIndex, oldIndex } = evt;
  47. // const fromElement = evt.item;
  48. const { srcElement } = (evt as any).originalEvent;
  49. if (!srcElement) {
  50. resetElState();
  51. return;
  52. }
  53. const srcParent = findParentElement(srcElement);
  54. if (!srcParent) {
  55. resetElState();
  56. return;
  57. }
  58. if (!srcParent.classList.contains('dragable')) {
  59. resetElState();
  60. return;
  61. }
  62. if (
  63. oldIndex !== undefined &&
  64. newIndex !== undefined &&
  65. !Number.isNaN(oldIndex) &&
  66. !Number.isNaN(newIndex) &&
  67. oldIndex !== newIndex
  68. ) {
  69. emit('sortTabs', oldIndex, newIndex);
  70. }
  71. resetElState();
  72. },
  73. onMove(evt) {
  74. const parent = findParentElement(evt.related);
  75. return parent?.classList.contains('dragable') && props.dragable;
  76. },
  77. onStart: () => {
  78. el.style.cursor = 'grabbing';
  79. el.classList.add('dragging');
  80. },
  81. });
  82. sortableInstance.value = await initializeSortable();
  83. }
  84. onMounted(initTabsSortable);
  85. watch(
  86. () => props.styleType,
  87. () => {
  88. sortableInstance.value?.destroy();
  89. initTabsSortable();
  90. },
  91. );
  92. onUnmounted(() => {
  93. sortableInstance.value?.destroy();
  94. });
  95. </script>
  96. <template>
  97. <TabsChrome v-if="styleType === 'chrome'" v-bind="forward" />
  98. <Tabs v-else v-bind="forward" />
  99. </template>