index.vue 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. <template>
  2. <div id="tabs-bar-container" class="tabs-bar-container">
  3. <el-tabs
  4. v-model="tabActive"
  5. type="card"
  6. class="tabs-content"
  7. @tab-click="handleTabClick"
  8. @tab-remove="handleTabRemove"
  9. >
  10. <el-tab-pane
  11. v-for="item in visitedRoutes"
  12. :key="item.path"
  13. :label="item.meta.title"
  14. :name="item.path"
  15. :closable="!isAffix(item)"
  16. ></el-tab-pane>
  17. </el-tabs>
  18. <el-dropdown @command="handleCommand">
  19. <span style="cursor: pointer">
  20. 更多操作
  21. <i class="el-icon-arrow-down el-icon--right"></i>
  22. </span>
  23. <el-dropdown-menu slot="dropdown" class="tabs-more">
  24. <el-dropdown-item command="closeOtherstabs">
  25. <vab-icon :icon="['fas', 'times-circle']" />
  26. 关闭其他
  27. </el-dropdown-item>
  28. <el-dropdown-item command="closeLefttabs">
  29. <vab-icon :icon="['fas', 'arrow-alt-circle-left']"></vab-icon>
  30. 关闭左侧
  31. </el-dropdown-item>
  32. <el-dropdown-item command="closeRighttabs">
  33. <vab-icon :icon="['fas', 'arrow-alt-circle-right']"></vab-icon>
  34. 关闭右侧
  35. </el-dropdown-item>
  36. <el-dropdown-item command="closeAlltabs">
  37. <vab-icon :icon="['fas', 'ban']"></vab-icon>
  38. 关闭全部
  39. </el-dropdown-item>
  40. </el-dropdown-menu>
  41. </el-dropdown>
  42. </div>
  43. </template>
  44. <script>
  45. import path from 'path'
  46. import { mapGetters } from 'vuex'
  47. export default {
  48. name: 'VabTabsBar',
  49. data() {
  50. return {
  51. affixtabs: [],
  52. tabActive: '',
  53. }
  54. },
  55. computed: {
  56. ...mapGetters({
  57. visitedRoutes: 'tabsBar/visitedRoutes',
  58. routes: 'routes/routes',
  59. }),
  60. },
  61. watch: {
  62. $route: {
  63. handler(route) {
  64. this.inittabs()
  65. this.addtabs()
  66. let tabActive = ''
  67. this.visitedRoutes.forEach((item, index) => {
  68. if (item.path === this.$route.path) {
  69. tabActive = item.path
  70. }
  71. })
  72. this.tabActive = tabActive
  73. },
  74. immediate: true,
  75. },
  76. },
  77. mounted() {
  78. //console.log(this.visitedRoutes);
  79. },
  80. methods: {
  81. async handleTabRemove(tabActive) {
  82. let view
  83. this.visitedRoutes.forEach((item, index) => {
  84. if (tabActive == item.path) {
  85. view = item
  86. }
  87. })
  88. const { visitedRoutes } = await this.$store.dispatch(
  89. 'tabsBar/delRoute',
  90. view
  91. )
  92. if (this.isActive(view)) {
  93. this.toLastTag(visitedRoutes, view)
  94. }
  95. },
  96. handleTabClick(tab) {
  97. const route = this.visitedRoutes.filter((item, index) => {
  98. if (tab.index == index) return item
  99. })[0]
  100. if (this.$route.path !== route.path) {
  101. this.$router.push({
  102. path: route.path,
  103. query: route.query,
  104. fullPath: route.fullPath,
  105. })
  106. } else {
  107. return false
  108. }
  109. },
  110. isActive(route) {
  111. return route.path === this.$route.path
  112. },
  113. isAffix(tag) {
  114. return tag.meta && tag.meta.affix
  115. },
  116. filterAffixtabs(routes, basePath = '/') {
  117. let tabs = []
  118. routes.forEach((route) => {
  119. if (route.meta && route.meta.affix) {
  120. const tagPath = path.resolve(basePath, route.path)
  121. tabs.push({
  122. fullPath: tagPath,
  123. path: tagPath,
  124. name: route.name,
  125. meta: { ...route.meta },
  126. })
  127. }
  128. if (route.children) {
  129. const temptabs = this.filterAffixtabs(route.children, route.path)
  130. if (temptabs.length >= 1) {
  131. tabs = [...tabs, ...temptabs]
  132. }
  133. }
  134. })
  135. return tabs
  136. },
  137. inittabs() {
  138. const affixtabs = (this.affixtabs = this.filterAffixtabs(this.routes))
  139. for (const tag of affixtabs) {
  140. if (tag.name) {
  141. this.$store.dispatch('tabsBar/addVisitedRoute', tag)
  142. }
  143. }
  144. },
  145. addtabs() {
  146. const { name } = this.$route
  147. if (name) {
  148. this.$store.dispatch('tabsBar/addVisitedRoute', this.$route)
  149. }
  150. return false
  151. },
  152. handleCommand(command) {
  153. switch (command) {
  154. case 'refreshRoute':
  155. this.refreshRoute()
  156. break
  157. case 'closeOtherstabs':
  158. this.closeOtherstabs()
  159. break
  160. case 'closeLefttabs':
  161. this.closeLefttabs()
  162. break
  163. case 'closeRighttabs':
  164. this.closeRighttabs()
  165. break
  166. case 'closeAlltabs':
  167. this.closeAlltabs()
  168. break
  169. }
  170. },
  171. async refreshRoute() {
  172. this.$baseEventBus.$emit('reloadrouter-view')
  173. },
  174. async closeSelectedTag(view) {
  175. const { visitedRoutes } = await this.$store.dispatch(
  176. 'tabsBar/delRoute',
  177. view
  178. )
  179. if (this.isActive(view)) {
  180. this.toLastTag(visitedRoutes, view)
  181. }
  182. },
  183. async closeOtherstabs() {
  184. const view = await this.toThisTag()
  185. await this.$store.dispatch('tabsBar/delOthersRoutes', view)
  186. },
  187. async closeLefttabs() {
  188. const view = await this.toThisTag()
  189. await this.$store.dispatch('tabsBar/delLeftRoutes', view)
  190. },
  191. async closeRighttabs() {
  192. const view = await this.toThisTag()
  193. await this.$store.dispatch('tabsBar/delRightRoutes', view)
  194. },
  195. async closeAlltabs() {
  196. const view = await this.toThisTag()
  197. const { visitedRoutes } = await this.$store.dispatch(
  198. 'tabsBar/delAllRoutes'
  199. )
  200. if (this.affixtabs.some((tag) => tag.path === view.path)) {
  201. return
  202. }
  203. this.toLastTag(visitedRoutes, view)
  204. },
  205. toLastTag(visitedRoutes, view) {
  206. const latestView = visitedRoutes.slice(-1)[0]
  207. if (latestView) {
  208. this.$router.push(latestView)
  209. } else {
  210. this.$router.push('/')
  211. }
  212. },
  213. async toThisTag() {
  214. const view = this.visitedRoutes.filter((item, index) => {
  215. if (item.path === this.$route.fullPath) {
  216. return item
  217. }
  218. })[0]
  219. if (this.$route.path !== view.path) this.$router.push(view)
  220. return view
  221. },
  222. },
  223. }
  224. </script>
  225. <style lang="scss" scoped>
  226. .tabs-bar-container {
  227. position: relative;
  228. box-sizing: border-box;
  229. display: flex;
  230. align-content: center;
  231. align-items: center;
  232. justify-content: space-between;
  233. height: $base-tabs-bar-height;
  234. padding-right: $base-padding;
  235. padding-left: $base-padding;
  236. user-select: none;
  237. background: $base-color-white;
  238. border-top: 1px solid #f6f6f6;
  239. ::v-deep {
  240. .fold-unfold {
  241. margin-right: $base-padding;
  242. }
  243. }
  244. .tabs-content {
  245. width: calc(100% - 90px);
  246. height: $base-tag-item-height;
  247. ::v-deep {
  248. .el-tabs__nav-next,
  249. .el-tabs__nav-prev {
  250. height: $base-tag-item-height;
  251. line-height: $base-tag-item-height;
  252. }
  253. .el-tabs__header {
  254. border-bottom: 0;
  255. .el-tabs__nav {
  256. border: 0;
  257. }
  258. .el-tabs__item {
  259. box-sizing: border-box;
  260. height: $base-tag-item-height;
  261. margin-right: 5px;
  262. line-height: $base-tag-item-height;
  263. border: 1px solid $base-border-color;
  264. border-radius: $base-border-radius;
  265. transition: padding 0.3s cubic-bezier(0.645, 0.045, 0.355, 1) !important;
  266. &.is-active {
  267. border: 1px solid $base-color-blue;
  268. }
  269. }
  270. }
  271. }
  272. }
  273. .more {
  274. display: flex;
  275. align-content: center;
  276. align-items: center;
  277. cursor: pointer;
  278. }
  279. }
  280. </style>