index.vue 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. <template>
  2. <div class="tabs">
  3. <el-scrollbar ref="scrollbarDom" class="scroll-container tags-view-container">
  4. <Item v-for="menu in menuList" :key="menu.meta.title" :active="activeMenu === menu.path" :menu="menu"
  5. @close="delMenu(menu)"/>
  6. </el-scrollbar>
  7. <div class="handle">
  8. <el-dropdown placement="bottom">
  9. <div class="el-dropdown-link">
  10. <i class="el-icon-arrow-down el-icon--right"></i>
  11. </div>
  12. <template #dropdown>
  13. <el-dropdown-menu>
  14. <el-dropdown-item :disabled="currentDisabled" icon="el-icon-circle-close" @click="closeCurrentRoute">
  15. 关闭当前标签
  16. </el-dropdown-item>
  17. <el-dropdown-item :disabled="menuList.length < 3" icon="el-icon-circle-close" @click="closeOtherRoute">
  18. 关闭其他标签
  19. </el-dropdown-item>
  20. <el-dropdown-item :disabled="menuList.length <= 1" icon="el-icon-circle-close" @click="closeAllRoute">
  21. 关闭所有标签
  22. </el-dropdown-item>
  23. </el-dropdown-menu>
  24. </template>
  25. </el-dropdown>
  26. <el-tooltip :content="contentFullScreen ? '退出全屏' : '内容全屏'" class="item" effect="dark" placement="bottom">
  27. <i class="el-icon-full-screen" @click="onFullscreen"></i>
  28. </el-tooltip>
  29. </div>
  30. </div>
  31. </template>
  32. <script name="tabs" setup>
  33. import Item from './item.vue'
  34. import {computed, nextTick, ref, watch} from 'vue'
  35. import {useStore} from 'vuex'
  36. import {useRoute, useRouter} from 'vue-router'
  37. import tabsHook from './tabsHook'
  38. const store = useStore()
  39. const route = useRoute()
  40. const router = useRouter()
  41. const scrollbarDom = ref(null)
  42. const defaultMenu = {
  43. path: '/dashboard',
  44. meta: {title: '首页', hideClose: true},
  45. }
  46. const contentFullScreen = computed(() => store.state.app.contentFullScreen)
  47. const currentDisabled = computed(() => route.path === defaultMenu.path)
  48. let activeMenu = $ref('')
  49. let menuList = ref(tabsHook.getItem())
  50. if (menuList.value.length === 0) {
  51. // 判断之前有没有调用过
  52. addMenu(defaultMenu)
  53. }
  54. watch(menuList.value, (newVal) => {
  55. tabsHook.setItem(newVal)
  56. })
  57. watch(menuList, (newVal) => {
  58. tabsHook.setItem(newVal)
  59. })
  60. router.afterEach(() => {
  61. addMenu(route)
  62. initMenu(route)
  63. })
  64. // 全屏
  65. function onFullscreen() {
  66. store.commit('app/contentFullScreenChange', !contentFullScreen.value)
  67. }
  68. // 关闭当前标签,首页不关闭
  69. function closeCurrentRoute() {
  70. if (route.path !== defaultMenu.path) {
  71. delMenu(route)
  72. }
  73. }
  74. // 关闭除了当前标签之外的所有标签
  75. function closeOtherRoute() {
  76. menuList.value = [defaultMenu]
  77. if (route.path !== defaultMenu.path) {
  78. addMenu(route)
  79. }
  80. setKeepAliveData()
  81. }
  82. // 关闭所有的标签,除了首页
  83. function closeAllRoute() {
  84. menuList.value = [defaultMenu]
  85. setKeepAliveData()
  86. router.push(defaultMenu.path)
  87. }
  88. // 添加新的菜单项
  89. function addMenu(menu) {
  90. let {path, meta, name} = menu
  91. if (meta.activeMenu) {
  92. let a = menuList.value.some((obj) => {
  93. return obj.path === meta.activeMenu
  94. })
  95. if (!a) {
  96. menuList.value.push({
  97. path: meta.activeMenu,
  98. meta: {title: meta.parentName}
  99. })
  100. }
  101. return;
  102. } else if (meta.hideTabs) {
  103. return
  104. }
  105. let hasMenu = menuList.value.some((obj) => {
  106. return obj.path === path || obj.path === meta.activeMenu
  107. })
  108. if (!hasMenu) {
  109. menuList.value.push({
  110. path,
  111. meta,
  112. name,
  113. })
  114. }
  115. }
  116. // 删除菜单项
  117. function delMenu(menu) {
  118. if (!menu.meta.hideClose) {
  119. if (menu.meta.cache && menu.name) {
  120. store.commit('keepAlive/delKeepAliveComponentsName', menu.name)
  121. }
  122. menuList.value.splice(
  123. menuList.value.findIndex((item) => item.path === menu.path),
  124. 1
  125. )
  126. }
  127. if (menu.path === activeMenu) {
  128. router.push(defaultMenu.path)
  129. }
  130. }
  131. // 初始化activeMenu
  132. function initMenu(menu) {
  133. if (menu.meta.activeMenu) {
  134. activeMenu = menu.meta.activeMenu
  135. } else {
  136. activeMenu = menu.path
  137. }
  138. nextTick(() => {
  139. setPosition()
  140. })
  141. }
  142. // 设置当前滚动条应该在的位置
  143. function setPosition() {
  144. if (scrollbarDom.value) {
  145. const domBox = {
  146. scrollbar: scrollbarDom.value.scrollbar.querySelector('.el-scrollbar__wrap '),
  147. activeDom: scrollbarDom.value.scrollbar.querySelector('.active'),
  148. activeFather: scrollbarDom.value.scrollbar.querySelector('.el-scrollbar__view'),
  149. }
  150. for (let i in domBox) {
  151. if (!domBox[i]) {
  152. return
  153. }
  154. }
  155. const domData = {
  156. scrollbar: domBox.scrollbar.getBoundingClientRect(),
  157. activeDom: domBox.activeDom.getBoundingClientRect(),
  158. activeFather: domBox.activeFather.getBoundingClientRect(),
  159. }
  160. const num = domData.activeDom.x - domData.activeFather.x + (1 / 2) * domData.activeDom.width - (1 / 2) * domData.scrollbar.width
  161. domBox.scrollbar.scrollLeft = num
  162. }
  163. }
  164. // 配置需要缓存的数据
  165. function setKeepAliveData() {
  166. let keepAliveNames = []
  167. menuList.value.forEach((menu) => {
  168. menu.meta && menu.meta.cache && menu.name && keepAliveNames.push(menu.name)
  169. })
  170. store.commit('keepAlive/setKeepAliveComponentsName', keepAliveNames)
  171. }
  172. // 初始化时调用:1. 新增菜单 2. 初始化activeMenu
  173. addMenu(route)
  174. initMenu(route)
  175. </script>
  176. <style lang="scss" scoped>
  177. .tabs {
  178. display: flex;
  179. justify-content: space-between;
  180. align-items: center;
  181. height: 40px;
  182. background: var(--system-header-background);
  183. border-bottom: 1px solid var(--system-header-border-color);
  184. border-top: 1px solid var(--system-header-border-color);
  185. box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.1);
  186. .handle {
  187. min-width: 95px;
  188. height: 100%;
  189. display: flex;
  190. align-items: center;
  191. .el-dropdown-link {
  192. margin-top: 5px;
  193. border-left: 1px solid var(--system-header-border-color);
  194. height: 25px;
  195. width: 40px;
  196. display: flex;
  197. justify-content: center;
  198. align-items: center;
  199. }
  200. i {
  201. color: var(--system-header-text-color);
  202. }
  203. }
  204. }
  205. .scroll-container {
  206. white-space: nowrap;
  207. position: relative;
  208. overflow: hidden;
  209. width: 100%;
  210. :deep {
  211. .el-scrollbar__bar {
  212. bottom: 0px;
  213. }
  214. .el-scrollbar__wrap {
  215. height: 49px;
  216. }
  217. }
  218. }
  219. .tags-view-container {
  220. height: 34px;
  221. flex: 1;
  222. width: 100%;
  223. display: flex;
  224. }
  225. .el-icon-full-screen {
  226. cursor: pointer;
  227. &:hover {
  228. background: rgba(0, 0, 0, 0.025);
  229. }
  230. &:focus {
  231. outline: none;
  232. }
  233. }
  234. </style>