一个普通的tab标签大致的结构通常是如此:
<div class="tabs-container">
<ul class="tabs">
<li class="active">Tab1</li>
<li>Tab2</li>
<li>Tab3</li>
</ul>
<div class="tab-content">
被激活标签项的内容
</div>
</div>
转换成vue组件,想象中的样子
<tabs class="tabs-container">
<tab class="active" :label="'Tab1'">Tab1内容</tab>
<tab :label="'Tab2'">Tab2内容</tab>
<tab :label="'Tab3'">Tab3内容</tab>
</tabs>
1. 外层 tabs容器组件 tabs.vue
<template>
<div class="tab-container">
<ul class="tab-head">
<slot></slot>
</ul>
<content-container :panels="panels" />
</div>
</template>
<script>
import ContentContainer from './content-container.vue' // 用于展示tab内容的组件
export default {
name: 'tabs',
data() {
return {
panels: [] // 子组件mounted的时候将自己push到该数组中
}
},
props: {
value: {
type: [String, Number],
required: true
}
},
methods: {
tabChange(index) {
this.$emit('tabChange',index)
}
},
components: {
ContentContainer
}
}
</script>
<style scoped>
.tab-container{
width: 500px;
}
.tab-head {
padding: 0 15px;
border-bottom: 2px solid #ccc;
font-size: 0;
}
</style>
2. 单个标签项 tab组件 (用了render function) tab.vue
<script>
export default {
name: 'tab',
props: {
index: {
type: [String,Number],
required: true
},
label: {
type: String
}
},
mounted() {
this.$parent.panels.push(this)
},
render(h) {
const tab = this.$slots.label || h('span',this.label) // 如果用name=label的slot,则用slot,否则展示label prop
return h('li', {
class: this.classes,
on: {
click: this.activeCurTab
}
},[ tab ])
},
computed: {
active() {
return this.index ===this.$parent.value
},
classes() {
return ['tab', this.active? 'active': '']
}
},
methods: {
activeCurTab() {
this.$parent.tabChange(this.index)
}
}
}
</script>
<style lang="scss" scoped>
.tab {
display: inline-block;
font-size: 14px;
padding: 0 10px;
height: 40px;
line-height: 38px;
position: relative;
bottom: -2px;
cursor: pointer;
margin: 0 5px;
border-bottom: 2px solid rgba(255,255,255,0);
&.active {
color: #f60;
border-bottom: 2px solid #f60;
}
}
</style>
3. tab内容展示容器组件 content-container.vue
<script>
export default {
props: {
panels: {
type: Array,
default: function() {
return []
}
}
},
render(h) {
const contents = this.panels.map(tab => {
return tab.active? tab.$slots.default: null
})
return h('div', {
class: 'tab-content'
}, contents)
}
}
</script>
<style scoped>
.tab-content {
margin-top: 8px;
border: 1px solid #ccc;
padding: 15px;
min-height: 100px;
}
</style>
4. 注册为全局组件 (和上面组件同目录的index.js)
import tabs from './tabs.vue'
import tab from './tab.vue'
export default (Vue) => {
Vue.component(tabs.name, tabs)
Vue.component(tab.name, tab)
}
接着要在main.js中引入并use一下
import tabs from './components/tabs'
Vue.use(tabs)
5. 使用组件
<template>
<div class="cus-tab">
<tabs :value="curTab" @tabChange="tabChange">
<tab :index="0" :label="'Tab1'">
<div>
Tab1的内容
<notify :content="'自定义组件测试'"></notify>
</div>
</tab>
<tab :index="1" :label="'Tab2'">
<span slot="label">我是tab2</span>
<div>Tab2的内容</div>
</tab>
<tab :index="2" :label="'Tab3'">
<div>Tab3的内容</div>
</tab>
</tabs>
</div>
</template>
<script>
export default {
data() {
return {
curTab: 0 // 当前激活的tab索引
}
},
methods: {
tabChange(index) {
this.curTab = index
}
}
}
</script>
6. 效果

网友评论