介绍
选项卡组件,用于H5,小程序等环境中,在不同的内容区域之间进行切换。
使用方式模仿vant UI
封装组件
"as-tabs":标签组
"as-tab":单个标签
基础用法
通过active
设定当前激活标签对应的索引值,默认情况下启用第一个标签。
<as-tabs v-model:active="active">
<template v-for="(item,key) in tabs" :key="key">
<as-tab :title="item.name" :badge="item.badge">
{{ item.name }}
</as-tab>
</template>
</as-tabs>
setup(){
const tabs = reactive( [
{ name: '售后申请',badge:1 },
{ name: '处理中',badge:2 },
{ name: '待评价',badge:3 },
{ name: '申请记录',badge:4 }
])
const active = ref(0)
return { active,tabs }
}
开发思路
tab标签选项卡。按照正常的逻辑,我们只需要把不同的需要切换的内容。贴上相应的编号,然后根据不同的编号。去切换相应的内容显示。通常的tab逻辑比这个复杂。这里我们把它简化成两部分,封装tab模块用于储存内容和编号,封装tabs模块由于接收调用编号后切换对应的tab显示。
由于tabs和tab是单独封装的2个模块,从外部看他们并没有什么联系。而在内部用provide和inject实现模块之间的数据共享。
重点:由于provide和inject并不支持实时响应,代码中我们需要添加独立的数据监听控制。
tabs封装
<template>
<view class="tabs">
<view class="tabs__wrap">
<view class="tabs__nav">
<template v-for="(item,key) in AllData" :key="key">
<view :class="['tab',key==active?'active':'']" :data-index="key" @click="handleTab">
<view class="text">
<view v-if="item.badge" class="list-num">{{item.badge}}</view>
<view>{{item.title}}</view>
</view>
</view>
</template>
</view>
</view>
<view class="tabs__content">
<slot></slot>
</view>
</view>
</template>
<script lang="ts">
import { defineComponent,reactive,toRefs,provide,useSlots,ref,computed,onMounted } from 'vue'
export default defineComponent({
name: "as-tabs",
props:{
active:{
default:0
}
},
emits:['update:active','click-tab'],
setup(props,ctx){
const AllData = reactive([])
const active = computed({
get:()=>props.active,
set:(value)=>{ctx.emit("update:active",value)}
})
provide('active',active)
provide('AllData',AllData)
const handleTab = (event:any)=>{
active.value = event.currentTarget.dataset.index
ctx.emit('click-tab',event)
}
return {
AllData,handleTab
}
}
})
</script>
<style lang="scss" scoped>
.tabs {
height: 100vh;
width: 100%;
box-sizing: border-box;
.tabs__wrap {
overflow-x: scroll;
.tabs__nav {
position: fixed;
width: 100%;
height: 88rpx;
line-height: 88rpx;
display: flex;
justify-content: space-between;
align-items: center;
background-color: #fff;
border-bottom: 1rpx solid #eee;
position: relative;
.tab {
font-size: 28rpx;
color: #233445;
height: 60rpx;
line-height: 60rpx;
text-align: center;
min-width: 130rpx;
padding: 0 6rpx;
position: relative;
display: flex;
justify-content: center;
border-bottom: 3px solid #fff;
.text {
width: fit-content;
position: relative;
.list-num {
position: absolute;
top: -2rpx;
right: -24rpx;
width: 30rpx;
height: 30rpx;
font-size: 20rpx;
background: #ff3456;
color: #fff;
/* border: 1rpx solid #ff3456; */
border-radius: 30rpx;
text-align: center;
line-height: 30rpx;
}
}
&.active {
color: #ff3456;
border-bottom: 3px solid #ff3456;
}
}
}
}
.tabs__content {
box-sizing: border-box;
height: calc(100% - 88rpx);
}
}
</style>
tab 封装
<template>
<slot v-if="select"></slot>
</template>
<script lang="ts">
import { defineComponent,reactive,toRefs,computed,inject,onMounted,nextTick,ref,effect } from 'vue'
export default defineComponent({
name: "as-tab",
props:{
title:{
default:'标题'
},
badge:{
default:undefined
}
},
setup(props,ctx){
const active = ref(inject<number>('active'))
const allData = inject<any>('AllData')
const key = ref(null)
const select = computed(()=>active.value == key.value)
onMounted(()=>{
allData.push({
title:props.title,
badge:computed(()=>props.badge)
})
key.value = allData.findIndex((item:any)=>item.title == props.title )
})
return {
allData,active,key,select
}
}
})
</script>
<style lang="scss" scoped>
.tab {
font-size: 28rpx;
color: #233445;
height: 60rpx;
line-height: 60rpx;
text-align: center;
padding: 0 6rpx;
position: relative;
border-bottom: 3px solid #fff;
&.active {
color: #ff3456;
border-bottom: 3px solid #ff3456;
}
}
</style>