index.vue 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. <template>
  2. <div class="tabs-box">
  3. <div class="tabs-menu">
  4. <el-tabs v-model="tabsMenuValue" type="card" @tab-click="tabClick" @tab-remove="tabRemove">
  5. <el-tab-pane v-for="item in tabsMenuList" :key="item.path" :label="item.title" :name="item.path" :closable="item.close">
  6. <template #label>
  7. <el-icon v-if="item.icon && tabsIcon" class="tabs-icon">
  8. <component :is="item.icon" />
  9. </el-icon>
  10. {{ item.title }}
  11. </template>
  12. </el-tab-pane>
  13. </el-tabs>
  14. <MoreButton />
  15. </div>
  16. </div>
  17. </template>
  18. <script setup lang="ts">
  19. import Sortable from "sortablejs";
  20. import { ref, computed, watch, onMounted } from "vue";
  21. import { useRoute, useRouter } from "vue-router";
  22. import { useGlobalStore } from "@/stores/modules/global";
  23. import { useTabsStore } from "@/stores/modules/tabs";
  24. import { useAuthStore } from "@/stores/modules/auth";
  25. import { TabsPaneContext, TabPaneName } from "element-plus";
  26. import MoreButton from "./components/MoreButton.vue";
  27. import { HOME_URL } from "@/config";
  28. const route = useRoute();
  29. const router = useRouter();
  30. const tabStore = useTabsStore();
  31. const authStore = useAuthStore();
  32. const globalStore = useGlobalStore();
  33. const tabsMenuValue = ref(route?.fullPath || "");
  34. const tabsMenuList = computed(() => tabStore.tabsMenuList);
  35. const tabsIcon = computed(() => globalStore.tabsIcon);
  36. onMounted(() => {
  37. tabsDrop();
  38. initTabs();
  39. });
  40. // 监听路由的变化(防止浏览器后退/前进不变化 tabsMenuValue)
  41. watch(
  42. () => route.fullPath,
  43. () => {
  44. // 检查 route 和 route.meta 是否存在
  45. if (!route || !route.meta || !route.fullPath) return;
  46. if (route.meta.isFull) return;
  47. tabsMenuValue.value = route.fullPath;
  48. const tabsParams = {
  49. icon: route.meta.icon as string,
  50. title: route.meta.title as string,
  51. path: route.fullPath,
  52. name: route.name as string,
  53. close: !route.meta.isAffix,
  54. isKeepAlive: route.meta.isKeepAlive as boolean
  55. };
  56. tabStore.addTabs(tabsParams);
  57. },
  58. { immediate: true }
  59. );
  60. // 初始化需要固定的 tabs
  61. const initTabs = () => {
  62. authStore.flatMenuListGet.forEach(item => {
  63. if (item.meta.isAffix && !item.meta.isHide && !item.meta.isFull) {
  64. const tabsParams = {
  65. icon: item.meta.icon,
  66. title: item.meta.title,
  67. path: item.path,
  68. name: item.name,
  69. close: !item.meta.isAffix,
  70. isKeepAlive: item.meta.isKeepAlive
  71. };
  72. tabStore.addTabs(tabsParams);
  73. }
  74. });
  75. };
  76. // tabs 拖拽排序
  77. const tabsDrop = () => {
  78. Sortable.create(document.querySelector(".el-tabs__nav") as HTMLElement, {
  79. draggable: ".el-tabs__item",
  80. animation: 300,
  81. onEnd({ newIndex, oldIndex }) {
  82. const tabsList = [...tabStore.tabsMenuList];
  83. const currRow = tabsList.splice(oldIndex as number, 1)[0];
  84. tabsList.splice(newIndex as number, 0, currRow);
  85. tabStore.setTabs(tabsList);
  86. }
  87. });
  88. };
  89. // Tab Click
  90. const tabClick = (tabItem: TabsPaneContext) => {
  91. const fullPath = tabItem.props.name as string;
  92. router.push(fullPath);
  93. };
  94. // Remove Tab
  95. const tabRemove = (fullPath: TabPaneName) => {
  96. // 判断是否为最后一个标签页
  97. if (tabStore.tabsMenuList.length === 1) {
  98. if (HOME_URL == fullPath) {
  99. return false;
  100. }
  101. tabStore.removeTabs(fullPath as string, route && route.fullPath ? fullPath == route.fullPath : false);
  102. router.push(HOME_URL);
  103. } else {
  104. tabStore.removeTabs(fullPath as string, route && route.fullPath ? fullPath == route.fullPath : false);
  105. }
  106. };
  107. </script>
  108. <style scoped lang="scss">
  109. @import "./index";
  110. </style>