Tab组件介绍
element-ui 也有类似的组件 但是每个TabItem组件会生成一个TabContent,这点不太喜欢。
其实我们可以使用Vue的<component is='name'></compoent>替代。话不多说直接上代码。
Tabs组件
<template>
<div class="tab-box">
<div class="tab-wrap " ref="tabList" :class="TabStyle">
<slot></slot>
<div class="scrollbar" :style="getLineStyle" v-show="left !== 0"></div>
</div>
</div>
</template>
<script>
let scrollLeftRafId
export function scrollLeftTo (scroller, to, duration) {
window.cancelAnimationFrame(scrollLeftRafId)
let count = 0
const from = scroller.scrollLeft
const frames = duration === 0 ? 1 : Math.round((duration * 1000) / 16)
function animate () {
scroller.scrollLeft += (to - from) / frames
if (++count < frames) {
scrollLeftRafId = window.requestAnimationFrame(animate)
}
}
animate()
}
export default {
name: 'tabs',
data () {
return {
left: 0,
lineWidth: null
}
},
mounted () {
// 监听当前实例上的$emit 触发的setActive事件
this.$on('setActive', name => {
this.$emit('input', name)
this.$emit('tabClick', name)
})
},
beforeDestroy () {
this.$off('setActive')
},
watch: {
//监听传过来的value值,当value改变重新渲染tabLine和滚动动画
value: {
immediate: true,
handler () {
this.$nextTick(() => {
this.setTabLine()
this.scrollIntoView()
})
}
}
},
methods: {
setTabLine () {
const currentTab = this.getCurrentTab.$el
this.left = currentTab.offsetLeft + currentTab.offsetWidth / 2
this.lineWidth =
currentTab.querySelector('.tab-text').offsetWidth + 'px'
this.getTab.forEach(item => {
item.$emit('active', {
active: this.value,
titleActiveColor: this.titleActiveColor
})
})
},
scrollIntoView () {
const nav = this.$refs.tabList
const title = this.getCurrentTab.$el
const scrollLeft = title.offsetLeft - (nav.offsetWidth - title.offsetWidth) / 2
scrollLeftTo(nav, scrollLeft, 0.3)
}
},
computed: {
getCurrentTab () {
return this.getTab.find(item => {
return item.name === this.value
})
},
getTab () {
return this.$children
},
getLineStyle () {
return {
transform: `translateX(${this.left}px) translateX(-50%)`,
width: this.lineWidth
}
}
},
props: {
value: {
type: [String, Number]
},
titleActiveColor: {
type: String,
default: '#333'
},
TabStyle: {
type: String,
default: 'spaceBetween'
}
}
}
</script>
TabItem组件
<template>
<div
class="tabItem"
@click="handleClick"
:class="getActive"
:style="getTitleActiveColor"
>
<div class="tab-text" ref="text">
<slot></slot>
<div class="dot" v-if="dot!==''">{{dot}}</div>
</div>
</div>
</template>
<script>
export default {
name: 'tabItem',
data () {
return {
active: false,
titleActiveColor: '#333'
}
},
mounted () {
this.$on('active', e => {
const { active, titleActiveColor } = e
this.active = active === this.name
this.titleActiveColor = titleActiveColor
})
},
beforeDestroy () {
this.$off('active')
},
computed: {
getActive () {
return {
active: this.active
}
},
getTitleActiveColor () {
if (this.active) {
return {
color: this.titleActiveColor
}
}
return ''
}
},
methods: {
handleClick () {
this.$parent.$emit('setActive', this.name)
}
},
props: {
name: {
type: [String, Number],
default: ''
},
dot: {
type: [Number, String],
default: ''
}
}
}
</script>
Tab组件的使用
<tabs v-model="search.status" @tabClick="handleClick">
<tabItem name="1">进行中</tabItem>
<tabItem name="2">已结束</tabItem>
</tabs>