web-base-h5/components/raised-tabbar.vue

327 lines
7.4 KiB
Vue
Raw Normal View History

2025-07-25 16:00:11 +08:00
<template>
<view class="raised-tabbar">
<!-- 背景遮罩层创建凸起效果 -->
<view class="tabbar-bg">
<view class="raised-circle"></view>
</view>
<!-- TabBar内容 -->
<view class="tabbar-content">
<!-- 左侧两个选项 -->
<view
v-for="(item, index) in leftItems"
:key="index"
class="tabbar-item"
:class="{ active: current === index }"
@click="tabbarChange(index)"
>
<view class="item-icon">
<image
:src="current === index ? item.activeIcon : item.inactiveIcon"
class="icon-normal"
/>
<view v-if="item.badge && item.badge > 0" class="badge">{{
item.badge
}}</view>
</view>
<text class="item-text" :class="{ active: current === index }">{{
item.text
}}</text>
</view>
<!-- 中间凸起的选项 -->
<view
class="tabbar-item center-item"
:class="{ active: current === 2 }"
@click="tabbarChange(2)"
v-if="!newShareMember"
2025-07-25 16:00:11 +08:00
>
<view class="center-icon-wrapper">
<view class="center-icon">
<image
:src="
current === 2 ? centerItem.activeIcon : centerItem.inactiveIcon
"
class="center-image"
/>
</view>
</view>
<text
class="item-text center-text"
:class="{ active: current === 2 }"
>{{ centerItem.text }}</text
>
</view>
<!-- 右侧两个选项 -->
<view
v-for="(item, index) in rightItems"
:key="index + 3"
class="tabbar-item"
:class="{ active: current === index + 3 }"
@click="tabbarChange(index + 3)"
>
<view class="item-icon">
<image
:src="current === index + 3 ? item.activeIcon : item.inactiveIcon"
class="icon-normal"
/>
<view v-if="item.badge && item.badge > 0" class="badge">{{
item.badge
}}</view>
</view>
<text class="item-text" :class="{ active: current === index + 3 }">{{
item.text
}}</text>
</view>
</view>
</view>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'RaisedTabbar',
props: {
current: {
type: Number,
default: 0,
},
},
mounted() {
this.newShareMember = uni.getStorageSync('User')?.loginType !== 0
},
2025-07-25 16:00:11 +08:00
data() {
return {
newShareMember: false,
2025-07-25 16:00:11 +08:00
// 完整的路由列表
list: [
{
text: '首页',
path: 'pages/index/index',
},
{
text: '会员专区',
path: 'pages/specialArea/index',
},
{
text: '个人推广',
path: 'pages/mine/share/index',
},
{
text: '购物车',
path: 'pages/shoppingCar/index',
},
{
text: '我的',
path: 'pages/mine/index',
},
],
}
},
computed: {
...mapGetters(['shopCarLength']),
// 左侧两个选项
leftItems() {
return [
{
text: '首页',
activeIcon: require('@/static/images/one1.png'),
inactiveIcon: require('@/static/images/one2.png'),
},
{
text: '会员专区',
activeIcon: require('@/static/images/five1.jpg'),
inactiveIcon: require('@/static/images/five2.jpg'),
},
]
},
// 中间凸起选项
centerItem() {
return {
text: '个人推广',
activeIcon: require('@/static/images/code.svg'),
inactiveIcon: require('@/static/images/code.svg'),
}
},
// 右侧两个选项
rightItems() {
return [
{
text: '购物车',
activeIcon: require('@/static/images/three1.png'),
inactiveIcon: require('@/static/images/three2.png'),
badge: this.shopCarLength,
},
{
text: '我的',
activeIcon: require('@/static/images/fore1.png'),
inactiveIcon: require('@/static/images/fore2.png'),
},
]
},
},
methods: {
tabbarChange(index) {
this.$emit('change', index)
uni.switchTab({
url: '/' + this.list[index].path,
})
},
},
}
</script>
<style lang="scss" scoped>
.raised-tabbar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 1000;
.tabbar-bg {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 60px;
background: #ffffff;
// 添加底部安全区域
&::after {
content: '';
position: absolute;
bottom: -40px;
left: 0;
right: 0;
height: env(safe-area-inset-bottom);
background: #ffffff;
}
}
.tabbar-content {
position: relative;
display: flex;
align-items: flex-end;
height: 60px;
padding-bottom: 5px;
background: transparent;
.tabbar-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-bottom: 5px;
position: relative;
.item-icon {
position: relative;
margin-bottom: 2px;
.icon-normal {
width: 20px;
height: 20px;
}
.badge {
position: absolute;
top: -5px;
right: -8px;
background: #ff4757;
color: white;
border-radius: 50%;
width: 16px;
height: 16px;
font-size: 10px;
text-align: center;
line-height: 16px;
display: flex;
align-items: center;
justify-content: center;
}
}
.item-text {
font-size: 10px;
color: #666666;
line-height: 1.2;
&.active {
color: #333333;
}
}
// 中间凸起的特殊样式
&.center-item {
position: relative;
.center-icon-wrapper {
position: absolute;
2025-07-25 17:52:25 +08:00
top: -58rpx;
2025-07-25 16:00:11 +08:00
background: #fff;
border-radius: 50%;
font-size: 0;
padding: 4rpx;
2025-07-25 17:52:25 +08:00
z-index: 10;
2025-07-25 16:00:11 +08:00
.center-icon {
width: 92rpx;
height: 92rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
// box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
position: relative;
// 添加呼吸动画
// animation: breathe 3s ease-in-out infinite;
2025-07-25 16:00:11 +08:00
.center-image {
width: 92rpx;
height: 92rpx;
// position: absolute;
// top: -10rpx;
}
// 活跃状态
// &.active {
// background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
// }
}
}
.center-text {
margin-top: 25px;
font-size: 9px;
}
&.active {
.center-icon-wrapper .center-icon {
// background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
transform: scale(1.1);
transition: all 0.3s ease;
}
}
}
}
}
}
// 呼吸动画关键帧
@keyframes breathe {
0%,
100% {
transform: scale(1);
// box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
}
50% {
transform: scale(1.05);
// box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
}
}
</style>