Files

204 lines
4.9 KiB
Vue
Raw Permalink Normal View History

2026-08-19 08:17:18 +08:00
<template>
2026-08-20 19:15:41 +08:00
<!-- PC 大屏不支持自定义顶栏,留给微信拖拽区;标题写到系统导航栏 -->
<view v-if="!pcMode" class="tn">
2026-08-19 08:17:18 +08:00
<view class="tn-fixed" :class="'tn--' + mode" :style="barStyle">
2026-08-20 19:15:41 +08:00
<view class="tn-status" :style="statusStyle" />
<view class="tn-inner" :style="innerStyle">
2026-08-19 08:17:18 +08:00
<view class="tn-left" @tap="onBack">
<u-icon v-if="showBack" name="arrow-left" :color="text" size="20" />
</view>
<text class="tn-title" :style="{ color: text }">{{ title }}</text>
<view class="tn-right">
<slot name="right" />
</view>
</view>
</view>
2026-08-20 19:15:41 +08:00
<view class="tn-ph" :style="phStyle" />
2026-08-19 08:17:18 +08:00
</view>
</template>
<script>
import { getActiveLayout, getActiveVars } from '@/utils/theme'
2026-08-20 19:15:41 +08:00
import { isPcWeixin } from '@/utils/device'
2026-08-19 08:17:18 +08:00
/**
* 自定义顶栏:色跟主题 token,形态跟 chrome.navbar
*/
export default {
name: 'ThemeNavbar',
props: {
title: { type: String, default: '' },
showBack: { type: Boolean, default: true },
},
data() {
return {
statusBarHeight: 20,
innerHeight: 44,
2026-08-20 19:15:41 +08:00
// 量不到胶囊时按常见胶囊宽度让开,避免右侧按钮被挡住
capsulePadRight: 96,
2026-08-19 08:17:18 +08:00
mode: 'solid',
surface: '#ffffff',
text: '#1C1917',
border: '#E7DFD3',
}
},
computed: {
2026-08-20 19:15:41 +08:00
pcMode() {
return isPcWeixin()
},
2026-08-19 08:17:18 +08:00
navHeight() {
return this.statusBarHeight + this.innerHeight
},
barStyle() {
return {
background: this.surface,
borderColor: this.border,
}
},
2026-08-20 19:15:41 +08:00
statusStyle() {
return {
height: this.statusBarHeight + 'px',
}
},
/** 占位跟固定栏同高,用像素避免 env(safe-area) 在安卓微信为 0 时切内容 */
phStyle() {
return {
height: this.navHeight + 'px',
width: '100%',
}
},
innerStyle() {
return {
height: this.innerHeight + 'px',
paddingRight: this.capsulePadRight + 'px',
}
},
},
watch: {
title: {
immediate: true,
handler(val) {
this.syncPcTitle(val)
},
},
2026-08-19 08:17:18 +08:00
},
created() {
this.measure()
this.pullTheme()
2026-08-20 19:15:41 +08:00
this.syncPcTitle(this.title)
},
mounted() {
// 部分机型 created 时 statusBarHeight 还是 0,挂载后再量一次
this.measure()
2026-08-19 08:17:18 +08:00
},
beforeUpdate() {
this.pullTheme()
},
methods: {
measure() {
try {
const sys = uni.getSystemInfoSync()
this.statusBarHeight = sys.statusBarHeight || 20
const menu = uni.getMenuButtonBoundingClientRect && uni.getMenuButtonBoundingClientRect()
if (menu && menu.height) {
this.innerHeight = (menu.top - this.statusBarHeight) * 2 + menu.height
2026-08-20 19:15:41 +08:00
const winW = Number(sys.windowWidth) || 375
this.capsulePadRight = Math.max(8, Math.round(winW - menu.left + 8))
2026-08-19 08:17:18 +08:00
}
} catch (err) {
this.statusBarHeight = 20
this.innerHeight = 44
}
},
pullTheme() {
const vars = getActiveVars() || {}
const chrome = ((getActiveLayout() || {}).chrome) || {}
this.mode = chrome.navbar || 'solid'
this.surface = vars['--color-surface'] || '#ffffff'
this.text = vars['--color-text'] || '#1C1917'
this.border = vars['--color-border'] || '#E7DFD3'
2026-08-20 19:15:41 +08:00
this.syncPcBarColor()
},
/** 系统导航栏底色跟主题表面色对齐,避免 PC 顶栏发灰 */
syncPcBarColor() {
if (!isPcWeixin()) return
const bg = this.surface || '#ffffff'
uni.setNavigationBarColor({
frontColor: '#000000',
backgroundColor: bg,
})
},
/** PC 走系统导航栏,动态标题(如编辑地址)要写过去 */
syncPcTitle(val) {
if (!isPcWeixin() || !val) return
uni.setNavigationBarTitle({ title: String(val) })
2026-08-19 08:17:18 +08:00
},
onBack() {
if (!this.showBack) return
const pages = getCurrentPages()
if (pages.length > 1) {
uni.navigateBack({ delta: 1 })
return
}
uni.switchTab({ url: '/pages/goods/index' })
},
},
}
</script>
<style scoped>
2026-08-20 19:15:41 +08:00
.tn {
display: block;
width: 100%;
}
.tn-ph {
display: block;
width: 100%;
overflow: hidden;
}
2026-08-19 08:17:18 +08:00
.tn-fixed {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 200;
}
.tn--line {
border-bottom: 1rpx solid;
}
.tn-inner {
display: flex;
align-items: center;
2026-08-20 19:15:41 +08:00
position: relative;
padding-left: 16rpx;
box-sizing: border-box;
2026-08-19 08:17:18 +08:00
}
2026-08-20 19:15:41 +08:00
.tn-left {
2026-08-19 08:17:18 +08:00
width: 80rpx;
flex-shrink: 0;
2026-08-20 19:15:41 +08:00
z-index: 1;
}
.tn-right {
width: auto;
min-width: 80rpx;
flex-shrink: 0;
margin-left: auto;
z-index: 1;
display: flex;
align-items: center;
justify-content: flex-end;
2026-08-19 08:17:18 +08:00
}
.tn-title {
2026-08-20 19:15:41 +08:00
position: absolute;
left: 160rpx;
right: 160rpx;
2026-08-19 08:17:18 +08:00
text-align: center;
font-size: 32rpx;
font-weight: 600;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
2026-08-20 19:15:41 +08:00
pointer-events: none;
2026-08-19 08:17:18 +08:00
}
</style>