150 lines
3.1 KiB
Vue
150 lines
3.1 KiB
Vue
<template>
|
|
<view class="app-navbar" :style="navbarStyle">
|
|
<view class="app-navbar__content" :style="contentStyle">
|
|
<view class="app-navbar__left" @tap="handleLeftTap">
|
|
<AtIcon v-if="showBack" value="chevron-left" size="24" />
|
|
<AtIcon v-else-if="leftIcon" :value="leftIcon" size="22" />
|
|
</view>
|
|
<text class="app-navbar__title">{{ title }}</text>
|
|
<view class="app-navbar__right" />
|
|
</view>
|
|
</view>
|
|
<view class="app-navbar-placeholder" :style="placeholderStyle" />
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import Taro from '@tarojs/taro'
|
|
import { computed } from 'vue'
|
|
|
|
defineOptions({ inheritAttrs: false })
|
|
|
|
const props = defineProps<{
|
|
title: string
|
|
showBack?: boolean
|
|
leftIcon?: string
|
|
fallbackUrl?: string
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
leftClick: []
|
|
}>()
|
|
|
|
interface MenuButtonRect {
|
|
top: number
|
|
height: number
|
|
}
|
|
|
|
const DEFAULT_STATUS_BAR_HEIGHT = 20
|
|
const DEFAULT_CONTENT_HEIGHT = 44
|
|
|
|
const systemInfo = Taro.getSystemInfoSync()
|
|
const statusBarHeight = systemInfo.statusBarHeight || DEFAULT_STATUS_BAR_HEIGHT
|
|
const menuButtonRect = getMenuButtonRect()
|
|
const navContentHeight = menuButtonRect
|
|
? menuButtonRect.height + (menuButtonRect.top - statusBarHeight) * 2
|
|
: DEFAULT_CONTENT_HEIGHT
|
|
|
|
const navbarStyle = computed(() => ({
|
|
height: `${statusBarHeight + navContentHeight}px`,
|
|
paddingTop: `${statusBarHeight}px`
|
|
}))
|
|
|
|
const contentStyle = computed(() => ({
|
|
height: `${navContentHeight}px`
|
|
}))
|
|
|
|
const placeholderStyle = computed(() => ({
|
|
height: `${statusBarHeight + navContentHeight}px`
|
|
}))
|
|
|
|
function getMenuButtonRect(): MenuButtonRect | undefined {
|
|
if (!Taro.getMenuButtonBoundingClientRect) return undefined
|
|
|
|
const rect = Taro.getMenuButtonBoundingClientRect()
|
|
if (!rect || rect.height <= 0) return undefined
|
|
|
|
return {
|
|
top: rect.top,
|
|
height: rect.height
|
|
}
|
|
}
|
|
|
|
function handleLeftTap() {
|
|
if (props.showBack) {
|
|
goBack()
|
|
return
|
|
}
|
|
if (props.leftIcon) emit('leftClick')
|
|
}
|
|
|
|
function goBack() {
|
|
if (Taro.getCurrentPages().length > 1) {
|
|
Taro.navigateBack()
|
|
return
|
|
}
|
|
if (props.fallbackUrl) Taro.reLaunch({ url: props.fallbackUrl })
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.app-navbar {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
z-index: 99;
|
|
box-sizing: border-box;
|
|
width: 100%;
|
|
overflow: hidden;
|
|
color: $text-primary;
|
|
background: $bg-page;
|
|
border-bottom: 1px solid $border-color-light;
|
|
}
|
|
|
|
.app-navbar-placeholder {
|
|
width: 100%;
|
|
}
|
|
|
|
.app-navbar__content {
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
width: 100%;
|
|
}
|
|
|
|
.app-navbar__left,
|
|
.app-navbar__right {
|
|
z-index: 1;
|
|
display: flex;
|
|
flex: 0 0 96px;
|
|
align-items: center;
|
|
height: 100%;
|
|
}
|
|
|
|
.app-navbar__left {
|
|
justify-content: flex-start;
|
|
padding-left: $space-lg;
|
|
color: $text-primary;
|
|
}
|
|
|
|
.app-navbar__right {
|
|
justify-content: flex-end;
|
|
padding-right: $space-lg;
|
|
}
|
|
|
|
.app-navbar__title {
|
|
position: absolute;
|
|
right: 0;
|
|
left: 0;
|
|
max-width: 100%;
|
|
padding: 0 120px;
|
|
overflow: hidden;
|
|
font-size: 18PX;
|
|
font-weight: 400;
|
|
line-height: 1.2;
|
|
color: $text-primary;
|
|
text-align: center;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
</style>
|