128 lines
2.8 KiB
Vue
128 lines
2.8 KiB
Vue
<template>
|
||
<view class="apv-head" :style="headPad">
|
||
<!-- 左侧:返回按钮 + 可选标题前缀 -->
|
||
<view class="apv-head-left">
|
||
<view class="apv-back" hover-class="apv-back-active" @tap="onBack">
|
||
<FaIcon name="chevron-left" color="#1f2b48" :size="20" />
|
||
</view>
|
||
<text v-if="backText" class="apv-back-text">{{ backText }}</text>
|
||
</view>
|
||
|
||
<!-- 中间:标题 -->
|
||
<view class="apv-head-center">
|
||
<text class="apv-title">{{ title }}</text>
|
||
</view>
|
||
|
||
<!-- 右侧:可选操作槽(默认占位,避免标题被挤偏) -->
|
||
<view class="apv-head-right">
|
||
<slot name="right" />
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { computed } from 'vue'
|
||
import { mpHeadPad } from '@/utils/mpHead.js'
|
||
|
||
const props = defineProps({
|
||
/** 标题 */
|
||
title: { type: String, required: true },
|
||
/** 返回按钮旁的文字(不传则只显示箭头图标)。常见「返回」「关闭」。 */
|
||
backText: { type: String, default: '' },
|
||
/**
|
||
* 自定义返回行为。函数返回 false 时阻止默认 navigateBack。
|
||
* 例:detail 页面有未保存修改时拦截。
|
||
*/
|
||
beforeBack: { type: Function, default: null }
|
||
})
|
||
|
||
const emit = defineEmits(['back'])
|
||
|
||
function onBack() {
|
||
if (typeof props.beforeBack === 'function' && props.beforeBack() === false) return
|
||
emit('back')
|
||
const pages = getCurrentPages()
|
||
if (pages.length > 1) {
|
||
uni.navigateBack({ delta: 1 })
|
||
} else {
|
||
// 栈内只有自己(直链进入),回到子系统首页
|
||
uni.reLaunch({ url: '/pages/nav/nav' })
|
||
}
|
||
}
|
||
|
||
// 状态栏高度避让:胶囊可用则按胶囊底缘 + 8px,否则回退到 safe-area-inset-top
|
||
const headPad = computed(() => mpHeadPad(8))
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
@import '@/src/styles/page-common.scss';
|
||
|
||
.apv-head {
|
||
flex-shrink: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
height: 88rpx;
|
||
padding: 0 $page-padding-x;
|
||
background: $color-card;
|
||
box-shadow: $shadow-header;
|
||
position: relative;
|
||
z-index: 10;
|
||
/* #ifdef MP-WEIXIN */
|
||
padding-top: 0; /* mpHeadPad 已注入 paddingTop;这里不再叠加 */
|
||
/* #endif */
|
||
}
|
||
|
||
.apv-head-left,
|
||
.apv-head-right {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8rpx;
|
||
min-width: 120rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.apv-head-right {
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
.apv-head-center {
|
||
flex: 1;
|
||
min-width: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 0 16rpx;
|
||
}
|
||
|
||
.apv-back {
|
||
width: 56rpx;
|
||
height: 56rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: $radius-full;
|
||
transition: background 0.2s;
|
||
}
|
||
|
||
.apv-back-active {
|
||
background: $color-bg-page;
|
||
}
|
||
|
||
.apv-back-text {
|
||
font-size: 26rpx;
|
||
color: $color-text-secondary;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.apv-title {
|
||
max-width: 100%;
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: $color-text;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
</style>
|