Skip to content
RayHao edited this page Nov 23, 2023 · 6 revisions

管理 PC 端标签页列表功能,提供便捷的辅助函数来实现功能。

此插件依托于 PageKeepAlive 实现!

安装方式

import { createSDK, createTabs } from 'vue-app-sdk'

export const SDK = createSDK({
  plugins: [
    createTabs({
      // 路由导航时自动收集标签页,
      // 设为 `false` 时需要自行处理路由跳转时添加标签页
      autoCollect: true,

      // 相同路由不同参数时处理标签页的模式
      // - `normal`: 正常模式,会生成相同路由不同参数的标签页
      // - `replace`: 替换模式,会先移除旧标签页再生成新标签页
      // 注意:由于 `KeepAlive` 组件是基于组件 `name` 进行保活,所以搭配使用时在移除标签页后会导致相同路由不同参数的标签页保活失效!
      mode: 'replace',

      // `mode` 设为 `replace` 时,替换标签页前的回调处理,若返回假值则取消替换改为打开已存在的标签页
      beforeReplace: (tabPage) => {
        return ElMessageBox.confirm(`当前已存在【${tabPage.title}】页面,请选择打开方式?`, '提示', {
          confirmButtonText: '旧标签页',
          cancelButtonText: '新标签页',
          closeOnClickModal: false,
          closeOnPressEscape: false,
          closeOnHashChange: false,
          showClose: false,
        })
          .then((value) => value === 'confirm')
          .catch((e) => e === 'confirm')
      },

      // `KeepAlive` 插件模式(需自行安装 `KeepAlive` 插件)
      // - `normal`: 默认模式,由 `KeepAlive` 插件自行控制
      // - `takeover`: 接管模式,由 `Tabs` 插件处理路由组件缓存,将禁用 `KeepAlive` 自动收集和清理模式
      keepAliveMode: 'takeover',

      // 其他不常更改参数可查看详细注释
      // ...
    }),
  ],
})

使用方式

这里使用 ElementPlus 展示如何渲染标签页列表和跳转。

<!-- layout/components/Tabs.vue -->
<script setup lang="ts">
import type { TabsPaneContext } from 'element-plus'
import { onMounted } from 'vue'
import { useAuthStore } from '@/stores/auth'
import { useAppSDK } from 'vue-app-sdk'
import { useRoute } from 'vue-router'

const { tabs } = useAppSDK()
const authStore = useAuthStore()

const route = useRoute()
function initTabs() {
  // 如果标签页列表为空则根据过滤权限页面元数据初始化数据
  if (tabs.pagesIsEmpty()) {
    tabs.setAffixTabsPagesByRawPages(authStore.authPages)
  }
  else {
    // 更新 keepAlive 缓存
    tabs.updateKeepAlive()
  }

  // 如果当前没有激活的标签页则添加自身路由
  if (!tabs.active.value) tabs.addOne(route)
}

// 初始化标签页列表
initTabs()

// 处理标签页点击事件
function handleTabClick(tabItem: TabsPaneContext) {
  // 设置激活的标签页
  tabs.setActive(tabItem.paneName)
}
</script>

<template>
  <ElTabs
    v-model="tabs.active.value"
    type="card"
    @tab-click="handleTabClick"
    @tab-remove="tabs.removeOne"
  >
    <ElTabPane
      v-for="item in tabs.pages.value"
      :key="item.id"
      :label="item.title"
      :name="item.id"
      :closable="!item.isAffix"
    >
      <template #label>
        <ElText>{{ item.title }}</ElText>
      </template>
    </ElTabPane>
  </ElTabs>
</template>

标签页操作

这里使用 ElementPlus 展示如何渲染标签页操作选项。

<!-- layout/components/TabsMoreButton.vue -->
<script setup lang="ts">
import { ArrowDown } from '@element-plus/icons-vue'
import { useAppSDK } from 'vue-app-sdk'

const { tabs } = useAppSDK()
</script>

<template>
  <ElDropdown
    class="more-button"
    trigger="click"
  >
    <ElButton
      class="trigger"
      :text="true"
    >
      <ElIcon :size="20">
        <ArrowDown />
      </ElIcon>
    </ElButton>
    <template #dropdown>
      <ElDropdownMenu>
        <ElDropdownItem
          :disabled="!tabs.canRemove(active)"
          @click="tabs.removeActive()"
        >
          关闭当前
        </ElDropdownItem>
        <ElDropdownItem
          :disabled="!tabs.canRemoveSide('left')"
          @click="tabs.removeBySide('left')"
        >
          关闭左侧
        </ElDropdownItem>
        <ElDropdownItem
          :disabled="!tabs.canRemoveSide('right')"
          @click="tabs.removeBySide('right')"
        >
          关闭右侧
        </ElDropdownItem>
        <ElDropdownItem
          divided
          :disabled="!tabs.canRemoveOther()"
          @click="tabs.removeOther()"
        >
          关闭其他
        </ElDropdownItem>
        <ElDropdownItem
          :disabled="!tabs.canRemoveAll()"
          @click="tabs.removeAll()"
        >
          全部关闭
        </ElDropdownItem>
      </ElDropdownMenu>
    </template>
  </ElDropdown>
</template>

<style lang="scss" scoped>
.more-button {
  border-left: 1px solid var(--el-color-info-light-5);

  .trigger {
    width: 100%;
    height: 100%;
    border: none;
    border-radius: 0;
  }
}
</style>

后面只需调整 Tabs.vue 组件结合 TabsMoreButton.vue 即可!

动态开启标签页

// Main.vue
import { ref, watch } from 'vue'
import { useAppSDK } from 'vue-app-sdk'

const { tabs } = useAppSDK()

// 启用标签页变量
const enableTabs = ref(true)
watch(enableTabs, (value) => {
  // 启用时开启标签页的自动收集模式
  if (value) {
    tabs.enableAuto()
  }
  else {
    // 关闭时禁用标签页的自动收集模式并强制清理所有的标签页数据
    tabs.disableAuto()
    tabs.removeAll(true)
  }
}, { immediate: true })

清理缓存

注册 AppSDK 后会自动注册 cleanup 事件,也可通过下面代码手动清理。

// ...

// 默认会在关闭标签页时处理 beforeRemove 回调,传入 true 可强制关闭所有标签页
SDK.tabs.removeAll(true)