Tabs

根据浏览记录自动记录历史 tab

使用

基础用法

将自动记录路由变化

<template>
  <pro-tabs />
</template>

风格类型

<template>
  <pro-radio-button v-model="type" :data="data" style="margin-bottom: 16px" />
  <pro-tabs ref="childTabs" :type="type" />
</template>

<script>
import { defineComponent, inject, onMounted, ref, shallowRef } from 'vue'

export default defineComponent({
  setup() {
    const tabs = inject('tabs', undefined) // Get top-level `Tabs` inject
    const childTabs = shallowRef({})
    const type = ref('')
    const data = [
      { value: '', label: 'Default' },
      { value: 'card', label: 'Card' },
      { value: 'border-card', label: 'BorderCard' },
    ]

    onMounted(() => {
      asyncList()
    })

    function asyncList() {
      if (tabs) {
        childTabs.value.list = tabs.value.list
      }
    }

    return {
      childTabs,
      type,
      data,
    }
  },
})
</script>

保持隐藏路由

默认自动关闭具有 hidden 标识的路由,可以通过 keep-hidden-route 阻止这种行为

<template>
  <pro-tabs keep-hidden-route />
</template>

外部调用关闭

通过 ref 绑定 Tabs 进而通过外部调用关闭 tab 页

const tabs = inject('tabs') 由顶层 Layout 注入 参考

<template>
  <pro-tabs ref="childTabs" style="margin-bottom: 15px" />
  <el-button @click="childTabs.close('/en-US/guide/')">
    Close homepage
  </el-button>
  <el-button @click="childTabs.closeOther"> Close otherpage </el-button>
  <el-button @click="asyncList"> async </el-button>
</template>

<script>
import { defineComponent, inject, onMounted, shallowRef } from 'vue'

export default defineComponent({
  setup() {
    const tabs = inject('tabs', undefined) // Get top-level `Tabs` inject
    const childTabs = shallowRef({})

    onMounted(() => {
      asyncList()
    })

    function asyncList() {
      if (tabs) {
        childTabs.value.list = tabs.value.list
      }
    }

    return {
      childTabs,
      asyncList,
    }
  },
})
</script>

增加标签之前

通过 before-add 钩子在标签增加前执行一些操作,若返回 false 或者返回 Promise 且被 reject,则阻止增加标签

<template>
  <pro-tabs :before-add="beforeAdd" />
</template>

<script>
import { defineComponent, nextTick } from 'vue'
import { ElNotification } from 'element-plus'

export default defineComponent({
  setup() {
    function beforeAdd(e) {
      console.log('Tabs-before-add', e)
      nextTick(() => {
        ElNotification({
          type: 'success',
          title: 'Success',
          message: 'Come from before-add',
        })
      })
    }

    return {
      beforeAdd,
    }
  },
})
</script>

切换标签之前

通过 before-leave 钩子在标签切换前执行一些操作,若返回 false 或者返回 Promise 且被 reject,则阻止切换标签

<template>
  <pro-tabs ref="childTabs" :before-leave="beforeLeave" />
</template>

<script>
import { defineComponent, inject, onMounted, shallowRef } from 'vue'
import { ElNotification } from 'element-plus'

export default defineComponent({
  setup() {
    const tabs = inject('tabs', undefined) // Get top-level `Tabs` inject
    const childTabs = shallowRef({})

    onMounted(() => {
      asyncList()
    })

    function asyncList() {
      if (tabs) {
        childTabs.value.list = tabs.value.list
      }
    }

    function beforeLeave(activeName, oldActiveName) {
      console.log({ activeName, oldActiveName })
      ElNotification({
        type: 'success',
        title: 'Success',
        message: 'Come from before-leave',
      })
      return false
    }

    return {
      childTabs,
      beforeLeave,
    }
  },
})
</script>

使用插槽

通过 label 自定义标签页的标题内容

提示

自 1.4.0 起,label 插槽参数更改为 RouteLocationNormalizedLoaded 类型

<template>
  <pro-tabs>
    <template #label="{ icon, meta }">
      <div class="tabs-label-content">
        <el-icon :size="16" class="label-icon">
          <component :is="icon" />
        </el-icon>
        <span>{{ meta.title.toUpperCase() }}</span>
      </div>
    </template>
  </pro-tabs>
</template>

<style scoped>
.tabs-label-content {
  display: flex;
  align-items: center;
  justify-content: center;
}
.tabs-label-content .label-icon {
  margin-right: 6px;
}
</style>

右键菜单

自 1.4.0 起,Tabs 组件支持通过配置 contextmenu 展示右键菜单

提示

如果启用 refresh 功能,需要额外配置 refreshPath 属性,并向 vue-router 中增加对应路由信息

参考路由配置
import {
  createMemoryHistory,
  createRouter as _createRouter,
  createWebHistory,
  Router,
} from 'vue-router'
import BaseLayout from '../layout/Layout.vue'
import enUS from './en-US'
import zhCN from './zh-CN'
import dev from './dev'
import { langs } from '../utils/index'
import type { RouteRecordRaw } from 'vue-router'

const routes: RouteRecordRaw[] = [
  {
    path: '/dev/refresh',
    component: BaseLayout,
    meta: { hidden: true },
    children: [
      {
        path: '/dev/refresh',
        component: () => import('../layout/Refresh.vue'),
        meta: { hidden: true },
      },
    ],
  },
  ...enUS,
  ...zhCN,
]

export function createRouter(): Router {
  const baseUrl = import.meta.env.BASE_URL
  const router = _createRouter({
    history: import.meta.env.SSR
      ? createMemoryHistory(baseUrl)
      : createWebHistory(baseUrl),
    routes: import.meta.env.MODE === 'production' ? routes : routes.concat(dev),
    scrollBehavior(to, from, savedPosition) {
      if (to.hash) {
        return new Promise((resolve) => {
          setTimeout(() => {
            resolve({
              el: to.hash,
              top: 94,
              behavior: 'smooth',
            })
          }, 600) // Wait for the animation to finish
        })
      } else if (savedPosition) {
        return savedPosition
      } else {
        return { top: 0 }
      }
    },
  })

  router.beforeEach((from) => {
    if (from.path === '/') {
      const lang = navigator.language
      return langs.find((item) => item.key === lang) ? lang : langs[0].key
    }
  })

  return router
}
<template>
  <pro-tabs ref="childTabs" contextmenu refresh-path="/dev/refresh" />
</template>

<script>
import { defineComponent, inject, onMounted, shallowRef } from 'vue'

export default defineComponent({
  setup() {
    const tabs = inject('tabs', undefined) // Get top-level `Tabs` inject
    const childTabs = shallowRef({})

    onMounted(() => {
      asyncList()
    })

    function asyncList() {
      if (tabs) {
        childTabs.value.list = tabs.value.list
      }
    }

    return {
      childTabs,
    }
  },
})
</script>

配置

参数说明类型可选值默认值
type风格类型stringcard / border-card-
tab-position标签位置stringtop / right / bottom / lefttop
stretch标签的宽度是否自撑开boolean-false
keep-hidden-route是否保持具有 hidden 标识的路由存在,默认自动关闭boolean-false
contextmenu右键菜单配置boolean / object ({ refresh?: boolean, left?: boolean, right?: boolean, others?: boolean })-false
refreshPath刷新路径,用于刷新标签页string--
before-add增加标签之前的钩子,若返回 false 或者返回 Promise 且被 reject,则阻止增加。Function({ route, oldPath, list, close, closeOther })--
before-leave切换标签之前的钩子,若返回 false 或者返回 Promise 且被 reject,则阻止切换。Function(activeName, oldActiveName)--

事件

事件名说明类型
tab-clicktab 被选中时触发(pane: TabsPaneContext, ev: Event) => void
tab-changeactiveName 变动后触发(name: TabPaneName) => void
tab-remove关闭时触发(name: TabPaneName) => void

方法

方法名说明类型
list浏览记录Ref<RouteLocationNormalizedLoaded[]>
close从 tabs 中关闭指定路由的页面(path: string) => void
closeOther从 tabs 中关闭除当前路由的其它路由() => void

插槽

名称说明类型
label自定义标题内容RouteLocationNormalizedLoaded