Files
2026-08-20 10:22:55 +08:00

206 lines
4.7 KiB
Vue
Raw Permalink 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.
<template>
<!--
外层容器:默认为空壳,仅用于包裹
如果开启 placeholder,则通过 style 撑开高度
-->
<view
class="nav-wrapper"
:style="{
background: props.bgColor
}"
>
<!-- 【占位层】:只在开启 placeholder 时渲染,纯粹为了撑高度 -->
<view
v-if="placeholder"
class="nav-placeholder"
:style="{ height: totalNavHeight + 'px' }"
></view>
<!-- 【功能层】:Fixed 定位,脱离文档流,永远显示在最上层 -->
<view class="nav-fixed-layer" :style="fixedLayerStyle">
<!-- 状态栏区域(用于沉浸式背景填充) -->
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
<!-- 导航栏主体 -->
<view class="nav-main" :style="{ height: navBarHeight + 'px' }">
<!-- 左侧 -->
<view class="nav-left" :style="{ width: leftWidth + 'px' }">
<slot name="left">
<view class="back-btn" @click="goBack" v-if="showBack">
<uni-icons type="left" size="22" color="inherit"></uni-icons>
<text v-if="backText" class="back-text">{{ backText }}</text>
</view>
</slot>
</view>
<!-- 中间 -->
<view class="nav-center">
<slot name="center">
<text class="nav-title" :style="{ color: titleColor }">{{ title }}</text>
</slot>
</view>
<!-- 右侧 -->
<view class="nav-right" :style="{ width: rightWidth + 'px' }">
<slot name="right"></slot>
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
// --- Props ---
interface Props {
title?: string
bgColor?: string
titleColor?: string
showBack?: boolean
backText?: string
placeholder?: boolean // 是否开启“占位层”
}
const props = withDefaults(defineProps<Props>(), {
title: '标题',
bgColor: 'transparent',
titleColor: '#333333',
showBack: true,
backText: '',
placeholder: true // 默认开启占位,防止遮挡
})
const emits = defineEmits(['clickLeft'])
// --- 状态 ---
const statusBarHeight = ref(0)
const navBarHeight = ref(44)
const capsuleHeight = ref(0)
const gap = ref(0)
// --- 计算属性 ---
const totalNavHeight = computed(() => statusBarHeight.value + navBarHeight.value)
// 2. 功能层样式:Fixed 定位,负责视觉展示
const fixedLayerStyle = computed(() => ({
backgroundColor: props.bgColor,
zIndex: 9999
}))
const leftWidth = computed(() => {
// #ifdef MP-WEIXIN
return capsuleHeight.value || 40
// #endif
// #ifndef MP-WEIXIN
return 40
// #endif
})
const rightWidth = computed(() => leftWidth.value)
// --- 生命周期 ---
onMounted(() => {
initNavBar()
})
// --- 方法 ---
const initNavBar = () => {
const windowInfo = uni.getWindowInfo()
statusBarHeight.value = windowInfo.statusBarHeight || 0
// #ifdef MP-WEIXIN
const menuInfo = uni.getMenuButtonBoundingClientRect()
if (menuInfo.top && menuInfo.height) {
gap.value = menuInfo.top - statusBarHeight.value
navBarHeight.value = (gap.value > 0 ? gap.value : 4) * 2 + menuInfo.height
capsuleHeight.value = menuInfo.height
} else {
navBarHeight.value = 44
capsuleHeight.value = 0
}
// #endif
// #ifndef MP-WEIXIN
navBarHeight.value = 44
// #endif
}
const goBack = () => {
emits('clickLeft')
}
</script>
<style scoped>
/* 【外层容器】:文档流中的普通盒子 */
.nav-wrapper {
width: 100%;
/* 这里不设置 position,让它老老实实在文档流里呆着 */
}
/* 【占位层】:看不见摸不着,只有高度 */
.nav-placeholder {
width: 100%;
/* 仅通过 height 撑开父容器 */
}
/* 【功能层】:脱离文档流,悬浮在页面上方 */
.nav-fixed-layer {
position: fixed;
top: 0;
left: 0;
width: 100vw;
box-sizing: border-box;
/* 视觉上的背景色在这里 */
}
.status-bar {
width: 100%;
pointer-events: none; /* 防止拦截点击 */
}
.nav-main {
display: flex;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
padding: 0 20rpx;
padding-top: 10rpx;
}
.nav-left,
.nav-right {
display: flex;
height: 100%;
}
.nav-center {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
height: 100%;
overflow: hidden;
}
.nav-title {
font-size: 32rpx;
font-weight: bold;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #000000;
line-height: 40rpx;
text-align: center;
}
.back-btn {
display: flex;
align-items: center;
font-weight: 600;
}
.back-text {
font-size: 28rpx;
margin-left: 4rpx;
color: inherit;
}
</style>