147 lines
4.8 KiB
Vue
147 lines
4.8 KiB
Vue
<template>
|
|
<view class="home-page" :style="{ height: pageHeight }">
|
|
<view class="banner-fixed">
|
|
<BrandBanner :studio-info="studioStore.studioInfo" />
|
|
</view>
|
|
<scroll-view class="page-scroll" scroll-y :scroll-into-view="scrollTarget"
|
|
scroll-with-animation refresher-enabled :refresher-triggered="refreshing"
|
|
@refresherrefresh="handleRefresh" @refresherrestore="refreshing = false">
|
|
<view class="banner-spacer" />
|
|
<view class="floating-card">
|
|
<view class="card-handle"><view class="card-handle-bar" /></view>
|
|
<QuickEntry @scroll-to-card-shop="scrollToCardShop" />
|
|
<UpcomingBooking />
|
|
<ReviewSummaryCard />
|
|
<StudioInfo :studio-info="studioStore.studioInfo" />
|
|
<view :id="cardShopAnchorId">
|
|
<CardShop ref="cardShopRef" />
|
|
</view>
|
|
<AboutSection />
|
|
<view class="bottom-padding" />
|
|
</view>
|
|
</scroll-view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, nextTick, onUnmounted } from 'vue'
|
|
import { onShow, onResize, onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app'
|
|
|
|
import BrandBanner from '../../components/BrandBanner.vue'
|
|
import ReviewSummaryCard from '../../components/ReviewSummaryCard.vue'
|
|
import StudioInfo from '../../components/StudioInfo.vue'
|
|
import QuickEntry from '../../components/QuickEntry.vue'
|
|
import UpcomingBooking from '../../components/UpcomingBooking.vue'
|
|
import CardShop from '../../components/CardShop.vue'
|
|
import AboutSection from '../../components/AboutSection.vue'
|
|
|
|
import { useUserStore } from '../../stores/user'
|
|
import { useStudioStore } from '../../stores/studio'
|
|
import { useBookingStore } from '../../stores/booking'
|
|
|
|
const userStore = useUserStore()
|
|
const studioStore = useStudioStore()
|
|
const bookingStore = useBookingStore()
|
|
|
|
// ─── 微信分享 ───────────────────────────────────────────────
|
|
onShareAppMessage(() => {
|
|
return {
|
|
title: '专注核心,遇见更好的自己 | Focus Core 普拉提',
|
|
path: '/pages/home/index',
|
|
imageUrl: '',
|
|
}
|
|
})
|
|
|
|
onShareTimeline(() => {
|
|
return {
|
|
title: '专注核心,遇见更好的自己 | Focus Core 普拉提',
|
|
query: '',
|
|
}
|
|
})
|
|
|
|
// ─── Layout ───────────────────────────────────────────────
|
|
const refreshing = ref(false)
|
|
const cardShopRef = ref<InstanceType<typeof CardShop> | null>(null)
|
|
const cardShopAnchorId = 'card-shop-anchor'
|
|
const scrollTarget = ref('')
|
|
const pageHeight = ref(`${uni.getWindowInfo().windowHeight}px`)
|
|
onResize(() => { pageHeight.value = `${uni.getWindowInfo().windowHeight}px` })
|
|
const pendingScrollToCardShop = ref(false)
|
|
|
|
// Listen for cross-page scroll request (e.g. from booking page "去购买")
|
|
uni.$on('scrollToCardShop', () => {
|
|
pendingScrollToCardShop.value = true
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
uni.$off('scrollToCardShop')
|
|
})
|
|
|
|
// Refresh all data on every show
|
|
onShow(async () => {
|
|
await refreshData()
|
|
|
|
// If another page requested scroll to card shop, execute after data is ready
|
|
if (pendingScrollToCardShop.value) {
|
|
pendingScrollToCardShop.value = false
|
|
await nextTick()
|
|
scrollToCardShop()
|
|
}
|
|
})
|
|
|
|
async function refreshData() {
|
|
const tasks: Promise<unknown>[] = [studioStore.fetchStudioInfo()]
|
|
|
|
if (userStore.loggedIn) {
|
|
tasks.push(
|
|
userStore.fetchProfile(),
|
|
userStore.fetchMemberships(),
|
|
bookingStore.fetchUpcomingBookings(),
|
|
)
|
|
}
|
|
|
|
await Promise.allSettled(tasks)
|
|
|
|
// Also refresh card shop
|
|
await Promise.allSettled([
|
|
cardShopRef.value?.fetchCardTypes(),
|
|
])
|
|
}
|
|
|
|
async function handleRefresh() {
|
|
refreshing.value = true
|
|
try {
|
|
await refreshData()
|
|
} finally {
|
|
refreshing.value = false
|
|
}
|
|
}
|
|
|
|
async function scrollToCardShop() {
|
|
scrollTarget.value = ''
|
|
await nextTick()
|
|
scrollTarget.value = cardShopAnchorId
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.home-page { position: relative; height: 100vh; background: #fbf9f6; }
|
|
.banner-fixed { position: fixed; top: 0; left: 0; width: 100%; z-index: 0; }
|
|
.page-scroll { position: relative; z-index: 1; height: 100%; }
|
|
.banner-spacer { height: 420rpx; }
|
|
.floating-card {
|
|
position: relative;
|
|
min-height: 100vh;
|
|
padding-top: 12rpx;
|
|
border-radius: 32rpx 32rpx 0 0;
|
|
border-top: 1rpx solid rgba(255, 255, 255, 0.7);
|
|
background: linear-gradient(180deg, rgba(251, 249, 246, 0.86), #fbf9f6 260rpx);
|
|
-webkit-backdrop-filter: blur(16px);
|
|
backdrop-filter: blur(16px);
|
|
box-shadow: 0 -4rpx 20rpx rgba(66, 54, 45, 0.06);
|
|
}
|
|
.card-handle { display: flex; justify-content: center; padding: 16rpx 0 8rpx; }
|
|
.card-handle-bar { width: 64rpx; height: 8rpx; border-radius: 4rpx; background: rgba(112, 94, 79, 0.18); }
|
|
.bottom-padding { height: 48rpx; }
|
|
</style>
|