Vue3 implements dynamic increase and decrease of tab pages

1. Target effect

        This blog focuses on sharing how vue3 realizes the dynamic increase and decrease of tab pages. The project interface may be ugly, and the performance optimization is not perfect. Please bear with me. The core of this article is to realize the logic of dynamically increasing and decreasing labels. Originally, I wanted to use a video to show the effect, but as a result, the video uploaded by csdn is under unanimous review, so I can only screenshot a few pictures for you to see.

        The comments of the project are relatively clear. Run the project by yourself and slowly internalize it into your own things. The project uses array methods, vue3 syntax, vue-router, vuex, dynamic binding class, and ternary operator knowledge .

 

 

 

Two, run the project

(1) Project address: git clone https://gitee.com/liu-wenxin/operateTags.git

(2) npm install installation dependencies

(3) npm run serve to run the project

(4) http://localhost:8080 to access the project

3. Implementation process

(1) Interface style

<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 routing

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 = []
        }
    }
})

Guess you like

Origin blog.csdn.net/weixin_42375707/article/details/126217076