《Vue.j实战》一书 p117 页练习 1 & 2 试做源代码
练习1 : 给pane 组件新增一个prop: closable 的布尔值, 来支持是否可以关闭这个pane , 如
果开启, 在tabs 的标签标题上会有一个关闭的按钮。
提示:初始化pane 时我们是在 mounted里通知的,关闭时,你会用到 beforedestroy
练习2 : 尝试在切换pane 的显示与隐藏时,使用滑动的动画。提示: 可以使用css 3 的transform:
translateX 。
IDE:VScode
项目由 main.js, app.vue, tabs.vue, pane.vue 4个文件构成。
main.js(略)
app.vue:
<template>
<div id="app">
<tabs v-model="activeKey" @on-click="handleOnclick" @on-close="handleOnclose">
<pane v-for="item in pans"
:key="item.mes"
:label="item.label"
:name="item.name"
:closable="item.closable"
>
{{item.mes}}
</pane>
</tabs>
</div>
</template>
<script>
import tabs from './components/tabs';
import pane from './components/pane';
export default {
components:{
tabs,
pane
},
data(){
return{
activeKey:'1',
pans:[
{
label:'标签一',
name:'1',
closable:true,
mes:'标签一的内容'
},
{
label:'标签二',
name:'2',
closable:true,
mes:'标签二的内容'
},
{
label:'标签三',
name:'3',
closable:true,
mes:'标签三的内容'
},
]
}
},
methods:{
handleOnclick(name){
},
handleOnclose(name){
var index=0;
for(var i=0;i<this.pans.length;i++) {
if(this.pans[i].name===name){
index=i;
break;
}
}
this.pans.splice(index,1);
}
},
beforeDestroy(){
this.$off('on-click',this.handleOnclick);
this.$off('on-close',this.handleOnclose);
}
}
</script>
tabs.vue(不含style)
<template>
<div class="tabs">
<div class="tabs-bar">
<div
:class="tabCls(item)"
v-for="(item, index) in navList"
:key="item.name"
@click.self="handleChange(index)">
{{ item.label }}
<template v-if="item.closable">
<a href="#" class="del" v-show="item.name===currentValue"
@click="handleClose(item,index)"
>x</a>
</template>
</div>
</div>
<div class="tabs-content">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
props:{
value:{
type:[String, Number]
},
},
data(){
return{
currentValue:this.value,
navList:[]
}
},
methods:{
handleClose(item,index){
this.navList.splice(index,1);
//console.log(this.navList.length);
var name = item.name;
//console.log(name)
this.handleChange(0)
this.$emit('on-close',name);
},
tabCls(item){
return[
'tabs-tab',
{
'tabs-tab-active':item.name === this.currentValue
}
]
},
getTabs(){
return this.$children.filter(function(item){
return item.$options.name === 'pane';
});
},
updateNav(){
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(){
var tabs = this.getTabs();
var _this = this;
tabs.forEach(function(tab){
return tab.show = tab.name === _this.currentValue;
})
},
handleChange(index){
if(this.navList.length){
var nav = this.navList[index];
var name = nav.name;
//console.log(name)
this.currentValue = name;
this.$emit('input',name);
this.$emit('on-click',name);
}
}
},
watch:{
value(val){
this.currentValue = val;
},
currentValue(){
this.updateStatus();
}
},
}
</script>
pane.vue
<template>
<transition name="fade" mode="out-in">
<div class="pane" v-show="show">
<slot></slot>
</div>
</transition>
</template>
<script>
export default {
name:'pane',
data(){
return{
show:true
}
},
props:{
name:{
type:String
},
label:{
type:String,
default:''
},
closable:{
type:Boolean,
default:true
}
},
methods:{
updateNav(){
this.$parent.updateNav();
}
},
watch:{
label(){
this.updateNav();
},
},
mounted(){
this.updateNav();
},
beforeDestroy(){
}
}
</script>
<style>
.pane{
display: inline-block;
}
.fade-enter-active,.fade-leave-active{
position: absolute;
transition: all .8s ease;
}
.fade-enter, .fade-leave-to{
transform: translateX(100px);
opacity: 0;
}
</style>

浙公网安备 33010602011771号