移动端(uniapp)底部导航栏切换

背景

        初项目构想时,以两个不同的角色分不同的项目提供进入平台的开发目标来实现,在实际开发过程中被告知业务方只提供一个入口,所以只能把现有的两个项目代码进行合并,前提底部导航栏按不同端进行变更,一个中间按钮凸起,一个没有,为了完成这个功能进行了探索。

构思

        实现思路首先创建一个基础底部配置文件和一个tabbar页面,用来控制业务端的切换变更的。再者按实际所需创建多个底部切换页面,说到这里那我们就可以利用uniapp提供的组件easycom模式来解决子组件多引用的问题。设想就是一个页面共用多个端使用v-if或者v-show的形式进行切换,最后在外层再做一层父类统一入口控制整个tabbar的切换。

实现

第一步,在根目录components(没有就新建一个components目录)下新建一个文件夹(ctab),然后分别新建一个ctab.vue组件和一个ctab-config.js文件。这样组件可以在其他页面上引用,js文件主要用于做配置。

第二步,新建底部页面组件,组件最多限制5个tab组件,然后需要通过easycom占位来实现,所以你需要几个tab组件就在components目录下建几个组件命名为ctab{n}。如下所示:

第三步,这里就直接贴ctab.vue的实现代码,想尝试就直接拷贝试一下,举个栗子。

<template>
	<view>
		<!--中间按钮凸起模式-->
		<block v-if="midBtn && midBtn.show">
			<!--凸起模式最多展示四个-->
			<block v-if="tabs.length < 4">
				<ctab1 v-show="sindex == 0"></ctab1>
				<ctab2 v-show="sindex == 2"></ctab2>
			</block>
			<block v-else="tabs.length >= 4">
				<ctab1 v-show="sindex == 0"></ctab1>
				<ctab2 v-show="sindex == 1"></ctab2>
				<ctab3 v-show="sindex == 3"></ctab3>
				<ctab4 v-show="sindex == 4"></ctab4>
			</block>
			<view class="tabbar">
				<!--中间按钮凸起模式tab为3个或者5个-->
				<view class="tab-item"
					  v-for="(item,index) in (tabs.length < 4 ? 3 : 5)"
					  :key="item"
					  @click="handleTabClick(index)">
					  <!--中间按钮凸起显示图片和文字-->
					<block v-if="index == floor">
						<view :style="'bottom: calc('+(midBtn.offset ? midBtn.offset : '50rpx')+' + env(safe-area-inset-bottom));width: '+(midBtn.width ? midBtn.width : '150rpx')+';height: '+(midBtn.width ? midBtn.width : '150rpx')+';background:'+(midBtn.background ? midBtn.background : '#ffffff')"
								class="mid-btn">
							<image :src="midBtn.icon"
								   :style="'width: '+(midBtn.iconwidth ? midBtn.iconwidth : midBtn.width)+';height: '+(midBtn.iconheight ? midBtn.iconheight : midBtn.width)+';'"
								   @click="handleMidBtn"/>
						</view>
						<text class="mid-text"
							  :style="'margin-top: '+(midBtn.textoffset ? midBtn.textoffset : '50rpx;')"
							  v-show="midBtn.showtext">{{midBtn.text}}</text>
					</block>
					<!--普通tab这里需要注意index选择-->
					<block v-else>
						<view class="c-tab-item">
							<text :class="'tab-iconfont iconfont '+(tabs[index < floor ? index : index-1].iconfont)"
								  :style="'color:'+(sindex == index ? scolor : color)"
								  v-if="tabs[index < floor ? index : index-1].iconfont"/>
							<image :src="sindex == index ? tabs[index < floor ? index : index-1].iconSelect : tabs[index < floor ? index : index-1].icon"
								   class="tab-icon"
								   v-else/>
							<text class="tab-text"
								  :style="'color:'+(sindex == index ? scolor : color)">{{tabs[index < floor ? index : index-1].text}}</text>
							<view class="corner"
								  v-show="tabs[index < floor ? index : index-1].mark > 0">{{tabs[index < floor ? index : index-1].mark > 99 ? '99+' : tabs[index < floor ? index : index-1].mark}}</view>
						</view>
					</block>
				</view>
			</view>
		</block>
		<!--普通模式-->
		<block v-else>
			<block v-if="tabs.length == 1">
				<ctab1 v-show="sindex == 0 && tabs[0].show"></ctab1>
			</block>
			<block v-else-if="tabs.length == 2">
				<ctab1 v-show="sindex == 0 && tabs[0].show"></ctab1>
				<ctab2 v-show="sindex == 1 && tabs[1].show"></ctab2>
			</block>
			<block v-else-if="tabs.length == 3">
				<ctab1 v-show="sindex == 0 && tabs[0].show"></ctab1>
				<ctab2 v-show="sindex == 1 && tabs[1].show"></ctab2>
				<ctab3 v-show="sindex == 2 && tabs[2].show"></ctab3>
			</block>
			<block v-else-if="tabs.length == 4">
				<ctab1 v-show="sindex == 0 && tabs[0].show"></ctab1>
				<ctab2 v-show="sindex == 1 && tabs[1].show"></ctab2>
				<ctab3 v-show="sindex == 2 && tabs[2].show"></ctab3>
				<ctab4 v-show="sindex == 3 && tabs[3].show"></ctab4>
			</block>
			<block v-else-if="tabs.length >= 5">
				<ctab1 v-show="sindex == 0 && tabs[0].show"></ctab1>
				<ctab2 v-show="sindex == 1 && tabs[1].show"></ctab2>
				<ctab3 v-show="sindex == 2 && tabs[2].show"></ctab3>
				<ctab4 v-show="sindex == 3 && tabs[3].show"></ctab4>
				<ctab5 v-show="sindex == 4 && tabs[4].show"></ctab5>
			</block>
			<view class="tabbar">
				<view class="tab-item"
					  v-for="(item,index) in tabs"
					  :key="item.text"
					  v-show="item.show"
					  @click="handleTabClick(index)">
					<view class="c-tab-item">
						<text :class="'tab-iconfont iconfont '+(item.iconfont)"
							  :style="'color:'+(sindex == index ? scolor : color)"
							  v-if="item.iconfont"/>
						<image :src="sindex == index ? item.iconSelect : item.icon"
							   class="tab-icon"
							   v-else/>
						<text class="tab-text"
							  :style="'color:'+(sindex == index ? scolor : color)">{{item.text}}</text>
						<view class="corner"
							  v-show="item.mark > 0">{{item.mark > 99 ? '99+' : item.mark}}</view>
					</view>
				</view>
			</view>
		</block>
	</view>
</template>
<script>
	//读取配置
	import ctabbar from './ctab-config.js'
	export default {
		name: "ctab",
		data() {
			return {
				tabs: [],
				color: '',
				scolor: '',
				midBtn: {},
				sindex: 0,
				floor: -1,//midButton开启时使用
			}
		},
		mounted() {
			let tabbar = ctabbar.tabbar
			this.color = tabbar.color
			this.scolor = tabbar.selectcolor
			if(tabbar.midButton && tabbar.midButton.show && tabbar.tabs.length < 2){
				throw new Error('midButton模式开启,配置tab选项不能少于2个')
			}
			if(tabbar.midButton && tabbar.midButton.show){
				let mlength = tabbar.tabs.length < 4 ? 3 : 5
				this.floor = Math.floor(mlength/2)
			}
			//普通模式,设置选中的tab项
			let tablen = tabbar.tabs.length
			if(!tabbar.midButton.show){
				if(!tabbar.tabs[0].show){
					this.sindex ++
					if(tablen >= 2 && !tabbar.tabs[1].show){
						this.sindex ++
						if(tablen >= 3 && !tabbar.tabs[2].show){
							this.sindex ++
							if(tablen >= 4 && !tabbar.tabs[3].show){
								this.sindex ++
								if(tablen >= 5 && !tabbar.tabs[4].show){
									throw new Error('tab不能全部隐藏')
								}
							}
						}
					}
				}
			}
			if(tabbar.tabs.length <= 5){
				this.tabs = tabbar.tabs
			}else {
				this.tabs = tabbar.tabs.slice(0,5)
			}
			this.midBtn = tabbar.midButton
		},
		methods: {
			setTheme(color){
				this.scolor = color
				this.midBtn.background = color
			},
			//设置tab隐藏和显示,midButton模式失效
			setTabVisible(index,visible){
				if(this.tabs[index]){
					this.tabs[index].show = visible
				}
			},
			//设置角标
			setCorner(index,num){
				if(this.tabs[index]){
					this.tabs[index].mark = num
				}
			},
			handleTabClick(tab){
				if(this.midBtn && this.midBtn.show){
					if(tab == this.floor){
						return
					}
				}
				this.sindex = tab
				let rindex = tab
				if(this.midBtn && this.midBtn.show){
					if(tab > this.floor){
						rindex --
					}
				}
				this.$emit('tabClick',rindex)
			},
			handleMidBtn(){
				this.$emit('midClick')
			}
		}
	}
</script>
<style>
	/*这里引入字体图标,如果使用字体图标的话*/
	@import '@/common/font/iconfont.css';
	.tabbar {
		position: fixed;
		z-index: 99;
		width: 100%;
		height: 100rpx;
		background-color: #ffffff;
		bottom: 0;
		left: 0;
		box-shadow: 0rpx 4rpx 8rpx 0rpx rgba(0,0,0,0.5);
		border-radius: 0px 0px 0px 0px;
		opacity: 1;
		display: flex;
		flex-direction: row;
		align-items: center;
		justify-content: center;
		padding-bottom: constant(safe-area-inset-bottom);
		padding-bottom: env(safe-area-inset-bottom);
		box-sizing: content-box;
	}
	.tab-item {
		flex: 1;
		height: 100rpx;
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
	}
	.c-tab-item {
		height: 120rpx;
		display: flex;
		flex-direction: column;
		width: 120rpx;
		align-items: center;
		justify-content: center;
		position: relative;
	}
	.tab-icon {
		width: 45rpx;
		height: 45rpx;
	}
	.tab-iconfont {
		font-size: 45rpx;
		font-weight: bold;
	}
	.tab-text {
		font-size: 26rpx;
		color: #333333;
		margin-top: 5rpx;
	}
	.mid-btn {
		position: absolute;
		display: flex;
		flex-direction: row;
		justify-content: center;
		align-items: center;
		background-color: red;
		border-radius: 50%;
	}
	.mid-text {
		font-size: 25rpx;
		color: #999999;
	}
	.corner {
		text-align: center;
		width: 45rpx;
		height: 45rpx;
		position: absolute;
		background-color: red;
		border-radius: 50%;
		color: white;
		font-size: 20rpx;
		font-weight: bold;
		top: 5rpx;
		right: 0;
		display: flex;
		flex-direction: row;
		justify-content: center;
		align-items: center;
	}
</style>

第四步,这里就是配置文件ctab-config.js代码,按需进行调整,举个栗子。

1)通过修改配置文件中的midButton中的show属性来开启是否中间按钮凸起。
2)通过配置或者动态修改tabs中tab obj中的show属性来动态形式和隐藏某个tab。

2)其他字段配置按需进行配置。

var tabbar = {
	/*开启midButton模式时取前两个或者前四个显示,其他忽略*/
	midButton: {
		show: true,//是否是中间凸起模式
		width: '153rpx',//不填默认150rpx 中间按钮大小
		iconwidth: '67rpx',//不填默认150rpx 中间图标大小
		iconheight: '60rpx',
		offset: '40rpx',//不填默认50rpx
		background: '#F7D456',//中间按钮背景颜色
		text: '拍一拍',
		textoffset: '50rpx',//不填默认50rpx
		showtext: false,
		icon: '../../static/tabbar/camera.png'
	},
	color: '#333333',//未选中颜色
	selectcolor: '#F7D456',//选中颜色
	/*tabs最多5个,超过5个超过的部分会被忽略,show属性用来控制tab显示隐藏,midButton开启时失效,iconfont优先,没有就使用icon*/
	tabs: [{
		icon: '../../static/tabbar/main_tab_home_normal.png',
		iconSelect: '../../static/tabbar/main_tab_home_select.png',
		text: '首页',
		iconfont: '',
		show: true,
		mark: 0//角标数量,小于等于0不显示
	}, {
		icon: '../../static/tabbar/main_tab_task_normal.png',
		iconSelect: '../../static/tabbar/main_tab_task_select.png',
		text: '任务',
		iconfont: '',
		show: true,
		mark: 100
	}, {
		icon: '../../static/tabbar/main_tab_my_normal.png',
		iconSelect: '../../static/tabbar/main_tab_my_select.png',
		text: '我的',
		iconfont: 'icon-wode',//注意配置字体图标会优先使用字体图标,这里是示例
		show: true,
		mark: 9
	}]
}

module.exports = {
	tabbar
}

第五步,tab切换1~n(ctab{1~n})的页面就是自己业务功能页面了,举个栗子。

<template>
	<!--用户端-->
	<view v-if="isUser"></view>
	<!--管理端-->
	<view v-else></view>
</template>

<script>
	export default {
		data() {
			return {
				isUser: true
			}
		}
	}
</script>

<style>
</style>

第六步,最后就是定义统一入口的页面,用于调用底部tabbar导航栏了,举个栗子。

<template>
	<ctab @midClick='midClick' @tabClick='tabClick' ref="ctab"/>
</template>

<script>
	export default {
		data() {
			return {}
		},
		onLoad() {},
		methods: {
			//凸起按钮点击事件
			midClick(){
				console.log('midClick')
			},
			//tab切换点击事件
			tabClick(tab){
				console.log('tabClick',tab)
			}
		}
	}
</script>

<style>
	page {
		width: 100%;
		height: 100%;
	}
</style>

最后 

        写到这里就宣告结束了。在此祝大家身体健康,越码越浓后会有期~~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值