Files
gym-manage/gym-manage-uniapp/components/TabBar.vue
T

281 lines
6.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- components/TabBar.vue -->
<template>
<view v-if="shouldShowTabBar" class="tab-bar">
<view
v-for="(tab, index) in tabs"
:key="tab.path"
:class="['tab-item', { active: currentActiveIndex === index }]"
hover-class="tab-item--hover"
@tap.stop="onTabTap(index)"
>
<!-- 判断是否使用字体图标我的页面用字体其他用图片 -->
<text
v-if="tab.useFontIcon"
:class="['iconfont', tab.icon]"
class="tab-icon-font"
:style="{ fontSize: tab.fontSize}"
></text>
<text class="tab-label">{{ tab.label }}</text>
</view>
</view>
</template>
<script setup>
import { ref, watch, onMounted, onBeforeUnmount } from 'vue'
import {
PAGE,
TAB_ROUTES,
getCurrentRoutePath,
getTabIndexByRoute
} from '@/common/constants/routes.js'
const props = defineProps({
active: { type: Number, default: -1 },
activeTab: { type: Number, default: -1 }
})
const emit = defineEmits(['update:active', 'tab-change'])
const currentActiveIndex = ref(-1)
const shouldShowTabBar = ref(true)
const HIDE_TABBAR_PAGES = [
'pages/memberInfo/courseList',
'pages/memberInfo/courseDetail',
'pages/memberInfo/booking',
'pages/memberInfo/bodyTestReport',
'pages/groupCourse/list',
'pages/groupCourse/detail',
'pages/searchCourse/searchCourse',
'pages/checkIn/checkIn',
'pages/memberInfo/myCourses',
'pages/memberInfo/coupons',
'pages/memberInfo/points',
'pages/memberInfo/pointsMall',
'pages/memberInfo/referral',
'pages/memberInfo/userInfo',
'pages/memberInfo/memberCard',
]
function getActiveIndexFromRoute() {
const routePath = getCurrentRoutePath()
const index = getTabIndexByRoute(routePath)
console.log('从路由获取索引:', routePath, '->', index)
return index >= 0 ? index : 0
}
function syncActiveState() {
const routeIndex = getActiveIndexFromRoute()
if (routeIndex >= 0) {
currentActiveIndex.value = routeIndex
return
}
if (props.active >= 0) {
currentActiveIndex.value = props.active
} else if (props.activeTab >= 0) {
currentActiveIndex.value = props.activeTab
} else {
currentActiveIndex.value = 0
}
}
function checkShouldShow() {
let routePath = getCurrentRoutePath()
if (routePath.startsWith('/')) {
routePath = routePath.slice(1)
}
if (routePath.includes('?')) {
routePath = routePath.split('?')[0]
}
const shouldHide = HIDE_TABBAR_PAGES.includes(routePath)
shouldShowTabBar.value = !shouldHide
console.log('=== TabBar 显示控制 ===')
console.log('原始路径:', getCurrentRoutePath())
console.log('标准化路径:', routePath)
console.log('是否隐藏:', shouldHide)
console.log('是否显示 TabBar:', shouldShowTabBar.value)
}
let routeWatcher = null
let appRouteCallback = null
onMounted(() => {
syncActiveState()
checkShouldShow()
// #ifdef APP-PLUS
routeWatcher = setInterval(() => {
syncActiveState()
checkShouldShow()
}, 300)
// #endif
// #ifdef MP-WEIXIN
if (typeof uni.onAppRoute === 'function') {
appRouteCallback = () => {
setTimeout(() => {
syncActiveState()
checkShouldShow()
}, 100)
}
uni.onAppRoute(appRouteCallback)
}
// #endif
})
onBeforeUnmount(() => {
// #ifdef APP-PLUS
if (routeWatcher) { clearInterval(routeWatcher) }
// #endif
// #ifdef MP-WEIXIN
if (appRouteCallback && typeof uni.offAppRoute === 'function') {
uni.offAppRoute(appRouteCallback)
}
// #endif
})
watch(() => props.active, () => {
const routeIndex = getActiveIndexFromRoute()
if (routeIndex !== currentActiveIndex.value) { syncActiveState() }
})
// tabs 配置:只有"我的"用字体图标
const tabs = [
{
path: PAGE.INDEX,
icon: 'icon-home',
label: '首页',
useFontIcon: true,
fontSize:"36rpx"
},
{
path: PAGE.COURSE,
icon: 'icon-course',
iconActive: '/static/tabBar/active/course.png',
label: '课程',
useFontIcon: true,
fontSize:"36rpx"
},
{
path: PAGE.TRAIN,
icon: 'icon-train',
label: '训练',
useFontIcon: true,
fontSize:"48rpx"
},
{
path: PAGE.DISCOVER,
icon: 'icon-discover',
label: '发现',
useFontIcon: true,
fontSize:"48rpx"
},
{
path: PAGE.MEMBER,
icon: 'icon-profile',
label: '我的',
useFontIcon: true,
fontSize:"36rpx"
}
]
let isSwitching = false
function onTabTap(index) {
if (isSwitching) return
const targetPath = TAB_ROUTES[index]
const currentPath = TAB_ROUTES[currentActiveIndex.value]
if (targetPath === currentPath) return
console.log('Tab 点击:', index, targetPath)
currentActiveIndex.value = index
emit('update:active', index)
emit('tab-change', index)
let timer = setTimeout(() => {
uni.showLoading({ title: '加载中...', mask: true })
}, 50)
isSwitching = true
uni.switchTab({
url: targetPath,
success: () => { console.log('switchTab 成功:', targetPath) },
fail: (err) => {
console.error('switchTab 失败:', err)
uni.reLaunch({ url: targetPath })
},
complete: () => {
clearTimeout(timer)
uni.hideLoading()
setTimeout(() => {
isSwitching = false
syncActiveState()
checkShouldShow()
}, 100)
}
})
}
</script>
<style lang="scss" scoped>
// 引入字体图标 CSS(定义 @font-face
@import '/common/style/tabbar_icon/tabbar.css';
.tab-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 120rpx;
background: rgba(200, 225, 238, 0.8);
backdrop-filter: blur(24px);
-webkit-backdrop-filter: blur(24px);
display: flex;
justify-content: space-around;
align-items: center;
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
box-shadow: 0 -4rpx 24rpx rgba(120, 185, 215, 0.2);
border-radius: 32rpx 32rpx 0 0;
z-index: 999;
}
.tab-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 8rpx;
padding: 12rpx 24rpx;
transition: all 0.1s ease;
}
.tab-item:active {
transform: scale(0.95);
}
// 图片图标样式
.tab-icon {
width: 40rpx;
height: 40rpx;
}
// 字体图标样式
.tab-icon-font {
font-size: 44rpx;
line-height: 1;
}
// 字体图标颜色控制(根据选中状态)
.tab-item .iconfont {
color: gray; // 未选中颜色
}
.tab-item.active .iconfont {
color: #5A98B0; // 选中颜色
}
.tab-label {
font-size: 22rpx;
color: #8AABBB;
}
.tab-item.active .tab-label {
color: #5A98B0;
font-weight: 600;
}
</style>