feat: 页面调整
This commit is contained in:
@@ -4,17 +4,21 @@
|
||||
<image class="absolute inset-0 h-full w-full" :src="scenicSpot.heroImage" mode="aspectFill" />
|
||||
<view class="absolute inset-0 bg-[linear-gradient(180deg,rgba(6,17,31,0.15)_18%,rgba(6,17,31,0.1)_45%,rgba(6,17,31,0.92)_100%)]" />
|
||||
|
||||
<view class="absolute left-[16px] right-[16px] top-[calc(env(safe-area-inset-top)+12px)] flex items-center justify-between">
|
||||
<view class="flex h-[38px] w-[38px] items-center justify-center rounded-full bg-black/30" @click="emit('back')">
|
||||
<uni-icons type="left" size="20" color="#FFFFFF" />
|
||||
<view class="absolute inset-x-0 top-0 z-20">
|
||||
<TopNavBar
|
||||
title=""
|
||||
background="transparent"
|
||||
title-color="#ffffff"
|
||||
back-icon-color="#ffffff"
|
||||
@back="emit('back')"
|
||||
/>
|
||||
</view>
|
||||
<view class="absolute right-[100px] top-[calc(env(safe-area-inset-top)+58px)] z-30 flex items-center gap-[10px]">
|
||||
<view class="flex h-[40px] w-[40px] items-center justify-center rounded-full bg-black/35 shadow-sm" @tap.stop="emit('open-travelers')">
|
||||
<uni-icons type="contact" size="21" color="#FFFFFF" />
|
||||
</view>
|
||||
<view class="flex items-center gap-[10px]">
|
||||
<view class="flex h-[38px] w-[38px] items-center justify-center rounded-full bg-black/30" @click="emit('open-travelers')">
|
||||
<uni-icons type="contact" size="20" color="#FFFFFF" />
|
||||
</view>
|
||||
<view class="flex h-[38px] w-[38px] items-center justify-center rounded-full bg-black/30" @click="emit('open-orders')">
|
||||
<uni-icons type="list" size="20" color="#FFFFFF" />
|
||||
</view>
|
||||
<view class="flex h-[40px] w-[40px] items-center justify-center rounded-full bg-black/35 shadow-sm" @tap.stop="emit('open-orders')">
|
||||
<uni-icons type="list" size="21" color="#FFFFFF" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -40,9 +44,15 @@
|
||||
|
||||
<view class="-mt-[16px] min-h-0 flex-1 overflow-hidden rounded-t-[22px] bg-white pt-[14px]">
|
||||
<view class="flex h-full min-h-0">
|
||||
<scroll-view class="h-full w-[96px] shrink-0 bg-white" scroll-y>
|
||||
<scroll-view
|
||||
class="h-full w-[96px] shrink-0 bg-white"
|
||||
scroll-y
|
||||
:scroll-into-view="leftScrollIntoViewId"
|
||||
scroll-with-animation
|
||||
>
|
||||
<view
|
||||
v-for="group in groups"
|
||||
:id="`ticket-nav-${group.id}`"
|
||||
:key="group.id"
|
||||
class="relative flex min-h-[68px] items-center justify-center px-[10px] py-[12px] text-center"
|
||||
:class="group.id === activeGroupId ? 'bg-[#ecf8f2] text-[#11795c]' : 'bg-white text-[#5f6875]'"
|
||||
@@ -53,7 +63,14 @@
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<scroll-view class="h-full min-w-0 flex-1 bg-[#f4f6f8]" scroll-y :scroll-into-view="scrollIntoViewId" scroll-with-animation>
|
||||
<scroll-view
|
||||
id="ticket-product-scroll"
|
||||
class="h-full min-w-0 flex-1 bg-[#f4f6f8]"
|
||||
scroll-y
|
||||
:scroll-into-view="scrollIntoViewId"
|
||||
scroll-with-animation
|
||||
@scroll="handleProductScroll"
|
||||
>
|
||||
<view class="px-[12px] pb-[24px]">
|
||||
<view v-for="group in groups" :id="`ticket-group-${group.id}`" :key="group.id" class="pt-[14px]">
|
||||
<view class="flex items-center">
|
||||
@@ -95,7 +112,8 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { computed, getCurrentInstance, nextTick, onMounted, ref, watch } from 'vue'
|
||||
import TopNavBar from "@/components/TopNavBar/index.vue"
|
||||
|
||||
const props = defineProps({
|
||||
scenicSpot: { type: Object, required: true },
|
||||
@@ -106,6 +124,10 @@ const props = defineProps({
|
||||
const emit = defineEmits(['back', 'open-travelers', 'open-orders', 'open-detail', 'buy'])
|
||||
const activeGroupId = ref(props.groups[0]?.id || '')
|
||||
const scrollIntoViewId = ref('')
|
||||
const leftScrollIntoViewId = computed(() => `ticket-nav-${activeGroupId.value}`)
|
||||
const groupOffsets = ref([])
|
||||
const rightScrollTop = ref(0)
|
||||
const instance = getCurrentInstance()
|
||||
const productsById = computed(() => new Map(props.products.map((product) => [product.id, product])))
|
||||
|
||||
const productsForGroup = (group) => (group.productIds || [])
|
||||
@@ -116,9 +138,57 @@ const ageRuleText = (rule) => `${rule.min}—${rule.max}周岁`
|
||||
|
||||
const selectGroup = (groupId) => {
|
||||
activeGroupId.value = groupId
|
||||
scrollIntoViewId.value = `ticket-group-${groupId}`
|
||||
scrollIntoViewId.value = ''
|
||||
nextTick(() => {
|
||||
scrollIntoViewId.value = `ticket-group-${groupId}`
|
||||
})
|
||||
}
|
||||
|
||||
const measureGroupOffsets = () => {
|
||||
if (!props.groups.length) return
|
||||
|
||||
nextTick(() => {
|
||||
const query = uni.createSelectorQuery().in(instance?.proxy)
|
||||
query.select('#ticket-product-scroll').boundingClientRect()
|
||||
props.groups.forEach((group) => {
|
||||
query.select(`#ticket-group-${group.id}`).boundingClientRect()
|
||||
})
|
||||
query.exec((rects = []) => {
|
||||
const containerRect = rects[0]
|
||||
if (!containerRect) return
|
||||
groupOffsets.value = props.groups
|
||||
.map((group, index) => ({
|
||||
id: group.id,
|
||||
top: Math.max(
|
||||
0,
|
||||
Number(rects[index + 1]?.top || 0) -
|
||||
Number(containerRect.top || 0) +
|
||||
rightScrollTop.value
|
||||
),
|
||||
}))
|
||||
.sort((left, right) => left.top - right.top)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const handleProductScroll = (event) => {
|
||||
rightScrollTop.value = Number(event.detail?.scrollTop || 0)
|
||||
if (!groupOffsets.value.length) {
|
||||
measureGroupOffsets()
|
||||
return
|
||||
}
|
||||
|
||||
const activationLine = rightScrollTop.value + 24
|
||||
const activeGroup = groupOffsets.value.reduce(
|
||||
(current, group) => (group.top <= activationLine ? group : current),
|
||||
groupOffsets.value[0]
|
||||
)
|
||||
if (activeGroup?.id) activeGroupId.value = activeGroup.id
|
||||
}
|
||||
|
||||
onMounted(measureGroupOffsets)
|
||||
watch(() => props.groups.map((group) => group.id).join(','), measureGroupOffsets)
|
||||
|
||||
const makePhoneCall = () => {
|
||||
if (!props.scenicSpot.phone) return
|
||||
uni.makePhoneCall({ phoneNumber: props.scenicSpot.phone })
|
||||
|
||||
Reference in New Issue
Block a user