custom-navbar.vue 5.39 KB
<template>
	<view class="custom-navbar-wrapper">
		<!-- 背景图片(可选) -->
		<view v-if="showBackground && backgroundImage" class="navbar-bg-image">
			<image :src="backgroundImage" mode="aspectFill" class="bg-image"></image>
		</view>
		
		<!-- 导航栏头部 -->
		<view class="navbar-head" :style="{backgroundColor: backgroundColor}">
			<!-- 状态栏占位 -->
			<view :style="{height: stateBarHeight + 'px'}"></view>
			
			<!-- 导航栏内容区域 -->
			<view :style="{height: navBarHeight + 'px'}" class="navbar-content">
				<!-- 左侧插槽 -->
				<view class="navbar-left">
					<slot name="left">
						<!-- 默认左侧内容为空 -->
					</slot>
				</view>
				
				<!-- 中间标题 -->
				<view class="navbar-title" :style="{color: titleColor}">{{ title }}</view>
				
				<!-- 右侧插槽 -->
				<view class="navbar-right">
					<slot name="right">
						<!-- 默认右侧内容为空 -->
					</slot>
				</view>
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		name: 'CustomNavbar',
		props: {
			// 标题
			title: {
				type: String,
				default: ''
			},
			// 背景图片
			backgroundImage: {
				type: String,
				default: ''
			},
			// 是否显示背景
			showBackground: {
				type: Boolean,
				default: false
			},
			// 背景高度(rpx)
			backgroundHeight: {
				type: Number,
				default: 290
			},
			// 标题颜色
			titleColor: {
				type: String,
				default: '#fff'
			},
			// 导航栏背景色
			backgroundColor: {
				type: String,
				default: 'rgba(255, 255, 255, 0)'
			}
		},
		data() {
			return {
				stateBarHeight: 0, // 状态栏高度
				navBarHeight: 44 // 导航栏内容区域高度
			}
		},
		computed: {
			// 将px转换为rpx
			customBarHeightRpx() {
				try {
					const systemInfo = uni.getSystemInfoSync()
					const pxToRpx = 750 / systemInfo.windowWidth
					const totalHeight = this.stateBarHeight + this.navBarHeight
					return Math.ceil(totalHeight * pxToRpx)
				} catch (e) {
					// 如果获取失败,使用默认转换比例 2
					const totalHeight = this.stateBarHeight + this.navBarHeight
					return Math.ceil(totalHeight * 2)
				}
			},
			// 导航栏总高度(rpx),供外部使用
			navbarHeightRpx() {
				return this.customBarHeightRpx
			}
		},
		created() {
			// 获取设备信息
			this.getStateBarHeight()
			
			// 获取小程序右侧悬浮按钮
			// #ifdef MP-WEIXIN
			this.getMenuButtonInfo()
			// #endif
		},
		mounted() {
			// 通知父组件导航栏高度变化
			this.$nextTick(() => {
				this.$emit('height-change', {
					px: this.stateBarHeight + this.navBarHeight,
					rpx: this.customBarHeightRpx
				})
			})
		},
		methods: {
			// 获取状态栏高度
			getStateBarHeight() {
				const info = uni.getSystemInfoSync()
				this.stateBarHeight = info.statusBarHeight || 0
				
				// #ifdef H5
				// H5平台:没有状态栏
				this.stateBarHeight = 0
				this.navBarHeight = 44
				// #endif
				
				// #ifdef APP-PLUS
				// APP平台:使用系统状态栏高度
				this.navBarHeight = 44
				// #endif
			},
			
			// 获取小程序胶囊按钮信息(仅微信小程序)
			// #ifdef MP-WEIXIN
			getMenuButtonInfo() {
				try {
					const menuButtonInfo = uni.getMenuButtonBoundingClientRect()
					if (menuButtonInfo && menuButtonInfo.top !== undefined && menuButtonInfo.height && menuButtonInfo.bottom !== undefined) {
						// 胶囊按钮距离状态栏的距离
						const paddingSize = menuButtonInfo.top - this.stateBarHeight
						
						// 导航栏内容区域高度 = (胶囊按钮底部 - 状态栏高度) + 上边距
						// 这样确保内容区域与胶囊按钮对齐
						this.navBarHeight = (menuButtonInfo.bottom - this.stateBarHeight) + paddingSize
					} else {
						// 如果获取失败,使用默认值44px
						this.navBarHeight = 44
					}
				} catch (e) {
					console.error('获取胶囊按钮信息失败:', e)
					this.navBarHeight = 44
				}
				
				// 通知父组件导航栏高度变化
				this.$nextTick(() => {
					this.$emit('height-change', {
						px: this.stateBarHeight + this.navBarHeight,
						rpx: this.customBarHeightRpx
					})
				})
			},
			// #endif
			
			// #ifndef MP-WEIXIN
			// 其他小程序平台(支付宝、百度等)使用固定值
			getMenuButtonInfo() {
				this.navBarHeight = 44
			},
			// #endif
		}
	}
</script>

<style lang="scss" scoped>
	.custom-navbar-wrapper {
		position: relative;
		width: 100%;
		z-index: 100;
	}

	.navbar-bg-image {
		position: absolute;
		top: 0;
		left: 0;
		width: 100%;
		height: 240rpx;
		overflow: hidden;
		border-radius: 0 0 40rpx 40rpx;
		z-index: 1;
		
		.bg-image {
			width: 100%;
			height: 100%;
		}
	}

	.navbar-head {
		position: fixed;
		left: 0;
		top: 0;
		z-index: 100;
		width: 100%;
		box-sizing: border-box;
		
		.navbar-content {
			width: 100%;
			display: flex;
			align-items: center;
			justify-content: space-between;
			box-sizing: border-box;
		}
		
		.navbar-left {
			width: 20%;
			display: flex;
			align-items: center;
			justify-content: flex-start;
			padding-left: 20rpx;
			height: 100%;
		}
		
		.navbar-title {
			flex: 1;
			font-size: 32rpx;
			font-weight: 600;
			text-align: center;
			color: #fff;
			display: flex;
			align-items: center;
			justify-content: center;
			height: 100%;
		}
		
		.navbar-right {
			width: 20%;
			display: flex;
			align-items: center;
			justify-content: flex-end;
			padding-right: 20rpx;
			height: 100%;
		}
	}
</style>