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>
|