Vue 单页应用 路由多标签显示设计

设计样式如图:

 

设计规格描述:

  • 当某个路由被第一次打开时,在标签栏最后插入新标签,标签内容显示为 页面名称 + 关键参数值

  • 点击某个标签时,显示该标签的路由页面

  • 点击标签上的 × ,

    • 如果关闭的不是当前显示的标签页,直接删除这个标签就可以了

    • 如果关闭的是当前显示标签页,移除标签,并且后退到当前标签左边一个标签页

  • 打开同一个路由页面时,但是传的关键参数值不同,要插入新标签页

    (标签的唯一性key判断 = 路由元素数据meta.title(即路由页面标签名) + 路由跳转时携带的关键参数flag的值)

  • 浏览器刷新时,要自动将路由对应的标签添加到标签列表

 

代码主要实现在App.vue中

1、创建一个用于显示标签栏的组件 syClick.vue

<template>
    <div class="nav-click" v-if="$route.meta.showClick">
    	<ul :style="{'left':left + 'px'}">
    		<li class="nav-click-body" v-for="(item,index) in titles" :key="index" :title="item.title + item.flag"
            :class="{'nav-click-show':item.curr,'nav-click-default':!item.curr}" >
    			<div class="nav-click-body-title hidden" @click="changeTag(index)">{{item.title}} {{item.flag}}</div>
    			<div v-if="index!=0" class="nav-click-body-close" @click="removeTag(index)" :class="{'nav-click-body-show':item.curr,'nav-click-body-default':!item.curr}">×</div>
    		</li>
    		<div class="nav-click-right">
    			<sy-icon type="ellipsis-v" @click="UnNavClick" v-show="downIcon"></sy-icon>
    			<sy-icon type="remove" @click="CloseNavClick" v-show="!downIcon"></sy-icon>
    		</div>
    	</ul>
    </div>
</template>
<script>
    export default {
        name:'syClick',
        props:{
            // 路由组件页标签列表
            titles:{
                type: Array,
                default: ()=>{
			        return []
			    }
            }
        },
        data() {
            return {
				left: 0,
				clickHeight:50,
				downIcon: true,
            }
        },
        methods: {
			UnNavClick(){
				this.downIcon = !this.downIcon;
				this.clickHeight = 100
			},
            // 关闭所有的标签
			CloseNavClick(){
				this.downIcon = !this.downIcon;
				this.clickHeight = 50
                this.$emit('removeAll')
			},
            
            // 删除标签页
            removeTag(index) {
                this.$emit('removeTag', index);
            },

            // 切换标签页
            changeTag(index) {
                this.$emit('changeTag', index);
            }
        }
    }
</script>

 

2、在 App.vue 中挂载 syClick

App.vue 添加的相关代码

<template>
    <div>
        <div v-if="showApp == true">

            <!-- 头部 -->
            <sy-Head @on-refrash="reload"></sy-Head>

            <!-- 页标签显示栏 -->
            <sy-Click :titles="history_list" @removeAll="removeAll" @removeTag="removeTab" @changeTag="changeTag"></sy-Click>

            <!-- 导航 -->
            <sy-Nav @showPage="showPage"></sy-Nav>

            <!-- 页面主体内容 -->
            <keep-alive :include="cachedViews">
                <router-view v-if="$route.meta.keepAlive" :key="key"></router-view>
            </keep-alive>
            <router-view v-if="!$route.meta.keepAlive"></router-view>

        </div>

       
        
    </div>
</template>

<script>
    import syHead from './pages/syHead.vue'
    import syNav from './pages/syNav.vue'
    import home from './pages/home.vue'
    import syClick from './pages/syClick.vue'
    var _self;

    export default {
        name: 'app',
        components: {
            home,
            syNav,
            syHead,
            syClick
        },
        data() {
            return {
                // 标签列表
                history_list: [{
                    "title": "系统首页",
                    "path": "/home",
                    "curr": true,
                    "name": "home",
                    "flag": "",
                    _route: 'home', // 用来存储对应的路由只读实例对象
                    "route": {
                        "name": "home",
                        "meta": {
                            "title": "系统首页",
                            "requireAuth": false,
                            "showHeader": true,
                            "showNav": true,
                            "showtag": true,
                            "showPosition": true,
                            "showClick": true
                        },
                        "path": "/home",
                        "query": {},
                        "params": {},
                        "fullPath": "/home"
                    }
                }],

                // 标签点击标识,用来控制切换路由时的缓存处理方式,如果是标签点击切换的路由,直接用缓存不需要处理,并且也不需要执行新增标签方法,
                // 如果不是标签点击的,那么要判断当前缓存中是否已存在将要跳转的路由,如果存在要先清除缓存
                isTabClick: false,
                
                // 键盘control是否被按下,用来控制F5刷新组件
                isUpKey_Control: false,
            }
        },
        created() {
            _self = this;
        },
        mounted: function() {
            
            

        },

        methods: {

            // 重置缓存:先删除再添加
            resetKeepAive(route) {},

            // 删除缓存
            deleteKeepAive(route) {},

            // 添加缓存
            addKeepAive(route) {},

            // 导航菜单点击
            showPage: function(router) {
                this.isTabClick = false;
                this.$router.push(router);
            },

            // 切换标签
            changeTag(index) {
                this.isTabClick = true;
                for (let i = 0; i < this.history_list.length; i++) {
                    this.history_list[i].curr = false;
                }
                this.history_list[index].curr = true;
                this.$router.push(this.history_list[index]._route);
            },
            

            // 不是标签点击的 (isTabClick == false)
            addTab(to) {
                // 判断是否要显示tab标签
                if (!to.meta || !to.meta.showtag) {
                    return
                }
                let haved = false;

                // 查找要转向的路由是否在已打开的标签列表里
                for (let i = 0; i < this.history_list.length; i++) {
                    this.history_list[i].curr = false;

                    // 如果当前转向的路由,已经在标签列表中,则直接设置对应的标签焦点状态 (如果路由有参数的,需要判断关键参数flag)
                    if (this.history_list[i].title == to.meta.title &&
                        (!to.query || !to.query.flag || this.history_list[i].flag == to.query.flag)) {
                        
                        haved = true;
                        this.history_list[i].curr = true;

                        // 如果当前点击的路由已经在缓存列表中,则先清除缓存列表,再添加;
                        this.resetKeepAive(to)
                    }
                }

                if (!haved) {
                    this.history_list.push({
                        title: to.meta.title,
                        path: to.path,
                        curr: true,
                        name: to.name,
                        flag: (to.query && to.query.flag) ? to.query.flag : "",
                        _route: to,
                        route: {
                            "name": to.name,
                            "meta": to.meta || {},
                            "path": to.name || '',
                            "query": to.query || {},
                            "params": to.params || {},
                            "fullPath": to.fullPath || ""
                        }
                    })
                    this.addKeepAive(to)
                }
            },

            // 关闭标签-清除标签页组件缓存(清除缓存时是根据组件name来清除的,所以组件name相同的多个标签页缓存会同时被清除)
            removeTab(index) {
                this.isTabClick = true;
                let name = this.history_list[index].name;
                // 清除缓存
                this.deleteKeepAive(this.history_list[index]);
                
                // 如果关闭的是当前活动标签页
                if (this.history_list[index].curr) {
                    this.history_list.splice(index, 1)
                    if (this.history_list.length == 1) {
                        this.$router.push('/home');
                    } else {
                        this.$router.push(this.history_list[index - 1]._route);
                    }
                } else {
                    this.history_list.splice(index, 1);
                }
            },

            // 关闭所有标签
            removeAll() {
                for (let i = this.history_list.length - 1; i > 0; i--) {
                    this.history_list.splice(i, 1);
                }
                this.$store.commit('CLEAR_KEEP_ALIVE');
                this.$router.push('/home');
            },

            // 刷新当前标签页组件
            reload() {

                let _time = new Date().getTime()
                let index = this.curr_index;
                
                // 先清除当前的路由缓存
                this.deleteKeepAive(this.history_list[index]);

                // 路由查询参数中将_VERSION时间戳参数值刷新
                let query = this.history_list[index].route.query || {};
                if (query._VERSION) {
                    this.history_list[index].route.query._VERSION = _time;
                } else {
                    this.$set(this.history_list[index].route.query, '_VERSION', _time);
                }
                // 将参数数组转成地址参数字符串
                let queryString = '';
                this.$XEUtils.each(query, function(value, key) {
                    queryString += key + '=' + value + '&'
                })
                queryString = queryString.substring(0, queryString.length - 1);
                
                let fullPath = this.history_list[index].route.fullPath || '';
                this.history_list[index].route.fullPath = this.history_list[index].path + "?" + queryString
                this.$router.push(this.history_list[index].path + "?" + queryString)
            }

        },
        computed: {
            // 当前活动状态的标签页序号
            curr_index() {
                let index = -1;
                for (let i = 0; i < this.history_list.length; i++) {
                    if (this.history_list[i].curr == true) {
                        index = i;
                        break;
                    }
                }
                return index ;
            },
            // 需要缓存的路由组件列表
            cachedViews() {
                return this.$store.state.keepAliveList
            },
            // 路由组件的key,用于keepAlive缓存
            key() {
                return this.history_list[this.curr_index].route.fullPath
            },
        },
        watch: {
            // 监听路由变化,自动新增标签
            '$route': {
                handler(to, from) {

                    // 如果不是标签头点击跳转的路由,要新增标签头
                    if (this.isTabClick == false) {
                        this.addTab(to);
                    }
                    if (!to.query._VERSION) {
                        to.query._VERSION = new Date().getTime()
                    }
                    this.isTabClick = false
                },
                immediate: true
            }
        }
    }
</script>

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值