2 13.封装标签导航组件实现

  • 封装导航组件

src/composables/useTabList.js

import { ref } from 'vue'
import { useRoute, onBeforeRouteUpdate } from 'vue-router'
import { useCookies } from '@vueuse/integrations/useCookies'
import { router } from '~/router';


export function useTabList() {
    const route = useRoute()
    const cookie = useCookies()
    const activeTab = ref(route.path)
    const tabList = ref([
        {
            title: '后台首页',
            path: '/',
        },
    ])

    // 添加标签导航
    function addTab(tab) {
        let noTab = tabList.value.findIndex(t => t.path == tab.path) == -1
        if (noTab) {
            tabList.value.push(tab)
        }

        cookie.set('tabList', tabList.value)
    }


    // 初始化标签导航列表
    function initTabList() {
        let tbs = cookie.get('tabList')
        if (tbs) {
            tabList.value = tbs
        }
    }
    initTabList()

    // 点击导航或者标签路由之前方法
    onBeforeRouteUpdate((to, from) => {
        activeTab.value = to.path
        addTab({
            title: to.meta.title,
            path: to.path
        })
    })

    // 点击导航或者标签路由之后方法
    const changeTab = (t) => {
        activeTab.value = t
        router.push(t)
    }


    // 移除标签
    const removeTab = (t) => {
        let tabs = tabList.value
        let a = activeTab.value
        if (a == t) {
            tabs.forEach((tab, index) => {
                if (tab.path == t) {
                    const nextTab = tabs[index + 1] || tabs[index - 1]
                    if (nextTab) {
                        a = nextTab.path
                    }
                }
            })
        }
        activeTab.value = a
        tabList.value = tabList.value.filter(tab => tab.path != t)

        cookie.set('tabList', tabList.value)
    }


    // 关闭出发方法
    const handleClose = (c) => {
        if (c == "clearAll") {
            activeTab.value = '/'
            tabList.value = [{
                title: '后台首页',
                path: '/'
            }]
        } else if (c == "clearOther") {
            tabList.value = tabList.value.filter(tab => tab.path == '/' || tab.path == activeTab.value)
        }

        cookie.set("tabList", tabList.value)
    }


    return {
        activeTab,
        tabList,
        changeTab,
        removeTab,
        handleClose
    }
}
  • Tag组件

src/layouts/components/FTagList.vue

<template>
    <div class="f-tag-list" :style="{ left:$store.state.asideWidth }">

        <el-tabs
            v-model="activeTab"
            type="card"
            class="flex-1"
            @tab-remove="removeTab"
            style="min-width: 100px"
            @tab-change="changeTab"
        >
            <el-tab-pane
                :closable="item.path !='/'"
                v-for="item in tabList"
                :key="item.path"
                :label="item.title"
                :name="item.path"
                >
            </el-tab-pane>
        </el-tabs>

        <span class="tag-btn">
            <el-dropdown @command="handleClose">
                <span class="el-dropdown-link">
                <el-icon>
                    <arrow-down />
                </el-icon>
                </span>
                <template #dropdown>
                <el-dropdown-menu>
                    <el-dropdown-item command="clearOther">关闭其他</el-dropdown-item>
                    <el-dropdown-item command="clearAll">关闭全部</el-dropdown-item>
                </el-dropdown-menu>
                </template>
            </el-dropdown>
        </span>
    </div>

    <div style="height:44px;"></div>
</template>


<script setup>
import { useTabList } from '~/composables/useTabList'

const {
        activeTab,
        tabList,
        changeTab,
        removeTab,
        handleClose
    } = useTabList()
</script>

<style scoped>
.f-tag-list {
    @apply fixed bg-gray-100 flex items-center px-2;
    top: 64px;
    right: 0;
    height: 44px;
    z-index: 100;
}

.tag-btn {
    @apply bg-white rounded ml-auto flex items-center justify-between px-1;
    height: 32px;
}


:deep(.el-tabs__header) {
    border: 0 !important;
    @apply mb-0;
}


:deep(.el-tabs__nav){
    border: 0 !important;
}

:deep(.el-tabs__item){
    border: 0 !important;
    height: 32px;
    line-height: 32px;
    @apply bg-white mx-1 rounded;
}

:deep(.el-tabs__nav-next),:deep(.el-tabs__nav-prev){
    line-height: 32px;
    height: 32px;
}

:deep(.is-disabled){
    cursor: not-allowed;
    @apply text-gray-300;
}
</style>
  • 优化点击速度

由于每次请求都会请求一次个人信息,所有优化一下速度

src/permission.js 全局守卫设置e

import { router, addRoutes }  from './router'
import { getToken } from '~/composables/auth'
import { toast,showFullLoading,hideFullLoading } from '~/composables/util'
import store from '~/store'


// 全局前置守卫
let hasGetInfo = false
router.beforeEach(async (to, from, next) => {

    ...

    // 如果用户登录了,自动获取用户信息,并存储再 vuex 当中
    let hasNewRoutes = false
    if(token && !hasGetInfo){
        let { menus } = await store.dispatch("getinfo")
        hasGetInfo = true
        hasNewRoutes = addRoutes(menus)
    }

     ...
  })


router.afterEach((to,from)=> hideFullLoading())
  • 效果图

Last updated