Vue.js组件——标签页组件

来源:互联网 发布:python bt文件下载 编辑:程序博客网 时间:2024/06/05 15:09

按照《Vue.js实战》的指导,制作了一个标签页的组件,并按照课后练习的要求,添加了一个小功能:
给pane组件新增一个prop:closable的布尔值,支持是否关闭这个pane,如果开启,tabs上会增加一个关闭按钮,可以关闭对应的标签。
做这个练习的时候,主要有两个关键点:

1)如何根据closable的值来动态显示/隐藏关闭按钮

解决第一个关键点时,我一开始使用了v-show,如下所示(下面的代码省略了其他不相关的代码),结果不能成功:

Vue.component('tabs',{   template:'\    <div class="tabs">\        <div class="tabs-bar">\            <div\                :class="tabCls(item)"\                v-for="(item,index) in navList"\                @click="handleChange(index)">{{ item.label }} \                <span :v-show="isShown(item)" @click="deleteTab(index,event)">×</span>\            </div>\        </div>\        <div class="tabs-content">\            <slot></slot>\        </div>\    </div>',    methods:{        isShown:function(item){            console.log(item.closable);            var flag = item.closable == 'true';            console.log("-------------------------");            console.log(flag);            return flag;        }    }});

后来查阅书籍,发现v-show不能在template中使用。又考虑到这个关闭按钮只会在刚加载时需要渲染,后面都不会改变其显示/隐藏效果,于是决定用v-if,然后奏效了,代码如下(同样省略了不相关代码):

Vue.component('tabs',{   template:'\    <div class="tabs">\        <div class="tabs-bar">\            <div\                :class="tabCls(item)"\                v-for="(item,index) in navList"\                @click="handleChange(index)">{{ item.label }} \                <span v-if="isShown(item)" @click="deleteTab(index,event)">×</span>\            </div>\        </div>\        <div class="tabs-content">\            <slot></slot>\        </div>\    </div>',    methods:{        isShown:function(item){            console.log(item.closable);            var flag = item.closable == 'true';            console.log("-------------------------");            console.log(flag);            return flag;        }    }});

2)如何实现关闭标签页的功能

鉴于Vue.js的核心思想是数据驱动DOM,实现关闭标签页的功能,只需要做到两点:
1、删除将navList中对应的元素(navList是标签页标题的集合);
2、通过改变currentValue的值,隐藏标签页对应的内容。
关闭标签页的方法deleteTab的代码如下:

deleteTab:function(index,event){            //添加关闭功能,即是将navList中对应的元素删除即可。            //存在一个问题,当关闭了所有的tab后,tab-content中依然会显示内容            //那么,我们不能仅仅只删除navList中对应的元素,还应该将pane中对应的内容也隐藏            //可以通过改变currentValue的值来实现            if(this.navList[index].name === this.currentValue){                if(index > 0){                    this.currentValue = this.navList[index - 1].name;                    this.navList.splice(index,1);                    event.stopPropagation();//阻止冒泡,避免触发handleChange(index)方法                }                else{                    this.navList.splice(index,1);                    event.stopPropagation();                    if(this.navList.length > 0){                       this.currentValue = this.navList[0].name;                    }                       else{                        this.currentValue = '';                    }                  }            }            else{                this.navList.splice(index,1);                event.stopPropagation();//阻止冒泡,避免触发handleChange(index)方法                if(this.navList.length === 0){                    this.currentValue = '';                }            }        }

完整代码

最后,附上完整的代码。
这个组件总共包含如下四个文件:
1、index.html:入口页面;
2、style.csss:样式表;
3、tabs.js:标签页外层的组件tabs,其包含一个标签页标题,一个用于嵌套pane组件的slot;
4、pane.js:标签页嵌套的组件pane,这个组件的作用在于包含具体的标签页的内容。

index.html

<!DOCTYPE html><html>    <head>        <meta charset="utf-8">        <title>标签页组件</title>        <link rel="stylesheet" type="text/css" href="../css/style.css">    </head>    <body>        <h2>练习</h2>        <p>给pane组件新增一个prop:closable的布尔值,支持是否关闭这个pane,如果开启,tabs上会增加一个关闭按钮,可以关闭对应的标签</p>        <div id="app" v-cloak>            <tabs v-model="activeKey" @on-click="doSomeThing">                <pane label="标签一" name="1" closable=true>                    标签一的内容                </pane>                <pane label="标签二" name="2" closable=true>                    标签二的内容                </pane>                <pane label="标签三" name="3" closable=false>                    标签三的内容                </pane>            </tabs>        </div>        <script src="../js/vue.min.js"></script>        <script src="../js/pane.js"></script>        <script src="../js/tabs.js"></script>        <script type="text/javascript">            var app = new Vue({                el: '#app',                data: {                    activeKey:'1'                },                methods:{                    doSomeThing:function(){                        //console.log("Do something!");                    }                }            });        </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: #ffffff;    border: 1px solid #d7dde4;    cursor: pointer;    position: relative;}.tabs-tab-active {    color: #3399ff;    border-top: 1px solid #3399ff;    border-bottom: 1px solid #ffffff;}.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

Vue.component('tabs',{   template:'\    <div class="tabs">\        <div class="tabs-bar">\            <div\                :class="tabCls(item)"\                v-for="(item,index) in navList"\                @click="handleChange(index)">{{ item.label }} \                <span v-if="isShown(item)" @click="deleteTab(index,event)">×</span>\            </div>\        </div>\        <div class="tabs-content">\            <slot></slot>\        </div>\    </div>',    props:{        value:{            type:[String,Number]        },    },    data:function(){        return {            currentValue:this.value,            navList:[]        }    },    methods:{        tabCls:function(item){            return [                'tabs-tab',                {                    'tabs-tab-active':item.name === this.currentValue                }            ];        },        getTabs:function(){            return this.$children.filter(function(item){                return item.$options.name === 'pane';            });        },        updateNav:function(){            this.navList = [];            var _this = this;            this.getTabs().forEach(function(pane,index){                _this.navList.push({                    label:pane.label,                    name:pane.name || index,                    closable:pane.closable                });                if(!pane.name){                    pane.name = index;                }                if(index === 0){                    if(!_this.currentValue){                        _this.currentValue = pane.name || index;                    }                }            });            this.updateStatus();        },        updateStatus: function(){            var tabs = this.getTabs();            var _this = this;            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);        },        deleteTab:function(index,event){            //添加关闭功能,即是将navList中对应的元素删除即可。            //存在一个问题,当关闭了所有的tab后,tab-content中依然会显示内容            //那么,我们不能仅仅只删除navList中对应的元素,还应该将pane中对应的内容也隐藏            //可以通过改变currentValue的值来实现            if(this.navList[index].name === this.currentValue){                if(index > 0){                    this.currentValue = this.navList[index - 1].name;                    this.navList.splice(index,1);                    event.stopPropagation();//阻止冒泡,避免触发handleChange(index)方法                }                else{                    this.navList.splice(index,1);                    event.stopPropagation();                    if(this.navList.length > 0){                       this.currentValue = this.navList[0].name;                    }                       else{                        this.currentValue = '';                    }                  }            }            else{                this.navList.splice(index,1);                event.stopPropagation();//阻止冒泡,避免触发handleChange(index)方法                if(this.navList.length === 0){                    this.currentValue = '';                }            }        },        isShown:function(item){            console.log(item.closable);            var flag = item.closable == 'true';            console.log("-------------------------");            console.log(flag);            return flag;        }    },    watch:{        value:function(val){            this.currentValue = val;        },        currentValue:function(){            this.updateStatus();        }    }});

pane.js

Vue.component('pane',{    name:'pane',    template:'\        <div class="pane" v-show="show">\            <slot></slot>\        </div>',    props:{        name:{            type:String        },        label:{            type:String,            default:''         },         closable:{              type:Boolean,              default:true         }    },    data:function(){        return {            show:true        }    },    methods:{        updateNav:function(){            this.$parent.updateNav();        }    },    watch:{        label:function(){            this.updateNav();        }    },    mounted:function(){        this.updateNav();    }});
原创粉丝点击