一、目标效果

        此篇博客重在分享vue3实现动态增减标签页实现方法,项目界面可能比较丑、性能优化也没有做到极致,请多多包涵,此文章核心是实现动态增减标签的逻辑。本来想要用视频展示效果的,结果csdn上传视频一致审核中,只能截几个图给你们看看了。

        项目的注释写的比较清晰,自己将项目跑起来,慢慢内化成自己的东西,项目用到数组的方法、vue3语法、vue-router、vuex、动态绑定class、三元运算符知识

 

 

 

二、运行项目

(1)项目地址:git clone https://gitee.com/liu-wenxin/operateTags.git

(2)npm install 安装依赖

(3)npm run serve 运行项目

(4)http://localhost:8080访问项目即可

三、实现过程

(1)界面样式

<template>
    <!-- 首页 -->
    <div>
        <el-container>
            <!-- 头部 -->
            <el-header>Header</el-header>
            <el-container>
                <!-- 侧边栏 -->
                <el-aside width="200px">
                    <el-menu active-text-color="#6787df" background-color="#252b41" default-active="1" text-color="#fff"
                        router>
                        <el-menu-item index="1">
                            <span>页面1</span>
                        </el-menu-item>
                        <el-menu-item index="2">
                            <span>页面2</span>
                        </el-menu-item>
                        <el-menu-item index="3">
                            <span>页面3</span>
                        </el-menu-item>
                        <el-menu-item index="4">
                            <span>页面4</span>
                        </el-menu-item>
                        <el-menu-item index="5">
                            <span>页面5</span>
                        </el-menu-item>
                        <el-menu-item index="6">
                            <span>页面6</span>
                        </el-menu-item>
                    </el-menu>
                </el-aside>
                <el-main>
                    <!-- tags栏 -->
                    <div class="tagList" v-if="showTags">
                        <ul>
                            <li v-for="(item, index) in tagsList" :class="{ 'active': isActive(item.path) }"
                                :key="index">
                                <router-link :to="item.path" class="tag">
                                    {{ item.name }}
                                </router-link>
                                <el-icon @click="closeTags(index)" class="close">
                                    <Close />
                                </el-icon>
                            </li>
                        </ul>
                        <el-dropdown>
                            <el-button type="primary">
                                标签选项
                                <el-icon>
                                    <ArrowDown />
                                </el-icon>
                            </el-button>
                            <template #dropdown>
                                <el-dropdown-menu>
                                    <el-dropdown-item @click.native="closeOther">关闭其它</el-dropdown-item>
                                    <el-dropdown-item @click.native="closeAll">关闭所有</el-dropdown-item>
                                </el-dropdown-menu>
                            </template>
                        </el-dropdown>
                    </div>

                    <!-- 内容渲染区 -->
                    <keep-alive>
                        <router-view></router-view>
                    </keep-alive>
                </el-main>
            </el-container>
        </el-container>
    </div>
</template>
<script>
import { useStore } from 'vuex'
import { useRoute, useRouter, onBeforeRouteUpdate } from 'vue-router'
import { computed } from 'vue'
export default {
    setup() {
        const route = useRoute();
        const router = useRouter();

        const store = useStore();

        // 获取vuex里面所有的tag
        const tagsList = computed(() => store.state.tagsList)

        // 关闭一个标签
        const closeTags = index => {
            //获取删除的标签的信息
            const delItem = tagsList.value[index];

            //让vuex删除指定的标签
            store.commit('delTagsItem', { index });

            // 如果关闭的是当前标签且标签不止一个,则激活前一个标签,如果关闭的是不是当前标签且标签不止一个,则激活后一个标签
            const item = tagsList.value[index] ? tagsList.value[index] : tagsList.value[index - 1];
            if (item) {
                // 如果删除的是当前标签,则跳往上面item判断好的目标标签的地址
                if (isActive(delItem.path)) {
                    router.push(item.path)
                }
            } else {
                // 如果tagsList只有一个标签,删除了标签就没有标签,则跳转到首页
                router.push("/");
            }
        }

        // 设置标签
        const setTags = (route) => {
            const isExist = tagsList.value.some(item => item.path === route.fullPath)
            if (!isExist) {
                // tag超过5个就删除第一个
                if (tagsList.value.length >= 5) {
                    store.commit('delTagsItem', { index: 0 })
                }
                store.commit('setTagsItem', {
                    name: route.name,
                    path: route.fullPath,
                    title: route.meta.title
                })
            }
        }

        //首次加载页面的时候便添加标签页
        setTags(route);

        // 监听路由变化,在当前页面即将要离开的时候触发
        onBeforeRouteUpdate((to) => {
            setTags(to)
        })

        // 关全部标签
        const closeAll = () => {
            store.commit('clearTags');
            router.push("/");
        }

        // 关闭其它标签
        const closeOther = () => {
            const curItem = tagsList.value.filter(item => {
                return item.path === route.fullPath;
            })
            store.commit('clearTagsOther', curItem);
        }
        // 显示标签栏
        const showTags = computed(() => tagsList.value.length > 0)

        // 判断tag是否被激活,如果被激活就高亮当前标签
        const isActive = path => path === route.fullPath

        return { tagsList, isActive, showTags, setTags, closeAll, closeOther, closeTags }
    }
}
</script>
<style scoped>
.active,
.active .tag {
    background-color: #409eff !important;
    color: #fff !important;
}

.close:hover {
    background-color: #ffffff;
    color: #409eff;
    cursor: pointer;
}

.tag {
    margin: 0 5px;
    height: 100%;
    font-size: 18px;
}

.tagList ul li {
    float: left;
    margin: 3px 5px 2px 3px;
    height: 23px;
    border: 1px solid #e9eaec;
    line-height: 23px;
    border-radius: 3px;
    text-align: center;
    background-color: #fff;
    color: #666;
}

.tagList {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 100%;
    background-color: #ffffff;
}

.el-main {
    margin: 0;
    padding: 0;
    background: #efefef;
}

.el-menu-item {
    display: flex;
    justify-content: center;
    font-size: 16px;
}

.el-menu {
    height: 100%;
}

.el-aside {
    height: calc(100vh - 60px);
}

/* 头部样式 */
.el-header {
    height: 60px;
    line-height: 60px;
    background-color: #252b41;
    color: #ffffff;
    text-align: center;
    font-size: 20px;
}
</style>

(2)vue-router路由

import { createRouter, createWebHistory } from 'vue-router'
const routes = [
  {
    path: '/',
    redirect: '/index',
  },
  {
    path: '/index',
    name: 'index',
    redirect: '/1',
    meta: {
      title: '首页'
    },
    component: () => import( /* webpackChunkName: "index" */ "../views/index.vue"),
    children: [
      {
        path: '/1',
        name: '1',
        meta: {
          title: '页面1'
        },
        component: () => import( /* webpackChunkName: "1" */ "../views/1/1.vue")
      },
      {
        path: '/2',
        name: '2',
        meta: {
          title: '页面2'
        },
        component: () => import( /* webpackChunkName: "2" */ "../views/2/2.vue")
      },
      {
        path: '/3',
        name: '3',
        meta: {
          title: '页面3'
        },
        component: () => import( /* webpackChunkName: "3" */ "../views/3/3.vue")
      },
      {
        path: '/4',
        name: '4',
        meta: {
          title: '页面4'
        },
        component: () => import( /* webpackChunkName: "4" */ "../views/4/4.vue")
      },
      {
        path: '/5',
        name: '5',
        meta: {
          title: '页面5'
        },
        component: () => import( /* webpackChunkName: "5" */ "../views/5/5.vue")
      },
      {
        path: '/6',
        name: '6',
        meta: {
          title: '页面6'
        },
        component: () => import( /* webpackChunkName: "6" */ "../views/6/6.vue")
      }
    ]
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router

(3)vuex

import { createStore } from 'vuex'
export default createStore({
    state: {
        tagsList: []    //存放所有tag标签的数组
    },
    mutations: {
        // 删除一个tag
        delTagsItem(state, data) {
            state.tagsList.splice(data.index, 1)
        },
        //添加一个tag
        setTagsItem(state, data) {
            state.tagsList.push(data);
        },
        // 清除其他tag
        clearTagsOther(state, data) {
            state.tagsList = data;
        },
        // 清除所有tag
        clearTags(state) {
            state.tagsList = []
        }
    }
})

更多推荐