356 lines
9.6 KiB
Vue
356 lines
9.6 KiB
Vue
|
<template>
|
|||
|
<view
|
|||
|
class="tn-custom-nav-bar-class tn-custom-nav-bar"
|
|||
|
:style="[navBarStyle]"
|
|||
|
>
|
|||
|
<view
|
|||
|
class="tn-custom-nav-bar__bar"
|
|||
|
:class="[barClass]"
|
|||
|
:style="[barStyle]"
|
|||
|
>
|
|||
|
<view v-if="isBack">
|
|||
|
<view v-if="customBack">
|
|||
|
<view
|
|||
|
:style="{
|
|||
|
width: customBackStyleInfo.width + 'px',
|
|||
|
height: customBackStyleInfo.height + 'px',
|
|||
|
marginLeft: customBackStyleInfo.left + 'px'
|
|||
|
}"
|
|||
|
>
|
|||
|
<slot name="back"></slot>
|
|||
|
</view>
|
|||
|
</view>
|
|||
|
<view v-else class="tn-custom-nav-bar__bar__action" @tap="handlerBack">
|
|||
|
<text class="tn-custom-nav-bar__bar__action--nav-back" :class="[`tn-icon-${backIcon}`]"></text>
|
|||
|
<text class="tn-custom-nav-bar__bar__action--nav-back-text" v-if="backTitle">{{ backTitle }}</text>
|
|||
|
</view>
|
|||
|
</view>
|
|||
|
<view class="tn-custom-nav-bar__bar__content" :style="[contentStyle]">
|
|||
|
<slot></slot>
|
|||
|
</view>
|
|||
|
<view>
|
|||
|
<slot name="right"></slot>
|
|||
|
</view>
|
|||
|
</view>
|
|||
|
</view>
|
|||
|
</template>
|
|||
|
|
|||
|
<script>
|
|||
|
import componentsColorMixin from '../../libs/mixin/components_color.js'
|
|||
|
export default {
|
|||
|
name: 'tn-nav-bar',
|
|||
|
mixins: [componentsColorMixin],
|
|||
|
props: {
|
|||
|
// 层级
|
|||
|
zIndex: {
|
|||
|
type: Number,
|
|||
|
default: 0
|
|||
|
},
|
|||
|
// 导航栏的高度
|
|||
|
height: {
|
|||
|
type: Number,
|
|||
|
default: 0
|
|||
|
},
|
|||
|
// 高度单位
|
|||
|
unit: {
|
|||
|
type: String,
|
|||
|
default: 'px'
|
|||
|
},
|
|||
|
// 是否显示返回按钮
|
|||
|
isBack: {
|
|||
|
type: Boolean,
|
|||
|
default: true
|
|||
|
},
|
|||
|
// 返回按钮的图标
|
|||
|
backIcon: {
|
|||
|
type: String,
|
|||
|
default: 'left'
|
|||
|
},
|
|||
|
// 返回按钮旁显示的文字
|
|||
|
backTitle: {
|
|||
|
type: String,
|
|||
|
default: '返回'
|
|||
|
},
|
|||
|
// 透明状态栏
|
|||
|
alpha: {
|
|||
|
type: Boolean,
|
|||
|
default: false
|
|||
|
},
|
|||
|
// 是否固定在顶部
|
|||
|
fixed: {
|
|||
|
type: Boolean,
|
|||
|
default: true
|
|||
|
},
|
|||
|
// 是否显示底部阴影
|
|||
|
bottomShadow: {
|
|||
|
type: Boolean,
|
|||
|
default: true
|
|||
|
},
|
|||
|
// 是否自定义返回按钮
|
|||
|
customBack: {
|
|||
|
type: Boolean,
|
|||
|
default: false
|
|||
|
},
|
|||
|
// 返回前回调
|
|||
|
beforeBack: {
|
|||
|
type: Function,
|
|||
|
default: null
|
|||
|
}
|
|||
|
},
|
|||
|
computed: {
|
|||
|
navBarStyle() {
|
|||
|
let style = {}
|
|||
|
style.height = this.height === 0 ? this.customBarHeight + this.unit : this.height + this.unit
|
|||
|
if (this.fixed) {
|
|||
|
style.position = 'fixed'
|
|||
|
}
|
|||
|
style.zIndex = this.elZIndex
|
|||
|
|
|||
|
return style
|
|||
|
},
|
|||
|
barClass() {
|
|||
|
let clazz = ''
|
|||
|
if (this.backgroundColorClass) {
|
|||
|
clazz += ` ${this.backgroundColorClass}`
|
|||
|
}
|
|||
|
if (this.fontColorClass) {
|
|||
|
clazz += `${this.fontColorClass}`
|
|||
|
}
|
|||
|
if (this.fixed) {
|
|||
|
clazz += ' tn-custom-nav-bar__bar--fixed'
|
|||
|
}
|
|||
|
if (this.alpha) {
|
|||
|
clazz += ' tn-custom-nav-bar__bar--alpha'
|
|||
|
}
|
|||
|
if (this.bottomShadow) {
|
|||
|
clazz += ' tn-custom-nav-bar__bar--bottom-shadow'
|
|||
|
}
|
|||
|
|
|||
|
return clazz
|
|||
|
},
|
|||
|
barStyle() {
|
|||
|
let style = {}
|
|||
|
style.height = this.height === 0 ? this.customBarHeight + this.unit : this.height + this.unit
|
|||
|
|
|||
|
if (this.fixed) {
|
|||
|
style.paddingTop = this.statusBarHeight + 'px'
|
|||
|
}
|
|||
|
|
|||
|
if(!this.backgroundColorClass) {
|
|||
|
style.backgroundColor = this.backgroundColor !== '' ? this.backgroundColor : '#FFFFFF'
|
|||
|
}
|
|||
|
if (!this.fontColorClass && this.fontColor) {
|
|||
|
style.color= this.fontColor
|
|||
|
}
|
|||
|
|
|||
|
style.zIndex = this.elZIndex
|
|||
|
|
|||
|
return style
|
|||
|
},
|
|||
|
contentStyle() {
|
|||
|
let style = {}
|
|||
|
style.top = this.fixed ? this.statusBarHeight + 'px' : '0px'
|
|||
|
style.height = this.height === 0 ? (this.customBarHeight - this.statusBarHeight) + this.unit : this.height + this.unit
|
|||
|
style.lineHeight = style.height
|
|||
|
|
|||
|
if (this.isBack) {
|
|||
|
if (this.customBack) {
|
|||
|
const width = (this.customBackStyleInfo.width + this.customBackStyleInfo.left) * 2
|
|||
|
style.width = `calc(100% - ${width}px)`
|
|||
|
} else {
|
|||
|
style.width = 'calc(100% - 340rpx)'
|
|||
|
}
|
|||
|
} else {
|
|||
|
style.width = '100%'
|
|||
|
}
|
|||
|
|
|||
|
return style
|
|||
|
},
|
|||
|
elZIndex() {
|
|||
|
return this.zIndex ? this.zIndex : this.$tn.zIndex.navbar
|
|||
|
}
|
|||
|
},
|
|||
|
data() {
|
|||
|
return {
|
|||
|
// 状态栏的高度
|
|||
|
statusBarHeight: 0,
|
|||
|
// 自定义导航栏的高度
|
|||
|
customBarHeight: 0,
|
|||
|
// 自定义返回按钮时,返回容器的宽高边距信息
|
|||
|
customBackStyleInfo: {
|
|||
|
width: 86,
|
|||
|
height: 32,
|
|||
|
left: 15
|
|||
|
}
|
|||
|
}
|
|||
|
},
|
|||
|
mounted() {
|
|||
|
// 获取vuex中的自定义顶栏的高度
|
|||
|
this.updateNavBarInfo()
|
|||
|
},
|
|||
|
created() {
|
|||
|
// 获取胶囊信息
|
|||
|
// #ifdef MP-WEIXIN
|
|||
|
let custom = wx.getMenuButtonBoundingClientRect()
|
|||
|
this.customBackStyleInfo.width = custom.width
|
|||
|
this.customBackStyleInfo.height = custom.height
|
|||
|
this.customBackStyleInfo.left = uni.upx2px(750) - custom.right
|
|||
|
// #endif
|
|||
|
},
|
|||
|
methods: {
|
|||
|
// 更新导航栏的高度
|
|||
|
async updateNavBarInfo() {
|
|||
|
// 获取vuex中的自定义顶栏的高度
|
|||
|
let customBarHeight = this.vuex_custom_bar_height
|
|||
|
let statusBarHeight = this.vuex_status_bar_height
|
|||
|
// 如果获取失败则重新获取
|
|||
|
if (!customBarHeight) {
|
|||
|
try {
|
|||
|
const navBarInfo = await this.$tn.updateCustomBar()
|
|||
|
customBarHeight = navBarInfo.customBarHeight
|
|||
|
statusBarHeight = navBarInfo.statusBarHeight
|
|||
|
} catch(e) {
|
|||
|
setTimeout(() => {
|
|||
|
this.updateNavBarInfo()
|
|||
|
}, 10)
|
|||
|
return
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 更新vuex中的导航栏信息
|
|||
|
this && this.$tn.vuex('vuex_status_bar_height', statusBarHeight)
|
|||
|
this && this.$tn.vuex('vuex_custom_bar_height', customBarHeight)
|
|||
|
|
|||
|
this.customBarHeight = customBarHeight
|
|||
|
this.statusBarHeight = statusBarHeight
|
|||
|
},
|
|||
|
// 处理返回事件
|
|||
|
async handlerBack() {
|
|||
|
if (this.beforeBack && typeof(this.beforeBack) === 'function') {
|
|||
|
// 执行回调,同时传入索引当作参数
|
|||
|
// 在微信,支付宝等环境(H5正常),会导致父组件定义的函数体中的this变成子组件的this
|
|||
|
// 通过bind()方法,绑定父组件的this,让this的this为父组件的上下文
|
|||
|
let beforeBack = this.beforeBack.bind(this.$tn.$parent.call(this))()
|
|||
|
// 判断是否返回了Promise
|
|||
|
if (!!beforeBack && typeof beforeBack.then === 'function') {
|
|||
|
await beforeBack.then(res => {
|
|||
|
// Promise返回成功
|
|||
|
this.navBack()
|
|||
|
}).catch(err => {})
|
|||
|
} else if (beforeBack === true) {
|
|||
|
this.navBack()
|
|||
|
}
|
|||
|
} else {
|
|||
|
this.navBack()
|
|||
|
}
|
|||
|
},
|
|||
|
// 返回上一页
|
|||
|
navBack() {
|
|||
|
|
|||
|
// 通过判断当前页面的页面栈信息,是否有上一页进行返回,如果没有则跳转到首页
|
|||
|
const pages = getCurrentPages()
|
|||
|
if (pages && pages.length > 0) {
|
|||
|
const firstPage = pages[0]
|
|||
|
if (pages.length == 1 && (!firstPage.route || firstPage.route != 'pages/index/index')) {
|
|||
|
uni.reLaunch({
|
|||
|
url: '/pages/index/index'
|
|||
|
})
|
|||
|
} else {
|
|||
|
uni.navigateBack({
|
|||
|
delta: 1
|
|||
|
})
|
|||
|
}
|
|||
|
} else {
|
|||
|
uni.reLaunch({
|
|||
|
url: '/pages/index/index'
|
|||
|
})
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
</script>
|
|||
|
|
|||
|
<style lang="scss" scoped>
|
|||
|
|
|||
|
.tn-custom-nav-bar {
|
|||
|
display: block;
|
|||
|
position: relative;
|
|||
|
|
|||
|
&__bar {
|
|||
|
display: flex;
|
|||
|
position: relative;
|
|||
|
align-items: center;
|
|||
|
min-height: 100rpx;
|
|||
|
justify-content: space-between;
|
|||
|
min-height: 0px;
|
|||
|
/* #ifdef MP-WEIXIN */
|
|||
|
padding-right: 220rpx;
|
|||
|
/* #endif */
|
|||
|
/* #ifdef MP-ALIPAY */
|
|||
|
padding-right: 150rpx;
|
|||
|
/* #endif */
|
|||
|
box-shadow: 0rpx 0rpx 0rpx;
|
|||
|
z-index: 9999;
|
|||
|
|
|||
|
&--fixed {
|
|||
|
position: fixed;
|
|||
|
width: 100%;
|
|||
|
top: 0;
|
|||
|
}
|
|||
|
|
|||
|
&--alpha {
|
|||
|
background: transparent !important;
|
|||
|
box-shadow: none !important;
|
|||
|
}
|
|||
|
|
|||
|
&--bottom-shadow {
|
|||
|
box-shadow: 0rpx 0rpx 80rpx 0rpx rgba(0, 0, 0, 0.05);
|
|||
|
}
|
|||
|
|
|||
|
&__action {
|
|||
|
display: flex;
|
|||
|
align-items: center;
|
|||
|
height: 100%;
|
|||
|
justify-content: center;
|
|||
|
max-width: 100%;
|
|||
|
|
|||
|
&--nav-back {
|
|||
|
/* position: absolute; */
|
|||
|
/* top: 50%; */
|
|||
|
/* left: 20rpx; */
|
|||
|
/* margin-top: -15rpx; */
|
|||
|
// width: 25rpx;
|
|||
|
// height: 25rpx;
|
|||
|
margin-left: 20rpx;
|
|||
|
font-size: 38rpx;
|
|||
|
line-height: 100%;
|
|||
|
// border-width: 0 0 4rpx 4rpx;
|
|||
|
// border-color: #000000;
|
|||
|
// border-style: solid;
|
|||
|
// transform: matrix(0.5, 0.5, -0.5, 0.5, 0, 0);
|
|||
|
}
|
|||
|
|
|||
|
&--nav-back-text {
|
|||
|
margin-left: 10rpx;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
&__content {
|
|||
|
position: absolute;
|
|||
|
text-align: center;
|
|||
|
left: 0;
|
|||
|
right: 0;
|
|||
|
bottom: 0;
|
|||
|
margin: auto;
|
|||
|
font-size: 32rpx;
|
|||
|
cursor: none;
|
|||
|
// pointer-events: none;
|
|||
|
text-overflow: ellipsis;
|
|||
|
white-space: nowrap;
|
|||
|
overflow: hidden;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
</style>
|