Files
lucky_shop/components/ns-mp-navbar/ns-mp-navbar.vue
jing df5be6045b common\js\floating-button.js
common\js\startup-ad.js
components\ns-mp-navbar\ns-mp-navbar.vue
2026-10-12 15:41:28 +08:00

248 lines
6.0 KiB
Vue
Raw 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>
<!--
自绘导航栏:把原生导航栏左侧的 "<" 换成 "‹ 返回" 文字
背景:微信小程序原生导航栏的返回按钮是客户端系统控件,pages.json 的任何配置、
官方 <navigation-bar> 组件(只支持 title / front-color / background-color / loading)
都无法修改它的内容。要显示"返回"二字,只能关闭原生栏后自绘。
使用前提:
1. 该页面在 pages.json 中开启 "navigationStyle": "custom"(组件只负责渲染,不自动改配置)
2. 该页面不能是 web-view 页面 —— 官方明确 "navigationStyle: custom 对 web-view 组件无效",
且 web-view 会铺满整页覆盖其他组件,自绘的导航栏不会显示
布局:组件是普通流式节点(非 fixed),放在页面顶部即可把内容自然下推。
-->
<view
v-if="visible"
class="ns-mp-navbar"
:class="{ 'is-fixed': fixed }"
:style="{ background: bgColor, paddingTop: statusBarHeight + 'px' }"
>
<view class="ns-mp-navbar__inner" :style="{ height: navBarHeight + 'px' }">
<view class="ns-mp-navbar__side">
<view
v-if="showBack && canBack"
class="ns-mp-navbar__back"
hover-class="ns-mp-navbar__back--hover"
hover-stay-time="80"
@tap="onBack"
>
<text class="iconfont icon-back_light ns-mp-navbar__arrow" :style="{ color }"></text>
<text class="ns-mp-navbar__text" :style="{ color }">{{ backText }}</text>
</view>
<slot name="left"></slot>
</view>
<view class="ns-mp-navbar__title" :style="{ color }">{{ title }}</view>
<view class="ns-mp-navbar__side ns-mp-navbar__side--right">
<slot name="right"></slot>
</view>
</view>
</view>
</template>
<script>
// 状态栏 / 胶囊信息:用于算出与原生导航栏一致的布局高度
let systemInfo = {};
let menuButtonInfo = { top: 0, height: 32, right: 0, width: 0 };
try {
systemInfo = uni.getSystemInfoSync() || {};
} catch (e) {
systemInfo = {};
}
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ || MP-HARMONY
try {
menuButtonInfo = uni.getMenuButtonBoundingClientRect() || menuButtonInfo;
} catch (e) {}
// #endif
export default {
name: 'ns-mp-navbar',
props: {
// 中间标题
title: {
type: String,
default: ''
},
// 导航栏背景色
bgColor: {
type: String,
default: '#ffffff'
},
// 文字 / 图标颜色
color: {
type: String,
default: '#000000'
},
// 返回按钮文字(原生导航栏只能显示 "<",这里可自定义)
backText: {
type: String,
default: '返回'
},
// 是否显示返回按钮
showBack: {
type: Boolean,
default: true
},
// 是否悬浮在页面之上(沉浸式页面用;false 时占文档流,会把内容往下推)
fixed: {
type: Boolean,
default: false
},
// 无页面栈时(如分享直接进入)跳转的首页地址
homePath: {
type: String,
default: '/pages/index/index'
},
// 自定义返回逻辑
customBack: {
type: Function,
default: null
},
// H5 端是否渲染(H5 由 uni-app 自带头像栏,默认不渲染,避免双导航栏)
showOnH5: {
type: Boolean,
default: false
}
},
data() {
return {
statusBarHeight: systemInfo.statusBarHeight || 20,
navBarHeight: 44,
visible: true,
// 页面栈里是否还有上级页面(无上级页面时原生导航栏也不显示返回键)
canBack: true
};
},
computed: {
// 导航栏总高度(状态栏 + 内容区),页面如需自行占位可取该值
navbarHeight() {
return this.statusBarHeight + this.navBarHeight;
}
},
created() {
// 导航栏内容区高度:MP 端按胶囊位置推算,与原生导航栏保持一致;其余端用 44
// #ifdef MP
const statusBarHeight = this.statusBarHeight;
const h = (menuButtonInfo.top - statusBarHeight) * 2 + menuButtonInfo.height;
// 兜底:胶囊信息异常时退回 44
this.navBarHeight = h > 0 ? h : 44;
// #endif
// #ifdef H5
this.visible = this.showOnH5;
// #endif
},
mounted() {
// 页面栈只有一页(分享/扫码直达)时,原生导航栏也无返回键,这里保持一致
const pages = (typeof getCurrentPages === 'function' && getCurrentPages()) || [];
this.canBack = pages.length > 1;
// 把实际总高度抛给页面,便于页面修正内容区高度,避免多出一屏空白
this.$emit('ready', this.statusBarHeight + this.navBarHeight);
},
methods: {
onBack() {
if (typeof this.customBack === 'function') {
this.customBack();
return;
}
const pages = (typeof getCurrentPages === 'function' && getCurrentPages()) || [];
if (pages.length > 1) {
uni.navigateBack();
return;
}
// 顶层页面(分享 / 扫码直接进入):回首页
uni.switchTab({
url: this.homePath,
fail: () => {
uni.reLaunch({ url: this.homePath });
}
});
}
}
};
</script>
<style scoped lang="scss">
.ns-mp-navbar {
width: 100%;
box-sizing: border-box;
}
.ns-mp-navbar.is-fixed {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 990;
}
.ns-mp-navbar__inner {
position: relative;
display: flex;
align-items: center;
width: 100%;
box-sizing: border-box;
}
.ns-mp-navbar__side {
position: relative;
z-index: 1;
display: flex;
align-items: center;
height: 100%;
min-width: 160rpx;
padding-left: 20rpx;
box-sizing: border-box;
}
.ns-mp-navbar__side--right {
justify-content: flex-end;
padding-left: 0;
padding-right: 20rpx;
}
.ns-mp-navbar__back {
display: flex;
align-items: center;
padding: 10rpx 20rpx 10rpx 0;
}
.ns-mp-navbar__back--hover {
opacity: 0.5;
}
.ns-mp-navbar__arrow {
font-size: 34rpx;
line-height: 1;
}
.ns-mp-navbar__text {
margin-left: 2rpx;
font-size: 30rpx;
line-height: 1;
}
.ns-mp-navbar__title {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
padding: 0 160rpx;
font-size: 32rpx;
line-height: 1.2;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>