效果图:
index.html
<link rel="stylesheet" type="text/css" href="style.css"/>
<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="vue.min.js"></script>
<script src="pane.js"></script>
<script src="tab.js"></script>
<script>
var app=new Vue({
el:"#app",
data:{
activekey:"1"
}
})
</script>
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}} \
</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(){
return this.$children.filter(function(item){
return item.$options.name==='pane';
})
},
updataNav(){
this.navList=[];
var _this=this;
this.getTabs().forEach(function(pane,index){
_this.navList.push({
label:pane.label,
name:pane.name||index
});
if(!pane.name){
pane.name=index;
}
if(index==0){
if(!_this.currentValue){
_this.currentValue=pane.name||index;
}
}
});
this.updataStatus();
},
updataStatus(){
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)
}
},
watch:{
value:function(val){
this.currentValue=val;
},
currentValue:function(){
this.updataStatus();
}
}
})
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:''
}
},
data:function(){
return {
show:true
}
},
methods:{
updataNav(){
this.$parent.updataNav();
}
},
watch:{
label(){
this.updataNav();
}
},
mounted(){
this.updataNav();
}
})
style.css
[v-cloak]{
display:none;
}
.tabs{
font-size:14px;
color:#657180;
}
.tabs-bar:after{
content:'';
display:block;
width:100%;
height:1px;
background:#dedde4;
margin-top:-1px;
}
.tabs-bar{
display:inline-block;
padding:4px 16px;
margin-right:6px;
background:#fff;
border:1px solid #d7dde4;
cursor:pointer;
position:relative;
}
.tabs-bar div{
float:left;
}
.tabs-tab-active{
color:#3399ff;
border-top:1px solid #3399ff;
border-bottom:1px solid #fff;
}
.tabs-tab-active:after{
content:'';
display:block;
height:1px;
background:#3399ff;
position:absolute;
top:0;
left:0;
right:0;
}
.tabs-content{
padding:8px 0;
}