Vue.js入门 0x9 组件(Component)(6)Demo——标签页组件

本文介绍如何使用Vue.js创建一个可交互的标签页组件,包括HTML结构、CSS样式和JavaScript逻辑,实现平级内容的收纳与展示。

    标签页(即选项卡切换组件)是网页布局中经常用到的元素,常用于平级区域大块内容的收纳和展现

• index.html 入口页

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>标签页组件</title>
        <link rel="stylesheet" type="text/css" href="style.css">
    </head>
    <body>
        <div id="app" v-cloak>
            <tabs v-model="activeKey">
                <pane label="标签一" name="1">
                    标签一的内容
                </pane>
                <pane label="标签二" name="2">
                    标签二的内容
                </pane>
                <pane label="标签三" name="3">
                    标签三的内容
                </pane>
            </tabs>
        </div>
        <script src="https://cdn.jsdelivr.net/npm/vue"></script>
        <script src="pane.js"></script>
        <script src="tabs.js"></script>
        <script type="text/javascript">
            var app = new Vue({
                el:'#app',
                data:{
                    activeKey:'1'
                }
            })
        </script>
    </body>
</html>

• style.css 样式表

[v-cloak] {
    display: none;
}
.tabs {
    font-size: 14px;
    color: #657180;
}
.tabs-bar:after{
    content: '';
    display: block;
    width: 100%;
    height: 1px;
    background: #d7dde4;
    margin-top: -1px;
}
.tabs-tab{
    display: inline-block;
    padding: 4px 16px;
    margin-right: 6px;
    background: #fff;
    border: 1px solid #d7dde4;
    cursor: pointer;
    position: relative;
}
.tabs-tab-active{
    color: #3399ff;
    border-top: 1px solid #3399ff;
    border-bottom: 1px  solid #fff;
}
.tabs-tab-active:before{
    content: '';
    display: block;
    height: 1px;
    background: #3399ff;
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
}
.tabs-content{
    padding: 8px 0;
}

• tabs.js 标签页外层的组件 tabs

Vue.component('tabs',{
    /**
     * 当点击到pane对应的标签页标题按钮时 , 
     * 此pane的show值设置为true,否则应该是false
     */
    template:'\
        <div class="tabs">\
            <div class="tabs-bar">\
                <!--标签页标题,这里用v-for-->\
                <div class="tabs-bar">\
                    <div \
                        :class="tabCls(item)"\
                        v-for="(item,index) in navList"\
                        @click="handleChange(index)">\
                        {{item.label}}\
                    </div>\
                </div>\
            </div>\
            <div class="tabs-content">\
                <!--这里的slot就是嵌套的pane-->\
                <slot></slot>\
            </div>\
        </div>',
    props:{
        //这里value是为了可以使用v-model
        value:{
            type:[String,Number]
        }
    },
    data:function(){
        return {
            //用于渲染tabs标题
            //因为不能修改value,所以复制一份自己维护
            currentValue:this.value,
            navList:[]
        }
    },
    methods:{
        tabCls:function(item){
            return [
                'tabs-tab',
                {
                    'tabs-tab-active':item.name === this.currentValue
                }
            ]
        },
        getTabs(){
            //遍历子组件,得到所有的pane组件
            return this.$children.filter(function(item){
                return item.$options.name === 'pane';
            });
        },
        updateNav(){
            this.navList = [];
            //设置对this的引用,在function回调里,this指的并不是Vue实例
            var _this = this;

            //遍历提取label和name
            this.getTabs().forEach(function(pane,index){
                _this.navList.push({
                    label:pane.label,
                    name:pane.name||index
                });
                //如果没有给pane设置name,默认设置他的索引
                if(!pane.name) pane.name = index;
                //设置当前选中的tab索引
                if(index===0){
                    if(!_this.currentValue){
                        _this.currentValue = pane.name||index;
                    }
                }
            });
            this.updateStatus();
        },
        updateStatus(){
            var tabs = this.getTabs();
            var _this = this;
            //显示当前选中的tab对应的pane组件,隐藏没有选中的
            tabs.forEach(function(tab){
                return tab.show = tab.name === _this.currentValue;
            })
        },
        handleChange:function(index){
            var nav = this.navList[index];
            var name = nav.name;
            this.currentValue = name;
            this.$emit('input',name);
            this.$emit('on-click',name);
        }
    },
    watch:{
        value:function(val){
            this.currentValue = val;
        },
        currentValue:function(){
            this.updateStatus();
        }
    }
})

• pane. 标签页嵌套的组件 pane 

Vue.component('pane',{
    name:'pane',
    //设置data:show,并且用v-show指令来控制元素
    template:'\
        <div class="pane" v-show="show">\
            <slot></slot>\
        </div>',
    data:function(){
        return {
            show :true
        }
    },
    props:{
        name:{
            type:String
        },
        label:{
            type:String,
            default:''
        }
    },
    /**
     * prop: label 用户是可以动态调整的,所以在 pane 初始化
     * 及 label 更新时,都要通知父组件也更新,因为是独立组件,
     * 所以不能依赖像 bus.js 或Vuex 这样的状态管理办法,我们
     * 可以直接通过 this.$parent 访问 tabs 组件的实例来调用
     * 它的方法更新标题,该方法名暂定为 updateNav。
     */
    methods:{
        updateNav(){
            this.$parent.updateNav();
        }
    },
    watch:{
        label(){
            this.updateNav();
        }
    },
    /** 
     * 在生命周期 mounted,也就是 pane 初始化时,调用一遍 
     * tabs 的 updateNav 方法,同时监昕了prop: label,在 
     * label 更新时,同样调用 。
    */
    mounted(){
        this.updateNav();
    }
})

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值