文件
fenleixinxi/uniapp/components/fc-navbar/fc-navbar.vue

260 行
5.9 KiB
Vue

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

<template>
<view class="fc-navbar" :class="[`fc-navbar--${theme}`]">
<!-- 系统状态栏占位 -->
<view class="fc-navbar__status" :style="{ height: statusBarHeight + 'px' }"></view>
<!-- 导航内容条:精确对齐胶囊高度并避让右侧胶囊 -->
<view
class="fc-navbar__bar"
:style="{
height: barHeight + 'px',
paddingRight: capsulePadding ? (capsulePadding + 'px') : '24rpx',
}"
>
<!-- 返回按钮(层级置顶,保证可点击,增加点击热区) -->
<view v-if="showBack" class="fc-navbar__back" @click.stop="onBack">
<text class="fc-navbar__back-icon">‹</text>
</view>
<!-- 标题区域:默认居中显示 -->
<view class="fc-navbar__title" :class="{ 'fc-navbar__title--center': center, 'fc-navbar__title--noback': !showBack }">
<slot>
<text class="fc-navbar__title-text fc-ellipsis">{{ title }}</text>
</slot>
</view>
<!-- 右侧功能区(位于胶囊左侧) -->
<view class="fc-navbar__right">
<slot name="right"></slot>
</view>
</view>
</view>
</template>
<script>
import { getNavMetrics } from '@/common/utils/navbar.js'
export default {
name: 'FcNavbar',
props: {
/** 导航标题文字 */
title: { type: String, default: '' },
/** 风格主题:gradient(主色渐变白字) | white(白底黑字) | transparent(透明底黑字) */
theme: { type: String, default: 'gradient' },
/** 是否展示左侧返回按钮 */
showBack: { type: Boolean, default: false },
/** 标题文字是否居中,默认 true(居中排列) */
center: { type: Boolean, default: true },
/** 自定义返回兜底跳转路径(默认返回首页 /pages/index/index) */
fallbackUrl: { type: String, default: '' },
},
data() {
return {
statusBarHeight: 0,
barHeight: 44,
capsulePadding: 0,
}
},
created() {
this.initMetrics()
},
mounted() {
this.syncStatusBarStyle()
},
methods: {
/**
* 初始化获取设备状态栏和胶囊参数
*/
initMetrics() {
const m = getNavMetrics()
this.statusBarHeight = m.statusBarHeight
this.barHeight = m.navBarHeight
this.capsulePadding = m.capsulePadding
this.syncStatusBarStyle()
},
/**
* 同步原生系统状态栏文字与胶囊颜色
*/
syncStatusBarStyle() {
try {
if (typeof uni !== 'undefined' && uni.setNavigationBarColor) {
uni.setNavigationBarColor({
frontColor: '#000000',
backgroundColor: this.theme === 'gradient' ? '#eafbf1' : '#ffffff',
})
}
} catch (e) {}
},
/**
* 点击返回上一页
* 优先 navigateBack;若无上一页历史(如分享直达、热重载、单页直开)或返回失败,则立即执行安全降级,绝不卡死在原地
*/
onBack() {
this.$emit('back')
let pages = []
try {
pages = (typeof getCurrentPages === 'function') ? getCurrentPages() : []
} catch (e) {
pages = []
}
// 页面栈深度 > 1 时,执行系统级返回
if (pages && pages.length > 1) {
uni.navigateBack({
delta: 1,
fail: () => {
this.fallbackNavigation()
},
})
} else {
// 页面栈仅有 1 页(分享直达、编译刷新),直接执行降级跳转,避免微信底层静默丢弃导致卡死原地
this.fallbackNavigation()
}
},
/**
* 导航失败或无页面栈时的降级逻辑(优先降级至指定 fallbackUrl,否则平滑回到同城首页)
*/
fallbackNavigation() {
const target = this.fallbackUrl || '/pages/index/index'
const tabPages = ['/pages/index/index', '/pages/category/index', '/pages/post/create', '/pages/message/index', '/pages/mine/index']
const isTabBar = tabPages.includes(target)
if (isTabBar) {
uni.switchTab({
url: target,
fail: () => {
uni.reLaunch({ url: target })
},
})
} else {
uni.redirectTo({
url: target,
fail: () => {
uni.reLaunch({
url: target,
fail: () => {
uni.switchTab({ url: '/pages/index/index' })
},
})
},
})
}
},
},
}
</script>
<style lang="scss" scoped>
.fc-navbar {
position: sticky;
top: 0;
z-index: 99;
&--gradient {
background: $fc-gradient-hero;
.fc-navbar__title-text {
color: $fc-text;
}
.fc-navbar__back-icon {
color: $fc-text;
}
}
&--white {
background: #ffffff;
border-bottom: 2rpx solid $fc-border;
.fc-navbar__title-text {
color: $fc-text;
}
.fc-navbar__back-icon {
color: $fc-text;
}
}
&--transparent {
background: transparent;
.fc-navbar__title-text {
color: #0c2b1c;
font-weight: 600;
}
.fc-navbar__back-icon {
color: #0c2b1c;
}
}
}
.fc-navbar__bar {
display: flex;
align-items: center;
padding-left: 12rpx;
box-sizing: border-box;
position: relative;
width: 100%;
}
.fc-navbar__back {
width: 96rpx;
height: 100%;
min-height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
cursor: pointer;
position: relative;
z-index: 25;
transition: opacity 0.15s ease, transform 0.15s ease;
&:active {
transform: scale(0.92);
opacity: 0.75;
}
}
.fc-navbar--white .fc-navbar__back:active {
opacity: 0.7;
}
.fc-navbar__back-icon {
font-size: 54rpx;
line-height: 1;
font-weight: 400;
margin-top: -6rpx;
pointer-events: none;
}
.fc-navbar__title {
flex: 1;
min-width: 0;
display: flex;
align-items: center;
padding-left: 4rpx;
&--noback {
padding-left: 20rpx;
}
&--center {
position: absolute;
left: 80rpx;
right: 80rpx;
top: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
pointer-events: none;
padding: 0 80rpx;
box-sizing: border-box;
z-index: 1;
}
}
.fc-navbar__title-text {
font-size: 34rpx;
font-weight: 700;
letter-spacing: 0.5rpx;
max-width: 100%;
text-align: center;
}
.fc-navbar__right {
position: relative;
z-index: 3;
display: flex;
align-items: center;
flex-shrink: 0;
min-width: 68rpx;
justify-content: flex-end;
}
</style>