2025-07-19 20:00:08 +08:00

524 lines
13 KiB
Vue
Raw Permalink 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>
<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>