很多组件库都有类似组件,但是我觉得对于前端来说,不只是只会使用组件而已。
完成效果
需要文件
Vuejs_actual_tabs.vue :标签页切换头
Vuejs_actual_pane.vue: 标签页显示内容
style.css
moduleApply.vue : 使用页面
Vuejs_actual_tabs.vue
<template>
<div class="tabs">
<div class="tabs-bar">
<div
:class="tabCls(item)"
v-for="(item, index) in navList"
:key="index"
@click="handleChange(index)">
{{ item.label }}
</div>
</div>
<div class="tabs-content">
<!-- 这里的slot就是嵌套的pane -->
<slot></slot>
</div>
</div>
</template>
<script>
export default {
// name:"tabs",
props: {
value: {
type: [String, Number]
}
},
data() {
return {
// 因为不能修改value,所以复制一份自己维护
currentValue:this.value,
// 用于渲染 tabs的标题
navList:[],
}
},
methods:{
tabCls(item){
return [
'tabs-tab',
{
// 给当前选中的tab加一个calss
'tabs-tab-active': item.name === this.currentValue
}
]
},
getTabs(){
return this.$children.filter((item) => { // 通过遍历子组件,得到所有的pane组件
return item.$options.name === 'pane'
})
},
updateNav(){
this.navList = []
// 设置对this的引用,在function回调里,this指向的并不是vue实例
var _this = this
this.getTabs().forEach((pane, index) => {
_this.navList.push({
label: pane.label,
name: pane.name || index
})
// 如果没有给pane设置name,默认设置它的索引
if (!pane.name) pane.name = index
// 设置当前选中的tab的索引,在后面介绍
if (index === 0) {
if (!_this.currentValue) {
_this.currentValue = pane.name || index
}
}
})
this.updateStatus()
},
updateStatus(){
var tabs = this.getTabs()
var _this = this
// 显示当前选中的tab对应的pane组件,隐藏没有选中的
tabs.forEach((tab) => {
return tab.show = tab.name === _this.currentValue
})
},
handleChange(index){
console.log('处罚【hanChange');
var nav = this.navList[index]
var name = nav.name
// 改变当前选中的tab,并出发下面的wath
this.currentValue = name
// 更新 value
this.$emit('input', name);
// 触发一个自定义事件,供父级使用
this.$emit('on-click', name)
},
},
watch:{
value:function (val) {
this.currentValue = val
},
currentValue:function (){
// 在当前选中的tab发生变化时,更新pane的显示状态
this.updateStatus()
}
}
}
</script>
<style lang="less">
</style>
Vuejs_actual_pane.vue
<template>
<div class="pane" v-show="show">
<slot></slot>
</div>
</template>
<script>
export default {
name:"pane", // 不加这个name标签tab展示不出来
props:{
name:{
type:String
},
label:{
type:String,
default:''
}
},
data() {
return {
show:true
}
},
methods:{
updateNav() {
this.$parent.updateNav()
},
},
watch:{
label(){
this.updateNav()
}
},
mounted(){
this.updateNav()
}
}
</script>
<style>
</style>
moduleApply.vue
<template>
<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>
</template>
<script>
import inpNumber from '@/components/Vuejs_actual_input.vue'
import tabs from '@/components/Vuejs_actual_tabs.vue'
import pane from '@/components/Vuejs_actual_pane.vue'
import '../assets/style.css'
export default {
// name:'moduleApply',
components:{
inpNumber,
tabs,
pane
},
data() {
return {
activeKey:'1'
}
},
}
</script>
<style lang="less">
</style>
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: #fff;
border:1px solid #d7dde4;
cursor: pointer;
position: relative;
}
.tabs-tab-active{
color: #3399ff;
border-top: 1px solid #3399ff;
border-bottom:1px solid #fff
}
.tabs-tab-active:before{
content: '';
display: block;
height: 1px;
background: #3399ff;
position: absolute;
top: 0;
left: 0;
right: 0;
}
.tabs-content{
padding: 8px 0;
}
追加一个可关闭按钮功能+内容滑动动画,有点迷,搜索中发现一篇完整的
掘金:https://juejin.cn/post/6844903881965568007
我使用transform
动画效果没出来,最后使用了@keyframes
@keyframes example { // 创建动画并命名
0% {padding-left: 100%;}
100% {padding-left: 0px;}
}
.pane {
width: 100%;
height: 100px;
animation-name: example; // 使用动画
animation-duration: .5s;
}