Files
frontend-miniapp/src/components/navigation/GuidePageFrame.vue

169 lines
3.2 KiB
Vue

<template>
<view
class="guide-page-frame"
:class="[`variant-${variant}`, { 'no-top-tabs': !showTopTabs, 'has-bottom-nav': showBottomNav }]"
>
<GuideTopTabs
v-if="showTopTabs"
:active-tab="activeTab"
:variant="topTabsVariant"
:top="topTabsTop"
@tab-change="handleTabChange"
/>
<view
v-if="showBack || showCancel"
class="guide-page-frame-actions"
>
<view
v-if="showBack"
class="frame-action frame-action-back"
@tap.stop="handleBackTap"
>
<text class="frame-action-text">{{ backLabel }}</text>
</view>
<view
v-if="showCancel"
class="frame-action frame-action-cancel"
@tap.stop="handleCancelTap"
>
<text class="frame-action-text">{{ cancelLabel }}</text>
</view>
</view>
<view class="guide-page-frame-body">
<slot></slot>
</view>
<GuideBottomNav
v-if="showBottomNav"
:active-tab="activeTab"
@tab-change="handleTabChange"
/>
</view>
</template>
<script setup lang="ts">
import GuideBottomNav from '@/components/navigation/GuideBottomNav.vue'
import GuideTopTabs from '@/components/navigation/GuideTopTabs.vue'
import type { GuideTopTab } from '@/utils/guideTopTabs'
withDefaults(defineProps<{
activeTab?: GuideTopTab
variant?: 'overlay' | 'static'
topTabsVariant?: 'underline' | 'segmented'
topTabsTop?: string
showTopTabs?: boolean
showBottomNav?: boolean
showBack?: boolean
showCancel?: boolean
backLabel?: string
cancelLabel?: string
}>(), {
activeTab: 'guide',
variant: 'overlay',
topTabsVariant: 'underline',
topTabsTop: '0',
showTopTabs: true,
showBottomNav: false,
showBack: false,
showCancel: false,
backLabel: '返回',
cancelLabel: '取消'
})
const emit = defineEmits<{
tabChange: [tab: GuideTopTab]
back: []
cancel: []
}>()
const handleTabChange = (tab: GuideTopTab) => {
emit('tabChange', tab)
}
const handleBackTap = () => {
emit('back')
}
const handleCancelTap = () => {
emit('cancel')
}
</script>
<style scoped lang="scss">
.guide-page-frame {
position: relative;
width: 100%;
height: 100vh;
height: 100dvh;
overflow: hidden;
isolation: isolate;
background: var(--museum-bg-map);
}
.guide-page-frame-body {
position: absolute;
left: 0;
right: 0;
bottom: 0;
overflow: hidden;
}
.variant-overlay .guide-page-frame-body {
top: 0;
}
.variant-static .guide-page-frame-body {
top: 44px;
background: var(--museum-bg-light);
}
.variant-static.no-top-tabs .guide-page-frame-body {
top: 0;
}
.guide-page-frame-actions {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 44px;
z-index: 2010;
pointer-events: none;
}
.frame-action {
position: absolute;
top: 0;
min-width: 54px;
height: 44px;
padding: 0 12px;
display: flex;
align-items: center;
box-sizing: border-box;
pointer-events: auto;
}
.frame-action-back {
left: 0;
justify-content: flex-start;
}
.frame-action-cancel {
right: 0;
justify-content: flex-end;
}
.frame-action-text {
font-size: 14px;
line-height: 20px;
font-weight: 500;
color: #1f2329;
}
@media (min-width: 768px) {
.guide-page-frame {
max-width: 430px;
margin: 0 auto;
}
}
</style>