index.vue 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. <template>
  2. <div class="tabs-box flx-center">
  3. <div class="flx-shrink pl-12 pr-8" style="cursor: pointer" @click="gohome">
  4. <i class="tabs-icon iconfont iconhome-smile"></i>
  5. </div>
  6. <el-divider direction="vertical"></el-divider>
  7. <div class="tabs-menu flx-1">
  8. <el-tabs v-model="tabsMenuValue" type="card" @tab-click="tabClick" @tab-remove="tabRemove">
  9. <el-tab-pane v-for="item in tabsMenuList" :key="item.path" :label="item.title" :name="item.path" :closable="item.close">
  10. <template #label>
  11. <template v-if="item.path.indexOf('/detail') > -1 || item.path.indexOf('/edit?') > -1">
  12. <div class="flx-start">
  13. <i
  14. class="iconfont flx-shrink tabs-icon"
  15. v-show="item.icon && tabsIcon"
  16. :class="item.path.indexOf('/edit?') > -1 ? 'iconedit-02' : item.icon"
  17. ></i>
  18. <div class="title flx-1 flx-col">
  19. <template v-if="getParam(item.path, 'code')">
  20. {{ getParam(item.path, "code") }}
  21. <span class="text-f-s">
  22. {{ $t(`menu.${item.name}`) }}
  23. </span>
  24. </template>
  25. <template v-else>
  26. {{ $t(`menu.${item.name}`) }}
  27. </template>
  28. </div>
  29. </div>
  30. </template>
  31. <template v-else>
  32. <i
  33. class="iconfont flx-shrink tabs-icon"
  34. v-show="item.icon && tabsIcon"
  35. :class="item.path.indexOf('/new') > -1 ? 'iconPlus_light' : item.icon"
  36. ></i>
  37. {{ $t(`menu.${item.name}`) }}
  38. </template>
  39. </template>
  40. </el-tab-pane>
  41. </el-tabs>
  42. <MoreButton v-if="showMoreButton" />
  43. </div>
  44. <slot name="menu-right" />
  45. </div>
  46. </template>
  47. <script setup lang="ts">
  48. import Sortable from "sortablejs";
  49. import { ref, computed, watch, onMounted } from "vue";
  50. import { useRoute, useRouter } from "vue-router";
  51. import { useGlobalStore } from "@/stores/modules/global";
  52. import { useTabsStore } from "@/stores/modules/tabs";
  53. import { useAuthStore } from "@/stores/modules/auth";
  54. import { useKeepAliveStore } from "@/stores/modules/keepAlive";
  55. import { TabsPaneContext, TabPaneName } from "element-plus";
  56. import MoreButton from "./components/MoreButton.vue";
  57. const route = useRoute();
  58. const router = useRouter();
  59. const tabStore = useTabsStore();
  60. const authStore = useAuthStore();
  61. const globalStore = useGlobalStore();
  62. const keepAliveStore = useKeepAliveStore();
  63. const tabsMenuValue = ref(route.fullPath);
  64. const tabsMenuList = computed(() => tabStore.tabsMenuList);
  65. const tabsIcon = computed(() => globalStore.tabsIcon);
  66. onMounted(() => {
  67. tabsDrop();
  68. initTabs();
  69. });
  70. const props = defineProps({
  71. showMoreButton: {
  72. type: Boolean,
  73. default: true
  74. }
  75. });
  76. // 监听路由的变化(防止浏览器后退/前进不变化 tabsMenuValue)
  77. watch(
  78. () => route.fullPath,
  79. () => {
  80. if (route.meta.isFull) return;
  81. console.log("watch route :>> ", route);
  82. tabsMenuValue.value = route.fullPath;
  83. const tabsParams = {
  84. icon: route.meta.icon as string,
  85. title: route.meta.title as string,
  86. path: route.fullPath,
  87. name: route.name as string,
  88. close: !route.meta.isAffix
  89. };
  90. console.log("tabStore :>> ", tabStore, route);
  91. let isReplace = false;
  92. if (tabsParams.path.indexOf("/edit?") > -1 || tabsParams.path.indexOf("/edit/") > -1) {
  93. let key = "/edit?";
  94. let name = "";
  95. let _path = tabsParams.path.replace(key, "/detail?");
  96. // 使用路由参数的地址,忽略查询参数后,查找相同地址
  97. let _path2 = tabsParams.path.replace("/edit/", "/detail/");
  98. let tabsMenuList = tabStore.tabsMenuList;
  99. for (let i = 0; i < tabsMenuList.length; i++) {
  100. if (tabsMenuList[i].path == _path || tabsMenuList[i].path.split("?")[0] == _path2.split("?")[0]) {
  101. name = tabsMenuList[i].name;
  102. tabsMenuList[i] = tabsParams;
  103. isReplace = true;
  104. break;
  105. }
  106. }
  107. console.log("keilll name :>> ", name);
  108. tabStore.setTabs(tabsMenuList);
  109. name && keepAliveStore.removeKeepAliveName(route);
  110. } else if (tabsParams.path.indexOf("/detail?") > -1 || tabsParams.path.indexOf("/detail/") > -1) {
  111. let key = "/detail?";
  112. let _path = tabsParams.path.replace(key, "/edit?");
  113. // 使用路由参数的地址,忽略查询参数后,查找相同地址
  114. let _path2 = tabsParams.path.replace("/detail/", "/edit/");
  115. let tabsMenuList = tabStore.tabsMenuList;
  116. for (let i = 0; i < tabsMenuList.length; i++) {
  117. if (tabsMenuList[i].path == _path || tabsMenuList[i].path.split("?")[0] == _path2.split("?")[0]) {
  118. tabsMenuList[i] = tabsParams;
  119. isReplace = true;
  120. break;
  121. }
  122. }
  123. tabStore.setTabs(tabsMenuList);
  124. route.meta.isKeepAlive && keepAliveStore.addKeepAliveName(route);
  125. console.log("add keepAliveStore.keepAliveName :>> ", keepAliveStore.keepAliveName);
  126. }
  127. !isReplace && tabStore.addTabs(tabsParams);
  128. !isReplace && route.meta.isKeepAlive && keepAliveStore.addKeepAliveName(route);
  129. },
  130. { immediate: true }
  131. );
  132. // tabs 拖拽排序
  133. const tabsDrop = () => {
  134. Sortable.create(document.querySelector(".el-tabs__nav") as HTMLElement, {
  135. draggable: ".el-tabs__item",
  136. animation: 300,
  137. onEnd({ newIndex, oldIndex }) {
  138. const tabsList = [...tabStore.tabsMenuList];
  139. const currRow = tabsList.splice(oldIndex as number, 1)[0];
  140. tabsList.splice(newIndex as number, 0, currRow);
  141. tabStore.setTabs(tabsList);
  142. }
  143. });
  144. };
  145. // 初始化需要固定的 tabs
  146. const initTabs = () => {
  147. authStore.flatMenuListGet.forEach(item => {
  148. if (item.meta.isAffix && !item.meta.isHide && !item.meta.isFull) {
  149. const tabsParams: any = {
  150. icon: item.meta.icon,
  151. title: item.meta.title,
  152. path: item.path,
  153. name: item.name,
  154. close: !item.meta.isAffix
  155. };
  156. tabStore.addTabs(tabsParams);
  157. }
  158. });
  159. };
  160. // Tab Click
  161. const tabClick = (tabItem: TabsPaneContext) => {
  162. const fullPath = tabItem.props.name as string;
  163. router.push(fullPath);
  164. };
  165. // Remove Tab
  166. const tabRemove = (fullPath: TabPaneName) => {
  167. // const name = tabStore.tabsMenuList.filter(item => item.path == fullPath)[0].name || "";
  168. let routeItem = tabStore.tabsMenuList.filter(item => item.path == fullPath)[0];
  169. console.log("tabRemove tabStore.tabsMenuList :>> ", tabStore.tabsMenuList);
  170. console.log("tabRemove routeItem :>> ", routeItem);
  171. keepAliveStore.removeKeepAliveName(routeItem);
  172. let arr = keepAliveStore.getKeepAliveList;
  173. arr = arr.filter(item => item.fullPath !== routeItem.path);
  174. console.log("tabRemove arr :>> ", arr);
  175. keepAliveStore.setKeepAliveList(arr);
  176. tabStore.removeTabs(fullPath as string, fullPath == route.fullPath);
  177. };
  178. // get Path param
  179. const getParam = (path: string, name: string) => {
  180. let reg = new RegExp(name + "=(.*?)(&|$)");
  181. let res = path.match(reg);
  182. return res ? res[1] : null;
  183. };
  184. const gohome = () => {
  185. router.push("/home/index");
  186. };
  187. defineExpose({
  188. tabRemove
  189. });
  190. </script>
  191. <style scoped lang="scss">
  192. @import "./index.scss";
  193. </style>