524 lines
13 KiB
Vue
Raw Permalink Normal View History

2025-07-19 20:00:08 +08:00
<template>
<view class="common-header" :style="{ height: headerHeight + 'px' }">
<!-- 状态栏占位 -->
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
<!-- 自定义导航栏 -->
<view class="nav-bar" :style="{ height: navBarHeight + 'px' }">
<!-- 左侧返回按钮 -->
<view class="nav-left" @click="handleBack">
<uni-icons :type="leftIcon" size="20" :color="textColor" />
</view>
<!-- 标题区域 - 调整到胶囊位置 -->
<view class="nav-title" :style="titleStyle">
<text class="title-text" :style="{ color: textColor }">{{ title }}</text>
</view>
<!-- 右侧按钮区域 - 调整到胶囊左侧 -->
<view class="nav-right" :style="rightButtonStyle" v-if="$slots.right">
<slot name="right"></slot>
</view>
</view>
<!-- 自定义渐变背景 -->
<view class="header-bg" :style="{ background: gradientBg }"></view>
</view>
</template>
<script>
export default {
name: 'CommonHeader',
props: {
title: {
type: String,
default: ''
},
leftIcon: {
type: String,
default: 'back'
},
backgroundColor: {
type: String,
default: 'transparent'
},
textColor: {
type: String,
default: '#000'
},
theme: {
type: String,
default: 'primary'
}
},
data() {
return {
statusBarHeight: 0,
navBarHeight: 44,
menuButtonInfo: null
}
},
computed: {
headerHeight() {
return this.statusBarHeight + this.navBarHeight
},
gradientBg() {
const themes = {
primary: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
success: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)',
warning: 'linear-gradient(135deg, #fa709a 0%, #fee140 100%)',
error: 'linear-gradient(135deg, #ff758c 0%, #ff7eb3 100%)',
activity: 'linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%)',
rebate: 'linear-gradient(135deg, #a8edea 0%, #fed6e3 100%)',
newproduct: 'linear-gradient(135deg, #d299c2 0%, #fef9d7 100%)',
distiller: 'linear-gradient(135deg, #89f7fe 0%, #66a6ff 100%)',
order: 'linear-gradient(135deg, #fdbb2d 0%, #22c1c3 100%)',
shipping: 'linear-gradient(135deg, #e0c3fc 0%, #9bb5ff 100%)',
common: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'
}
return themes[this.theme] || themes.primary
},
titleStyle() {
if (!this.menuButtonInfo) return {}
// 计算标题位置,使其与胶囊垂直居中对齐
const titleTop = (this.menuButtonInfo.top - this.statusBarHeight) + 'px'
const titleHeight = this.menuButtonInfo.height + 'px'
const titleRight = (this.menuButtonInfo.left - 20) + 'px' // 胶囊左侧留20px间距
return {
top: titleTop,
height: titleHeight,
right: titleRight
}
},
rightButtonStyle() {
if (!this.menuButtonInfo) return {}
// 右侧按钮定位到胶囊左侧
const buttonTop = (this.menuButtonInfo.top - this.statusBarHeight) + 'px'
const buttonHeight = this.menuButtonInfo.height + 'px'
const buttonRight = (this.menuButtonInfo.left - 10) + 'px' // 胶囊左侧留10px间距
return {
top: buttonTop,
height: buttonHeight,
right: buttonRight
}
}
},
mounted() {
this.initHeaderInfo()
this.setHeaderHeightCssVar()
},
updated() {
this.setHeaderHeightCssVar()
},
methods: {
initHeaderInfo() {
// 获取系统信息
const systemInfo = uni.getSystemInfoSync()
this.statusBarHeight = systemInfo.statusBarHeight || 0
// 获取胶囊按钮信息
// #ifdef MP-WEIXIN
try {
this.menuButtonInfo = uni.getMenuButtonBoundingClientRect()
// 根据胶囊按钮调整导航栏高度,确保合适的显示
if (this.menuButtonInfo) {
const topGap = this.menuButtonInfo.top - this.statusBarHeight
const bottomGap = topGap
this.navBarHeight = topGap + this.menuButtonInfo.height + bottomGap
}
} catch (e) {
console.warn('获取胶囊按钮信息失败:', e)
this.navBarHeight = 44
}
// #endif
// #ifdef APP-PLUS
// App端适配
this.navBarHeight = 44
// #endif
// #ifdef H5
// H5端适配
this.navBarHeight = 44
// #endif
// #ifndef MP-WEIXIN || APP-PLUS || H5
// 其他平台使用默认值
this.navBarHeight = 44
// #endif
// 确保最小高度
if (this.navBarHeight < 44) {
this.navBarHeight = 44
}
},
handleBack() {
this.$emit('back')
uni.navigateBack({
fail: () => {
uni.switchTab({
url: '/pages/work/index'
})
}
})
},
// 设置CSS变量供其他组件使用
setHeaderHeightCssVar() {
this.$nextTick(() => {
const headerHeight = this.headerHeight
// #ifdef H5
document.documentElement.style.setProperty('--header-height', headerHeight + 'px')
// #endif
// 为小程序和App动态设置头部间距
this.updatePagePadding(headerHeight)
// 向父组件发送头部高度信息
this.$emit('header-info', {
headerHeight: headerHeight,
statusBarHeight: this.statusBarHeight,
navBarHeight: this.navBarHeight
})
})
},
// 动态更新页面内容的padding-top
updatePagePadding(headerHeight) {
// #ifdef H5
try {
const cssSelectors = [
'.page-content',
'.activity-content',
'.rebate-content',
'.newproduct-content',
'.distiller-content',
'.order-content',
'.shipping-content',
'.activity-container',
'.rebate-container',
'.newproduct-container',
'.distiller-container',
'.order-container',
'.shipping-container',
'.add-content',
'.edit-content',
'.detail-content',
'.batch-content',
'.transfer-content',
'.history-content',
'.logistics-content',
'.destroy-container .page-content'
]
// 创建动态样式
const dynamicStyle = cssSelectors.map(selector => {
return `${selector} { padding-top: ${headerHeight}px !important; }`
}).join('\n')
// 添加或更新style标签
let styleElement = document.getElementById('common-header-dynamic-style')
if (!styleElement) {
styleElement = document.createElement('style')
styleElement.id = 'common-header-dynamic-style'
document.head.appendChild(styleElement)
}
styleElement.textContent = dynamicStyle
} catch (e) {
console.warn('更新页面间距失败:', e)
}
// #endif
// #ifdef MP-WEIXIN || APP-PLUS
// 小程序和App中通过事件通知父页面更新样式
uni.$emit('header-height-updated', {
headerHeight: headerHeight,
statusBarHeight: this.statusBarHeight,
navBarHeight: this.navBarHeight
})
// 设置全局数据供其他组件使用
try {
const app = getApp()
if (app) {
app.globalData = app.globalData || {}
app.globalData.headerHeight = headerHeight
app.globalData.statusBarHeight = this.statusBarHeight
app.globalData.navBarHeight = this.navBarHeight
}
} catch (e) {
console.warn('设置全局头部高度失败:', e)
}
// #endif
}
}
}
</script>
<style lang="scss" scoped>
.common-header {
position: fixed;
top: 0;
left: 0;
right: 0;
width: 100%;
z-index: 999;
.header-bg {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: -1;
}
.status-bar {
width: 100%;
background: transparent;
}
.nav-bar {
position: relative;
display: flex;
align-items: center;
width: 100%;
padding: 0 20rpx;
box-sizing: border-box;
}
.nav-left {
display: flex;
align-items: center;
justify-content: center;
width: 80rpx;
height: 80rpx;
border-radius: 50%;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
cursor: pointer;
transition: all 0.3s ease;
&:hover {
background: rgba(255, 255, 255, 0.2);
}
&:active {
transform: scale(0.95);
}
}
.nav-title {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
left: 120rpx;
z-index: 1;
.title-text {
font-size: 32rpx;
font-weight: 600;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 400rpx;
text-shadow: 0 1px 3px rgba(255, 255, 255, 0.8);
}
}
.nav-right {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
z-index: 2;
// 右侧按钮样式美化
::v-deep .uni-icons {
display: flex;
align-items: center;
justify-content: center;
width: 60rpx;
height: 60rpx;
border-radius: 50%;
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(10px);
transition: all 0.3s ease;
&:hover {
background: rgba(255, 255, 255, 0.25);
}
&:active {
transform: scale(0.95);
}
}
}
}
// 适配不同主题的文字颜色
.common-header.dark {
.nav-left,
.nav-right ::v-deep .uni-icons {
background: rgba(0, 0, 0, 0.1);
&:hover {
background: rgba(0, 0, 0, 0.2);
}
}
}
// 适配不同设备
/* #ifdef H5 */
.common-header {
.nav-left,
.nav-right ::v-deep .uni-icons {
cursor: pointer;
}
}
/* #endif */
/* #ifdef MP-WEIXIN */
.common-header {
.nav-title {
// 小程序中的标题位置微调
.title-text {
font-size: 30rpx;
}
}
}
/* #endif */
</style>
<!-- 全局样式为其他页面提供头部间距 -->
<style lang="scss">
// 为页面内容提供合适的顶部间距
.page-content,
.activity-content,
.rebate-content,
.newproduct-content,
.distiller-content,
.order-content,
.shipping-content {
padding-top: var(--header-height, 88px);
min-height: 100vh;
box-sizing: border-box;
padding-left: 20rpx;
padding-right: 20rpx;
padding-bottom: 20rpx;
}
// 为使用CommonHeader的页面容器提供头部间距
.activity-container,
.rebate-container,
.newproduct-container,
.distiller-container,
.order-container,
.shipping-container {
padding-top: var(--header-height, 88px);
min-height: 100vh;
box-sizing: border-box;
}
// 添加和编辑页面的内容区域
.add-content,
.edit-content,
.detail-content,
.batch-content,
.transfer-content,
.history-content,
.logistics-content {
padding-top: var(--header-height, 88px);
padding-left: 20rpx;
padding-right: 20rpx;
padding-bottom: 20rpx;
min-height: calc(100vh - var(--header-height, 88px));
box-sizing: border-box;
}
// 销毁酒类页面特殊处理
.destroy-container .page-content {
padding-top: var(--header-height, 88px);
}
// 在小程序中的特殊处理 - 使用计算的实际高度
/* #ifdef MP-WEIXIN */
.page-content,
.activity-content,
.rebate-content,
.newproduct-content,
.distiller-content,
.order-content,
.shipping-content,
.activity-container,
.rebate-container,
.newproduct-container,
.distiller-container,
.order-container,
.shipping-container {
padding-top: 96px; // 状态栏(44px) + 导航栏(52px) 的常见值
}
.add-content,
.edit-content,
.detail-content,
.batch-content,
.transfer-content,
.history-content,
.logistics-content {
padding-top: 96px; // 状态栏(44px) + 导航栏(52px) 的常见值
}
.destroy-container .page-content {
padding-top: 96px; // 状态栏(44px) + 导航栏(52px) 的常见值
}
/* #endif */
// 在App中的特殊处理
/* #ifdef APP-PLUS */
.page-content,
.activity-content,
.rebate-content,
.newproduct-content,
.distiller-content,
.order-content,
.shipping-content,
.activity-container,
.rebate-container,
.newproduct-container,
.distiller-container,
.order-container,
.shipping-container {
padding-top: calc(44px + var(--status-bar-height, 20px));
}
.add-content,
.edit-content,
.detail-content,
.batch-content,
.transfer-content,
.history-content,
.logistics-content {
padding-top: calc(44px + var(--status-bar-height, 20px));
}
.destroy-container .page-content {
padding-top: calc(44px + var(--status-bar-height, 20px));
}
/* #endif */
</style>