tabbar适配安卓端

This commit is contained in:
future
2026-06-11 14:51:07 +08:00
parent c94be9bc38
commit c2a09a5057
9 changed files with 308 additions and 304 deletions
+55 -49
View File
@@ -3,75 +3,81 @@
<GlobalLoading /> <GlobalLoading />
</view> </view>
</template> </template>
<script> <script>
import GlobalLoading from '@/components/global/GlobalLoading.vue' import GlobalLoading from '@/components/global/GlobalLoading.vue'
export default { export default {
components: {
GlobalLoading
},
onLaunch: function() { onLaunch: function() {
console.log('App Launch') console.log('App Launch')
this.preloadTabData() this.preloadTabData()
}, },
onShow: function() { onShow: function() {
console.log('App Show') console.log('App Show')
// #ifdef APP-PLUS
this.hideNativeTabBar()
// #endif
}, },
onHide: function() { onHide: function() {
console.log('App Hide') console.log('App Hide')
}, },
methods: { methods: {
// 隐藏原生 TabBar - 这里是核心修复
hideNativeTabBar() {
// 尝试多次执行,确保执行成功
const tryHide = (times = 0) => {
if (times > 10) return // 最多尝试10次
uni.hideTabBar({
animation: false,
success: () => {
console.log('✅ 原生TabBar隐藏成功')
// 强制 CSS 覆盖(双重保险)
this.forceCSSHide()
},
fail: (err) => {
console.log(`❌ 第${times+1}次隐藏失败,1秒后重试`, err)
setTimeout(() => tryHide(times + 1), 1000)
}
})
}
// 延迟 300ms 执行,确保页面挂载完成
setTimeout(() => tryHide(), 300)
},
// 强制 CSS 覆盖(最终保险)
forceCSSHide() {
// #ifdef APP-PLUS
const style = document.createElement('style')
style.innerHTML = `
uni-tabbar,
uni-tabbar .uni-tabbar,
.uni-tabbar,
uni-tabbar > .uni-tabbar,
[class*="uni-tabbar"] {
display: none !important;
height: 0 !important;
opacity: 0 !important;
visibility: hidden !important;
pointer-events: none !important;
}
`
document.head.appendChild(style)
console.log('✅ CSS 强制覆盖已注入')
// #endif
},
// 预加载所有 Tab 页面的核心数据 // 预加载所有 Tab 页面的核心数据
preloadTabData() { preloadTabData() {
// 延迟执行,不阻塞首屏 // 延迟执行,不阻塞首屏
setTimeout(() => { setTimeout(() => {
// 预加载课程数据 // 预加载课程数据等...
// #ifdef MP-WEIXIN
// 小程序端预请求数据
// #endif
// 预加载训练数据
}, 1000) }, 1000)
} }
} }
} }
</script> </script>
<style lang="scss">
@import 'common/style/base.css';
/* 全局骨架屏样式 */
.skeleton {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
}
@keyframes skeleton-loading {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
/* 页面切换动画 */
.page-enter-active,
.page-leave-active {
transition: opacity 0.2s ease, transform 0.2s ease;
}
.page-enter-from {
opacity: 0;
transform: translateX(30rpx);
}
.page-leave-to {
opacity: 0;
transform: translateX(-30rpx);
}
.app-container {
width: 100%;
min-height: 100vh;
max-width: 430px;
margin: 0 auto;
background-color: var(--bg-light);
position: relative;
overflow-x: hidden;
}
</style>
+45 -8
View File
@@ -1,6 +1,7 @@
<!-- components/TabBar.vue --> <!-- components/TabBar.vue -->
<template> <template>
<view v-if="shouldShowTabBar" class="tab-bar"> <view v-if="shouldShowTabBar" class="tab-bar-wrapper">
<view class="tab-bar">
<view <view
v-for="(tab, index) in tabs" v-for="(tab, index) in tabs"
:key="tab.path" :key="tab.path"
@@ -18,6 +19,7 @@
<text class="tab-label">{{ tab.label }}</text> <text class="tab-label">{{ tab.label }}</text>
</view> </view>
</view> </view>
</view>
</template> </template>
<script setup> <script setup>
@@ -98,15 +100,22 @@ function checkShouldShow() {
let routeWatcher = null let routeWatcher = null
let appRouteCallback = null let appRouteCallback = null
let isNavigating = false
onMounted(() => { onMounted(() => {
syncActiveState() syncActiveState()
checkShouldShow() checkShouldShow()
// #ifdef APP-PLUS // #ifndef MP-WEIXIN
// H5和其他平台使用轮询监听路由变化
routeWatcher = setInterval(() => { routeWatcher = setInterval(() => {
syncActiveState() // 导航期间不更新状态,避免覆盖用户点击的索引
if (isNavigating) return
const newIndex = getActiveIndexFromRoute()
if (newIndex !== currentActiveIndex.value) {
currentActiveIndex.value = newIndex
}
checkShouldShow() checkShouldShow()
}, 300) }, 200)
// #endif // #endif
// #ifdef MP-WEIXIN // #ifdef MP-WEIXIN
if (typeof uni.onAppRoute === 'function') { if (typeof uni.onAppRoute === 'function') {
@@ -122,12 +131,17 @@ onMounted(() => {
}) })
onBeforeUnmount(() => { onBeforeUnmount(() => {
// #ifdef APP-PLUS // #ifndef MP-WEIXIN
if (routeWatcher) { clearInterval(routeWatcher) } // H5和其他平台清理定时器
if (routeWatcher) {
clearInterval(routeWatcher)
routeWatcher = null
}
// #endif // #endif
// #ifdef MP-WEIXIN // #ifdef MP-WEIXIN
if (appRouteCallback && typeof uni.offAppRoute === 'function') { if (appRouteCallback && typeof uni.offAppRoute === 'function') {
uni.offAppRoute(appRouteCallback) uni.offAppRoute(appRouteCallback)
appRouteCallback = null
} }
// #endif // #endif
}) })
@@ -184,9 +198,12 @@ function onTabTap(index) {
const currentPath = TAB_ROUTES[currentActiveIndex.value] const currentPath = TAB_ROUTES[currentActiveIndex.value]
if (targetPath === currentPath) return if (targetPath === currentPath) return
console.log('Tab 点击:', index, targetPath) console.log('Tab 点击:', index, targetPath)
// 立即更新状态
currentActiveIndex.value = index currentActiveIndex.value = index
emit('update:active', index) emit('update:active', index)
emit('tab-change', index) emit('tab-change', index)
// 设置导航标志,阻止轮询覆盖状态
isNavigating = true
let timer = setTimeout(() => { let timer = setTimeout(() => {
uni.showLoading({ title: '加载中...', mask: true }) uni.showLoading({ title: '加载中...', mask: true })
}, 50) }, 50)
@@ -203,7 +220,10 @@ function onTabTap(index) {
uni.hideLoading() uni.hideLoading()
setTimeout(() => { setTimeout(() => {
isSwitching = false isSwitching = false
isNavigating = false
// #ifdef MP-WEIXIN
syncActiveState() syncActiveState()
// #endif
checkShouldShow() checkShouldShow()
}, 100) }, 100)
} }
@@ -215,11 +235,21 @@ function onTabTap(index) {
// 引入字体图标 CSS(定义 @font-face // 引入字体图标 CSS(定义 @font-face
@import '/common/style/tabbar_icon/tabbar.css'; @import '/common/style/tabbar_icon/tabbar.css';
.tab-bar { // 固定容器 - 确保TabBar始终在屏幕底部
.tab-bar-wrapper {
position: fixed; position: fixed;
bottom: 0; bottom: 0;
left: 0; left: 0;
right: 0; right: 0;
z-index: 9999;
pointer-events: none;
}
.tab-bar-wrapper .tab-bar {
pointer-events: auto;
}
.tab-bar {
height: 120rpx; height: 120rpx;
background: white; background: white;
backdrop-filter: blur(24px); backdrop-filter: blur(24px);
@@ -231,7 +261,14 @@ function onTabTap(index) {
padding-bottom: env(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom);
box-shadow: 0 -4rpx 24rpx var(--tabbar-shadow); box-shadow: 0 -4rpx 24rpx var(--tabbar-shadow);
border-radius: 32rpx 32rpx 0 0; border-radius: 32rpx 32rpx 0 0;
z-index: 999; /* 防闪烁优化 */
transform: translateZ(0);
-webkit-transform: translateZ(0);
will-change: transform;
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
perspective: 1000;
-webkit-perspective: 1000;
} }
.tab-item { .tab-item {
+5 -3
View File
@@ -1,6 +1,6 @@
{ {
"name" : "gym-manage-uniapp", "name" : "活氧舱",
"appid" : "__UNI__1F1874C", "appid" : "__UNI__52E2F0D",
"description" : "", "description" : "",
"versionName" : "1.0.0", "versionName" : "1.0.0",
"versionCode" : "100", "versionCode" : "100",
@@ -41,7 +41,9 @@
] ]
}, },
/* ios */ /* ios */
"ios" : {}, "ios" : {
"dSYMs" : false
},
/* SDK */ /* SDK */
"sdkConfigs" : {} "sdkConfigs" : {}
} }
+1 -1
View File
@@ -284,7 +284,7 @@
}, },
"uniIdRouter": {}, "uniIdRouter": {},
"tabBar": { "tabBar": {
"custom": true, // 启用自定义 tabBar "custom": true,
"list": [ "list": [
{ "pagePath": "pages/index/index", "text": "首页" }, { "pagePath": "pages/index/index", "text": "首页" },
{ "pagePath": "pages/course/index", "text": "课程" }, { "pagePath": "pages/course/index", "text": "课程" },
+25 -32
View File
@@ -1,9 +1,7 @@
<!-- pages/course/index.vue --> <!-- pages/course/index.vue -->
<template> <template>
<!-- <view class="bg-wrapper"> <!-- 滚动内容区域 -->
<image src="https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/images/wave_top.png" mode="widthFix" class="wave-bg wave-top" /> <scroll-view scroll-y="false" class="scroll-container">
<image src="https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/images/wave_bottom.png" mode="widthFix" class="wave-bg wave-bottom" />
</view> -->
<view class="tab-page"> <view class="tab-page">
<view class="tab-page__header"> <view class="tab-page__header">
<text class="tab-page__title">课程</text> <text class="tab-page__title">课程</text>
@@ -34,7 +32,12 @@
<view class="bottom-placeholder"></view> <view class="bottom-placeholder"></view>
</view> </view>
</scroll-view>
<!-- 固定 TabBar -->
<view class="tabbar-fixed">
<TabBar @update:active="handleTabActive" /> <TabBar @update:active="handleTabActive" />
</view>
</template> </template>
<script setup> <script setup>
@@ -128,34 +131,6 @@ function goMyCourses() {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.bg-wrapper {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
pointer-events: none;
}
.wave-bg {
position: fixed;
left: 0;
width: 100%;
pointer-events: none;
opacity: 0.6;
}
.wave-top {
top: 0;
opacity: 0.5;
}
.wave-bottom {
bottom: 100rpx;
opacity: 0.35;
}
.tab-page { .tab-page {
min-height: 100vh; min-height: 100vh;
padding-bottom: 160rpx; padding-bottom: 160rpx;
@@ -259,4 +234,22 @@ function goMyCourses() {
0% { background-position: 200% 0; } 0% { background-position: 200% 0; }
100% { background-position: -200% 0; } 100% { background-position: -200% 0; }
} }
/* 滚动容器 */
.scroll-container {
position: relative;
z-index: 1;
height: 100vh;
width: 100%;
}
/* 固定 TabBar */
.tabbar-fixed {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 1000;
background-color: transparent;
}
</style> </style>
+25 -31
View File
@@ -1,8 +1,6 @@
<template> <template>
<!-- <view class="bg-wrapper"> <!-- 滚动内容区域 -->
<image src="https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/images/wave_top.png" mode="widthFix" class="wave-bg wave-top" /> <scroll-view scroll-y class="scroll-container">
<image src="https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/images/wave_bottom.png" mode="widthFix" class="wave-bg wave-bottom" />
</view> -->
<view class="tab-page"> <view class="tab-page">
<view class="tab-page__header"> <view class="tab-page__header">
<text class="tab-page__title">发现</text> <text class="tab-page__title">发现</text>
@@ -28,7 +26,12 @@
<view class="bottom-placeholder"></view> <view class="bottom-placeholder"></view>
</view> </view>
</scroll-view>
<!-- 固定 TabBar -->
<view class="tabbar-fixed">
<TabBar :active="3" /> <TabBar :active="3" />
</view>
</template> </template>
<script setup> <script setup>
@@ -50,33 +53,6 @@ function goPointsMall() {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.bg-wrapper {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
pointer-events: none;
}
.wave-bg {
position: fixed;
left: 0;
width: 100%;
pointer-events: none;
opacity: 0.6;
}
.wave-top {
top: 0;
opacity: 0.5;
}
.wave-bottom {
bottom: 100rpx;
opacity: 0.35;
}
.tab-page { .tab-page {
min-height: 100vh; min-height: 100vh;
padding-bottom: 160rpx; padding-bottom: 160rpx;
@@ -139,4 +115,22 @@ function goPointsMall() {
.bottom-placeholder { .bottom-placeholder {
height: 40rpx; height: 40rpx;
} }
/* 滚动容器 */
.scroll-container {
position: relative;
z-index: 1;
height: 100vh;
width: 100%;
}
/* 固定 TabBar */
.tabbar-fixed {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 1000;
background-color: transparent;
}
</style> </style>
+21 -57
View File
@@ -1,9 +1,4 @@
<template> <template>
<!-- 水波纹背景 - 顶层显示 -->
<!-- <view class="bg-wrapper">
<image src="https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/images/wave_top.png" mode="widthFix" class="wave-bg wave-top" />
<image src="https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/images/wave_bottom.png" mode="widthFix" class="wave-bg wave-bottom" />
</view> -->
<!-- 固定白色块滚动时显示 --> <!-- 固定白色块滚动时显示 -->
<view class="hand" :style="{height : handHeight + 'rpx'}" v-show="isShow"></view> <view class="hand" :style="{height : handHeight + 'rpx'}" v-show="isShow"></view>
@@ -61,24 +56,11 @@ const handleScroll = (e) => {
const distance = e.detail.scrollTop const distance = e.detail.scrollTop
scrollDistance.value = distance scrollDistance.value = distance
// 获取滚动容器的高度(可视区域高度) console.log(`滚动距离: ${distance}`)
const scrollViewHeight = e.detail.scrollHeight
scrollContentHeight.value = scrollViewHeight
// 计算滚动百分比 // 当滚动超过一定距离时显示白色块(200px = 400rpx
// 滚动百分比 = 当前滚动距离 / (内容总高度 - 可视区域高度) * 100 const SHOW_THRESHOLD = 50 // 滚动超过200px时显示白条
const maxScrollDistance = scrollContentHeight.value - windowHeight.value isShow.value = distance > SHOW_THRESHOLD
let scrollPercent = 0
if (maxScrollDistance > 0) {
scrollPercent = (distance / maxScrollDistance) * 100
}
console.log(`滚动距离: ${distance}, 滚动百分比: ${scrollPercent.toFixed(2)}%`)
// 当滚动超过 10% 时显示白色块(你可以调整这个百分比)
const SHOW_THRESHOLD = 40 // 10% 的阈值,可以根据需要调整
isShow.value = scrollPercent > SHOW_THRESHOLD
} }
// 下拉刷新处理 // 下拉刷新处理
@@ -118,16 +100,27 @@ onMounted(() => {
loading.value = false loading.value = false
}, 1500) }, 1500)
// 获取胶囊按钮高度 // 获取系统信息
const menuButtonInfo = uni.getMenuButtonBoundingClientRect()
const navTotalHeight = menuButtonInfo.top + menuButtonInfo.height
handHeight.value = navTotalHeight * 2.5
// 获取可视窗口高度
uni.getSystemInfo({ uni.getSystemInfo({
success: (res) => { success: (res) => {
windowHeight.value = res.windowHeight windowHeight.value = res.windowHeight
console.log('可视窗口高度:', windowHeight.value) console.log('可视窗口高度:', windowHeight.value)
console.log('平台:', res.platform)
// #ifdef MP-WEIXIN
// 微信小程序使用胶囊按钮高度(保持原有逻辑)
const menuButtonInfo = uni.getMenuButtonBoundingClientRect()
const navTotalHeight = menuButtonInfo.top + menuButtonInfo.height
handHeight.value = navTotalHeight * 2.5
console.log('微信小程序胶囊按钮高度:', handHeight.value)
// #endif
// #ifndef MP-WEIXIN
// H5和安卓App只显示状态栏高度
const statusBarHeight = res.statusBarHeight || 0
handHeight.value = statusBarHeight * 2 // 转换为rpx1px = 2rpx
console.log('非微信小程序状态栏高度:', handHeight.value, 'rpx')
// #endif
} }
}) })
@@ -145,35 +138,6 @@ onMounted(() => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
/* 背景包装器 - 固定在最底层 */
.bg-wrapper {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
pointer-events: none;
}
.wave-bg {
position: fixed;
left: 0;
width: 100%;
pointer-events: none;
opacity: 0.6;
}
.wave-top {
top: 0;
opacity: 0.5;
}
.wave-bottom {
bottom: 100rpx;
opacity: 0.35;
}
/* 滚动容器 */ /* 滚动容器 */
.scroll-container { .scroll-container {
position: relative; position: relative;
@@ -1,5 +1,6 @@
<template> <template>
<view class="scroll-container theme-light"> <!-- 滚动内容区域 -->
<scroll-view scroll-y class="scroll-container theme-light">
<view class="member-page"> <view class="member-page">
<MemberInfoHeader <MemberInfoHeader
:user-info="userInfo" :user-info="userInfo"
@@ -44,7 +45,10 @@
</view> </view>
</view> </view>
</view> </view>
<!-- TabBar --> </scroll-view>
<!-- 固定 TabBar -->
<view class="tabbar-fixed">
<TabBar /> <TabBar />
</view> </view>
</template> </template>
@@ -238,4 +242,14 @@ export default {
@import '@/common/style/memberInfo/member-info-referral.css'; @import '@/common/style/memberInfo/member-info-referral.css';
@import '@/common/style/memberInfo/member-info-settings.css'; @import '@/common/style/memberInfo/member-info-settings.css';
@import '@/common/style/memberInfo/member-info-logout.css'; @import '@/common/style/memberInfo/member-info-logout.css';
/* 固定 TabBar */
.tabbar-fixed {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 1000;
background-color: transparent;
}
</style> </style>
+25 -31
View File
@@ -1,8 +1,6 @@
<template> <template>
<!-- <view class="bg-wrapper"> <!-- 滚动内容区域 -->
<image src="https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/images/wave_top.png" mode="widthFix" class="wave-bg wave-top" /> <scroll-view scroll-y class="scroll-container">
<image src="https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/images/wave_bottom.png" mode="widthFix" class="wave-bg wave-bottom" />
</view> -->
<view class="tab-page"> <view class="tab-page">
<view class="tab-page__header"> <view class="tab-page__header">
<text class="tab-page__title">训练</text> <text class="tab-page__title">训练</text>
@@ -30,7 +28,12 @@
<view class="bottom-placeholder"></view> <view class="bottom-placeholder"></view>
</view> </view>
</scroll-view>
<!-- 固定 TabBar -->
<view class="tabbar-fixed">
<TabBar :active="2" /> <TabBar :active="2" />
</view>
</template> </template>
<script setup> <script setup>
@@ -55,33 +58,6 @@ function goCheckIn() {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.bg-wrapper {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 2;
pointer-events: none;
}
.wave-bg {
position: fixed;
left: 0;
width: 100%;
pointer-events: none;
opacity: 0.6;
}
.wave-top {
top: 0;
opacity: 0.5;
}
.wave-bottom {
bottom: 100rpx;
opacity: 0.35;
}
.tab-page { .tab-page {
min-height: 100vh; min-height: 100vh;
padding-bottom: 160rpx; padding-bottom: 160rpx;
@@ -144,4 +120,22 @@ function goCheckIn() {
.bottom-placeholder { .bottom-placeholder {
height: 40rpx; height: 40rpx;
} }
/* 滚动容器 */
.scroll-container {
position: relative;
z-index: 1;
height: 100vh;
width: 100%;
}
/* 固定 TabBar */
.tabbar-fixed {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 1000;
background-color: transparent;
}
</style> </style>