项目需求:使用Tabs标签页显示对应菜单项内容,同时实现数据缓存便于操作,关闭Tabs标签页时重置组件清空缓存。这里使用 Vue keep-alive 动态缓存的方式实现。
同时配合 vuex 实现Tabs标签页的全局配置 - 多标签页、单标签页、关闭。

具体实现方式如下:
<template>
  <!-- 公共多Tab缓存组件 -->
  <div id="common-tabs-keep-alive">
    <a-tabs v-model="activeKey"
      :hide-add="true"
      type="editable-card"
      @change="changePage"
      @edit="onEdit"
      style="margin-top: 10px;"
      v-if="tabsState !== 'hide'"
    >
      <a-tab-pane
        v-for="pane in panes"
        :key="pane.fullPath"
        :tab="pane.title"
        :closable="closable"
      ></a-tab-pane>
    </a-tabs>
    <!-- router-view 插槽 -->
    <slot />
  </div>
</template>

<script>
import { mapState } from 'vuex'

export default {
  name: 'CommonTabsKeepAlive',
  data () {
    return {
      panes: [], // Tabs基础数据
      activeKey: '', // 当前选中Tab - 默认为当前路由
      keepAliveData: [] // 用于处理缓存组件
    }
  },
  created () {
    this.tabsInit()
  },
  computed: {
    // 菜单基础数据、标签页状态
    ...mapState({
      userMenu: state => state.permission.userMenu,
      tabsState: state => state.frontSettings.tabsState
    }),
    // 标签页关闭状态
    closable () {
      return this.panes.length !== 1
    }
  },
  methods: {
    // 初始化数据
    tabsInit () {
    // 重置
      delete sessionStorage.keepAliveData
      if (this.$route.fullPath !== '/system/exception/404/list' && this.$route.fullPath !== '/system/exception/301/list') {
      // 初始化 Tab 标签
        this.activeKey = this.$route.fullPath
        this.keepAliveData = [this.$route.name]
        const data = { ...this.$route, title: '' }
        this.panes = [data]
        this.getPaneTitle(this.$route.name, this.userMenu)
        this.updateLocalKeepAlive()
      }
    },
    /**
     * 递归处理标签标题
     * @param routeName 组件名
     * @param menuData 菜单基础数据
     */
    getPaneTitle (routeName, menuData) {
      let data = menuData.filter(item => routeName.match(item.name))
      if (data[0].children.length > 0) {
        this.getPaneTitle(routeName, data[0].children)
      } else {
        this.$set(this.panes.filter(item => item.name === routeName)[0], 'title', data[0].meta.title)
      }
    },
    // 选择 Tab
    changePage (activeKey) {
      this.activeKey = activeKey
      this.$router.push(activeKey)
      this.updateLocalKeepAlive()
    },
    // 关闭 Tab
    onEdit (targetKey) {
      if (this.panes.length > 1) {
        this.keepAliveData = this.keepAliveData.filter((item) => item !== this.panes.filter((item) => item.fullPath === targetKey)[0].name)
        this.panes = this.panes.filter((item) => item.fullPath !== targetKey)
        // 强制更新路由实现清除缓存 - 重定向
        this.$router.push('/system/exception/301/list')
        // 选中最后一个Tab
        this.changePage(this.panes[this.panes.length - 1].fullPath)
      }
    },
    // 更新本地缓存组件
    updateLocalKeepAlive () {
      sessionStorage.keepAliveData = JSON.stringify(this.keepAliveData)
    }
  },
  watch: {
    // 监听路由变化,处理Tab基础数据
    $route (val) {
      // 处理详情页空白 - 因路由定义问题导致浏览器返回显示空白页,暂无法处理
      if (val.path !== '/' && val.path !== '/system/exception/404/list' && val.path !== '/system/exception/301/list') {
        // 判断当前路由是否已打开 Tab 页
        if (this.panes.filter((item) => item.fullPath === val.fullPath).length === 0) {
          // 添加 Tab 数据
          const data = { ...val, title: '' }
          this.panes = this.tabsState === 'more' ? [...this.panes, data] : [data]
          this.getPaneTitle(val.name, this.userMenu)
          // 添加缓存组件数据
          this.keepAliveData = this.tabsState === 'more' ? [...this.keepAliveData, val.name] : [val.name]
        }
        this.changePage(val.fullPath)
      }
    },
    // 监听标签页显示状态
    tabsState (val) {
      this.tabsInit()
    }
  }
}
</script>

<style lang="less" scoped>
</style>
<template>
  <div>
    <template v-if="layoutMode === 'default'">
      <h-menu>
        <!-- 多Tab缓存组件 -->
        <CommonTabsKeepAlive>
          <router-view />
        </CommonTabsKeepAlive>
      </h-menu>
    </template>
    <template v-else>
      <router-view />
    </template>
  </div>
</template>

<script>
import HMenu from '@/components/base/HMenu.vue'
import CommonTabsKeepAlive from '@/components/base/CommonTabsKeepAlive'

export default {
  name: 'rootPlaceholderRouter',
  components: {
    HMenu,
    CommonTabsKeepAlive
  },
  data () {
    return {
      layoutMode: localStorage.getItem('layoutMode') || 'default'
    }
  }
}
</script>

<style lang="less" scoped>
</style>
<!--代理router-view组件-->
<template>
  <div id="">
    <keep-alive :include="keepAliveIncludes" v-if="tabsState === 'more'">
      <router-view />
    </keep-alive>
    <router-view v-else />
  </div>
</template>

<script>
import { mapState } from 'vuex'

export default {
  name: 'placeholderRouter',
  data () {
    return {
      keepAliveData: [] // 缓存组件 name 数组
    }
  },
  mounted () {
    // 初始化设置缓存组件
    this.keepAliveData = sessionStorage.keepAliveData ? JSON.parse(sessionStorage.keepAliveData) : []
  },
  computed: {
    // 标签页状态
    ...mapState({
      tabsState: state => state.frontSettings.tabsState
    }),
    // 缓存组件 name 格式化
    keepAliveIncludes () {
      return this.keepAliveData.join(',')
    },
    // 无需缓存组件 name - 暂不使用
    keepAliveExcludes () {
      return ''
    }
  },
  watch: {
    // 监听路由变化、更新缓存组件
    $route (val) {
      this.keepAliveData = JSON.parse(sessionStorage.keepAliveData)
    }
  }
}
</script>

<style lang="less" scoped>
</style>
Logo

前往低代码交流专区

更多推荐