当删除的是激活状态tag时,我就让剩下集合中最有一个为激活态并跳转到对应路由

效果图
删除前
image.png
删除激活标签guide后
image.png

2-1 修改tagviews组件

删除后 调用toLastView方法,从剩下集合中得到最后一个路由tag 触发路由跳转 即可

删除方法
image.png
删除后调用toLastView
image.png
src/layout/components/TagsView/index.vue

  1. <template>
  2. <div class="tags-view-container">
  3. <div class="tags-view-wrapper">
  4. <router-link
  5. class="tags-view-item"
  6. :class="{
  7. active: isActive(tag)
  8. }"
  9. v-for="(tag, index) in visitedTags"
  10. :key="index"
  11. :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
  12. tag="span"
  13. >
  14. {{ tag.title }}
  15. <span
  16. class="el-icon-close"
  17. @click.prevent.stop="closeSelectedTag(tag)"
  18. ></span>
  19. </router-link>
  20. </div>
  21. </div>
  22. </template>
  23. <script lang="ts">
  24. import { defineComponent, computed, watch, onMounted } from 'vue'
  25. import { useRoute, RouteRecordRaw, useRouter } from 'vue-router'
  26. import { useStore } from '@/store'
  27. import { RouteLocationWithFullPath } from '@/store/modules/tagsView'
  28. export default defineComponent({
  29. name: 'TagsView',
  30. setup() {
  31. const store = useStore()
  32. const router = useRouter()
  33. const route = useRoute()
  34. // 可显示的tags view
  35. const visitedTags = computed(() => store.state.tagsView.visitedViews)
  36. // 添加tag
  37. const addTags = () => {
  38. const { name } = route
  39. if (name) {
  40. store.dispatch('tagsView/addView', route)
  41. }
  42. }
  43. // 路径发生变化追加tags view
  44. watch(() => route.path, () => {
  45. addTags()
  46. })
  47. // 最近当前router到tags view
  48. onMounted(() => {
  49. addTags()
  50. })
  51. // 当前是否是激活的tag
  52. const isActive = (tag: RouteRecordRaw) => {
  53. return tag.path === route.path
  54. }
  55. // 让删除后tags view集合中最后一个为选中状态
  56. const toLastView = (visitedViews: RouteLocationWithFullPath[], view: RouteLocationWithFullPath) => {
  57. // 得到集合中最后一个项tag view 可能没有
  58. const lastView = visitedViews[visitedViews.length - 1]
  59. if (lastView) {
  60. router.push(lastView.fullPath as string)
  61. } else { // 集合中都没有tag view时
  62. // 如果刚刚删除的正是Dashboard 就重定向回Dashboard(首页)
  63. if (view.name === 'Dashboard') {
  64. router.replace({ path: '/redirect' + view.fullPath as string })
  65. } else {
  66. // tag都没有了 删除的也不是Dashboard 只能跳转首页
  67. router.push('/')
  68. }
  69. }
  70. }
  71. // 关闭当前右键的tag路由
  72. const closeSelectedTag = (view: RouteLocationWithFullPath) => {
  73. // 关掉并移除view
  74. store.dispatch('tagsView/delView', view).then(() => {
  75. // 如果移除的view是当前选中状态view, 就让删除后的集合中最后一个tag view为选中态
  76. if (isActive(view)) {
  77. toLastView(visitedTags.value, view)
  78. }
  79. })
  80. }
  81. return {
  82. visitedTags,
  83. isActive,
  84. closeSelectedTag
  85. }
  86. }
  87. })
  88. </script>
  89. <style lang="scss" scoped>
  90. .tags-view-container {
  91. width: 100%;
  92. height: 34px;
  93. background: #fff;
  94. border-bottom: 1px solid #d8dce5;
  95. box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
  96. .tags-view-wrapper {
  97. .tags-view-item {
  98. display: inline-block;
  99. height: 26px;
  100. line-height: 26px;
  101. border: 1px solid #d8dce5;
  102. background: #fff;
  103. color: #495060;
  104. padding: 0 8px;
  105. box-sizing: border-box;
  106. font-size: 12px;
  107. margin-left: 5px;
  108. margin-top: 4px;
  109. &:first-of-type {
  110. margin-left: 15px;
  111. }
  112. &:last-of-type {
  113. margin-right: 15px;
  114. }
  115. &.active {
  116. background-color: #42b983;
  117. color: #fff;
  118. border-color: #42b983;
  119. &::before {
  120. position: relative;
  121. display: inline-block;
  122. content: '';
  123. width: 8px;
  124. height: 8px;
  125. border-radius: 50%;
  126. margin-right: 5px;
  127. background: #fff;
  128. }
  129. }
  130. }
  131. }
  132. }
  133. </style>
  134. <style lang="scss">
  135. .tags-view-container {
  136. .el-icon-close {
  137. width: 16px;
  138. height: 16px;
  139. position: relative;
  140. left: 2px;
  141. border-radius: 50%;
  142. text-align: center;
  143. transition: all .3s cubic-bezier(.645, .045, .355, 1);
  144. transform-origin: 100% 50%;
  145. &:before {
  146. transform: scale(.6);
  147. display: inline-block;
  148. vertical-align: -1px;
  149. }
  150. &:hover {
  151. background-color: #b4bccc;
  152. color: #fff;
  153. }
  154. }
  155. }
  156. </style>

2-2 修改tagsView/delView action

修改delView action 返回promise

image.png
src/store/modules/tagsView.ts

  1. import { Module, ActionTree, MutationTree } from 'vuex'
  2. import { RouteRecordRaw, RouteRecordNormalized } from 'vue-router'
  3. import { IRootState } from '@/store'
  4. // 携带fullPath
  5. export interface RouteLocationWithFullPath extends RouteRecordNormalized {
  6. fullPath?: string;
  7. }
  8. export interface ITagsViewState {
  9. // 存放当前显示的tags view集合
  10. visitedViews: RouteLocationWithFullPath[];
  11. }
  12. // 定义mutations
  13. const mutations: MutationTree<ITagsViewState> = {
  14. // 添加可显示tags view
  15. ADD_VISITED_VIEW(state, view) {
  16. // 过滤去重
  17. if (state.visitedViews.some(v => v.path === view.path)) return
  18. // 没有titles时处理
  19. state.visitedViews.push(Object.assign({}, view, {
  20. title: view.meta.title || 'tag-name'
  21. }))
  22. },
  23. DEL_VISITED_VIEW(state, view) {
  24. const i = state.visitedViews.indexOf(view)
  25. if (i > -1) {
  26. state.visitedViews.splice(i, 1)
  27. }
  28. }
  29. }
  30. // 定义actions
  31. const actions: ActionTree<ITagsViewState, IRootState> = {
  32. // 添加tags view
  33. addView({ dispatch }, view: RouteRecordRaw) {
  34. dispatch('addVisitedView', view)
  35. },
  36. // 添加可显示的tags view 添加前commit里需要进行去重过滤
  37. addVisitedView({ commit }, view: RouteRecordRaw) {
  38. commit('ADD_VISITED_VIEW', view)
  39. },
  40. // 删除tags view
  41. delView({ dispatch }, view: RouteRecordRaw) {
  42. return new Promise(resolve => {
  43. dispatch('delVisitedView', view)
  44. resolve(null)
  45. })
  46. },
  47. // 从可显示的集合中 删除tags view
  48. delVisitedView({ commit }, view: RouteRecordRaw) {
  49. commit('DEL_VISITED_VIEW', view)
  50. }
  51. }
  52. const tagsView: Module<ITagsViewState, IRootState> = {
  53. namespaced: true,
  54. state: {
  55. visitedViews: []
  56. },
  57. mutations,
  58. actions
  59. }
  60. export default tagsView

本节参考源码

https://gitee.com/brolly/vue3-element-admin/commit/192704483ace14c92be98dc0a1d76ac6665111a5