背景。自定义导航栏。体验版发布后。产品进行测试。发现底部导航切换多了以后会失效,所有uni.navigateTo都会失效。
- 真机测试不会报错。不会提示
解决方案:上代码
- 原理:检测栈的长度。超过了使用reLauch进行跳转便清空了navigateTo的栈空间,再重新继续使用navigateTo。也可以都是用reLauch,但体验度不好,会白屏后加载。
<template>
<view class="custom-tabbar">
<!-- 彩色装饰条 - 按设计稿三色比例 -->
<view class="color-bars">
<view class="color-bar bar-orange"></view>
<view class="color-bar bar-brown"></view>
<view class="color-bar bar-red"></view>
</view>
<!-- tabBar内容 -->
<view class="tabbar-content">
<!-- 首页 -->
<view class="tab-item" @click="switchTab('home')" :class="{ 'tab-active': activeTab === 'home' }">
<image
:src="activeTab === 'home' ? '/static/img/figma/首页.png' : '/static/img/figma/首页1.png'"
class="tab-icon"
:class="{ 'icon-active': activeTab === 'home' }"
mode="aspectFit" />
<text class="tab-text" :class="{ active: activeTab === 'home' }">首页</text>
</view>
<!-- 动态 -->
<view class="tab-item" @click="switchTab('dynamic')" :class="{ 'tab-active': activeTab === 'dynamic' }">
<image
:src="activeTab === 'dynamic' ? '/static/img/figma/动态.png' : '/static/img/figma/动态1.png'"
class="tab-icon"
:class="{ 'icon-active': activeTab === 'dynamic' }"
mode="aspectFit" />
<text class="tab-text" :class="{ active: activeTab === 'dynamic' }">动态</text>
</view>
<!-- 项目 -->
<view class="tab-item" @click="switchTab('project')" :class="{ 'tab-active': activeTab === 'project' }">
<image
:src="activeTab === 'project' ? '/static/img/figma/项目.png' : '/static/img/figma/项目1.png'"
class="tab-icon"
:class="{ 'icon-active': activeTab === 'project' }"
mode="aspectFit" />
<text class="tab-text" :class="{ active: activeTab === 'project' }">项目</text>
</view>
<!-- 个人中心 -->
<view class="tab-item" @click="switchTab('profile')" :class="{ 'tab-active': activeTab === 'profile' }">
<image
:src="activeTab === 'profile' ? '/static/img/figma/个人中心.png' : '/static/img/figma/个人中心1.png'"
class="tab-icon"
:class="{ 'icon-active': activeTab === 'profile' }"
mode="aspectFit" />
<text class="tab-text" :class="{ active: activeTab === 'profile' }">个人中心</text>
</view>
</view>
<!-- 帮助中心(大logo,不可点击) -->
<view class="tab-item help-center">
<image src="/static/bar/image10.png" class="help-icon" mode="aspectFit" />
<text class="help-text">帮助中心</text>
</view>
</view>
</template>
<script>
export default {
name: 'CustomTabBar',
props: {
// 当前激活的tab
currentTab: {
type: String,
default: 'home'
},
// 是否在点击时自动导航。为false时仅切换高亮并抛出事件,不跳转页面
navigateOnClick: {
type: Boolean,
default: true
}
},
data() {
return {
activeTab: 'home',
// 防重复点击,避免连续跳转
isNavigating: false,
// 页面栈软阈值,超过后改用reLaunch以清栈
stackSoftLimit: 8
};
},
watch: {
currentTab: {
handler(newVal) {
this.activeTab = newVal;
},
immediate: true
}
},
mounted() {
// 组件挂载时,根据当前页面路径设置激活状态
this.setActiveTabByCurrentPage();
},
methods: {
/**
* 切换tab页面
* @param {string} tabName - tab名称
*/
switchTab(tabName) {
if (this.activeTab === tabName) {
return; // 已经是当前tab,无需切换
}
// 添加触觉反馈
// #ifdef APP-PLUS
plus.device.vibrate(50);
// #endif
this.activeTab = tabName;
// 若关闭自动导航,仅更新激活态并通知父级
if (!this.navigateOnClick) {
this.$emit('tabChange', tabName);
return;
}
// 计算目标地址
let targetUrl = '';
switch (tabName) {
case 'home':
targetUrl = '/pages/index/index';
break;
case 'dynamic':
targetUrl = '/pages/article/index';
break;
case 'project':
targetUrl = '/pages/yzpg/index';
break;
case 'profile':
targetUrl = '/pages/profile/center';
break;
default:
console.warn('未知的tab名称:', tabName);
}
// 已在目标页则不跳转
const pages = getCurrentPages();
if (pages.length) {
const currentRoute = '/' + pages[pages.length - 1].route;
if (currentRoute === targetUrl) {
return;
}
}
// 优先使用navigateTo以获得更丝滑的切换;当接近栈上限时自动切换为reLaunch清栈
this.navigateToSafe('auto', targetUrl);
// 触发父组件事件
this.$emit('tabChange', tabName);
},
/**
* 安全导航(带防抖与自动方式)
* @param {'auto'|'navigateTo'|'redirectTo'|'reLaunch'|'switchTab'} method
* @param {string} url
*/
navigateToSafe(method, url) {
if (this.isNavigating) return;
this.isNavigating = true;
const release = () => {
setTimeout(() => {
this.isNavigating = false;
}, 300);
};
try {
if (method === 'auto') {
const pages = getCurrentPages();
const len = pages.length;
// 近栈上限走reLaunch,正常走navigateTo
if (len >= this.stackSoftLimit) {
uni.reLaunch({ url, complete: release });
} else {
uni.navigateTo({ url, complete: release });
}
} else if (method === 'switchTab') {
uni.switchTab({ url, complete: release });
} else if (method === 'reLaunch') {
uni.reLaunch({ url, complete: release });
} else if (method === 'redirectTo') {
uni.redirectTo({ url, complete: release });
} else {
uni.navigateTo({ url, complete: release });
}
} catch (e) {
release();
}
},
/**
* 根据当前页面路径设置激活的tab
*/
setActiveTabByCurrentPage() {
const pages = getCurrentPages();
if (pages.length === 0) return;
const currentPage = pages[pages.length - 1];
const currentPath = currentPage.route;
// 根据页面路径设置对应的tab状态
if (currentPath.includes('pages/index/index')) {
this.activeTab = 'home';
} else if (
currentPath.includes('pages2agree/pages/article/list') ||
currentPath.includes('pages/article/index')
) {
this.activeTab = 'dynamic';
} else if (currentPath.includes('pages/yzpg/index')) {
this.activeTab = 'project';
} else if (
currentPath.includes('pages/profile/index') ||
currentPath.includes('pages/profile/center') ||
currentPath.includes('pages3profile/pages/profile/logged')
) {
this.activeTab = 'profile';
}
}
}
};
</script>
<style lang="scss" scoped>
.custom-tabbar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 999;
background-color: #ffffff;
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.1);
}
/* 彩色装饰条 - 按设计稿样式 */
.color-bars {
display: flex;
height: 10rpx; /* 按设计稿5px转换为10rpx */
background-color: #ffffff;
}
.color-bar {
height: 100%;
}
.bar-orange {
background-color: #f2981f; /* 按设计稿颜色 */
flex: 350; /* 按设计稿比例 350:248:92 */
}
.bar-brown {
background-color: #ca5737; /* 按设计稿颜色 */
flex: 248; /* 按设计稿比例 */
}
.bar-red {
background-color: #d84a24; /* 按设计稿颜色 */
flex: 92; /* 按设计稿比例 */
}
/* tabBar内容 */
.tabbar-content {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 32rpx 26rpx 32rpx; /* 按设计稿调整内边距 */
background-color: #ffffff;
position: relative; /* 为绝对定位的帮助中心图标提供定位上下文 */
padding-right: 160rpx; /* 右侧留出空间给帮助中心大图标 */
}
.tab-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex: 1;
position: relative;
cursor: pointer;
min-width: 0;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
transform-origin: center;
}
.tab-item:active {
transform: scale(0.95);
}
.tab-item.tab-active {
transform: scale(1.05);
}
.tab-icon {
width: 56rpx; /* 按设计稿28px转换为56rpx */
height: 56rpx; /* 按设计稿28px转换为56rpx */
margin-bottom: 6rpx; /* 调整图标与文字间距 */
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
filter: grayscale(0);
}
.tab-icon.icon-active {
filter: grayscale(0) brightness(1.1);
transform: scale(1.1);
}
.tab-icon-uview {
margin-bottom: 6rpx; /* 调整u-icon与文字间距 */
}
.help-icon {
width: 128rpx; /* 按设计稿64px转换为128rpx */
height: 128rpx; /* 按设计稿64px转换为128rpx */
display: block;
object-fit: contain;
margin-bottom: 6rpx; /* 与其他图标保持一致的间距 */
}
.tab-text {
font-size: 24rpx; /* 按设计稿12px转换为24rpx */
color: #393939; /* 按设计稿未选中颜色 */
line-height: 1;
transition: color 0.3s ease;
letter-spacing: 0.96rpx; /* 按设计稿letterSpacing 0.48转换 */
}
.tab-text.active {
color: #e63e0d; /* 按设计稿选中状态颜色 */
font-weight: 600; /* 选中状态稍微加粗 */
transform: scale(1.05);
}
/* 帮助中心特殊样式 - 按设计稿64x64大图标 */
.help-center {
position: absolute;
right: 32rpx; /* 按设计稿调整距离右边缘的距离 */
bottom: 26rpx; /* 与其他tab项对齐 */
width: 128rpx; /* 按设计稿64px转换为128rpx */
height: 156rpx; /* 为文字留出足够空间 */
z-index: 15; /* 确保在最上层 */
display: flex;
flex-direction: column; /* 垂直排列 */
align-items: center;
justify-content: flex-start; /* 从顶部开始排列 */
}
.help-center .help-icon {
position: relative;
z-index: 10;
opacity: 1;
visibility: visible;
}
/* 帮助中心文字样式 */
.help-text {
font-size: 24rpx; /* 与其他文字保持一致 */
color: #393939; /* 与其他未选中文字保持一致 */
line-height: 1;
text-align: center;
white-space: nowrap;
letter-spacing: 0.96rpx; /* 与其他文字保持一致 */
}
/* 响应式适配 */
@media screen and (max-width: 750rpx) {
.tab-text,
.help-text {
font-size: 20rpx; /* 小屏幕下稍微缩小字体 */
}
.tab-icon {
width: 48rpx; /* 小屏幕下稍微缩小图标 */
height: 48rpx;
}
.tab-icon-uview {
margin-bottom: 4rpx; /* 小屏幕下调整间距 */
}
/* 小屏幕下调整u-icon大小 */
.tab-item .tab-icon-uview /deep/ .u-icon {
font-size: 48rpx !important; /* 小屏幕下图标大小 */
}
.help-center {
width: 112rpx; /* 小屏幕下帮助中心图标容器 */
height: 140rpx; /* 小屏幕下高度 */
right: 24rpx; /* 小屏幕下距离边缘 */
}
.help-icon {
width: 112rpx; /* 小屏幕下帮助中心图标 */
height: 112rpx;
}
.tabbar-content {
padding: 16rpx 24rpx 20rpx 24rpx; /* 小屏幕下调整内边距 */
padding-right: 140rpx; /* 小屏幕下右侧空间 */
}
.color-bars {
height: 8rpx; /* 小屏幕下装饰条稍微细一些 */
}
}
/* 安全区域适配 */
@supports (padding-bottom: env(safe-area-inset-bottom)) {
.tabbar-content {
padding-bottom: calc(26rpx + env(safe-area-inset-bottom));
}
}
</style>