perf: 支持微信支付接口
This commit is contained in:
@@ -3,19 +3,25 @@
|
||||
{
|
||||
"path": "pages/home/index",
|
||||
"style": {
|
||||
"navigationStyle": "custom"
|
||||
"navigationStyle": "custom",
|
||||
"enableShareAppMessage": true,
|
||||
"enableShareTimeline": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/booking/index",
|
||||
"style": {
|
||||
"navigationStyle": "custom"
|
||||
"navigationStyle": "custom",
|
||||
"enableShareAppMessage": true,
|
||||
"enableShareTimeline": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/profile/index",
|
||||
"style": {
|
||||
"navigationStyle": "custom"
|
||||
"navigationStyle": "custom",
|
||||
"enableShareAppMessage": true,
|
||||
"enableShareTimeline": true
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
<template>
|
||||
<view class="booking-page" :style="pageStyle">
|
||||
<!-- ──────────── Custom nav bar ──────────── -->
|
||||
<CustomNavBar title="预约课程" />
|
||||
<view class="booking-page">
|
||||
<!-- ──────────── Status bar spacing ──────────── -->
|
||||
<view class="status-bar" :style="{ height: statusBarHeight }" />
|
||||
|
||||
<!-- ──────────── Sticky header area ──────────── -->
|
||||
<view class="sticky-header">
|
||||
<!-- Date selector -->
|
||||
<!-- ──────────── Page title ──────────── -->
|
||||
<view class="page-header">
|
||||
<text class="page-title">课程预约</text>
|
||||
</view>
|
||||
|
||||
<!-- ──────────── Date & period filters ──────────── -->
|
||||
<view class="filter-header">
|
||||
<DateSelector v-model="selectedDate" @select="onDateSelect" />
|
||||
|
||||
<!-- Time period filter -->
|
||||
<TimePeriodFilter v-model="selectedPeriod" @change="onPeriodChange" />
|
||||
</view>
|
||||
|
||||
@@ -16,7 +18,7 @@
|
||||
<scroll-view
|
||||
class="slot-scroll"
|
||||
scroll-y
|
||||
:style="{ height: scrollHeight, paddingTop: stickyHeaderHeight }"
|
||||
:style="{ height: scrollHeight }"
|
||||
refresher-enabled
|
||||
:refresher-triggered="refreshing"
|
||||
@refresherrefresh="onRefresh"
|
||||
@@ -76,7 +78,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app'
|
||||
import type { TimeSlotWithBookingStatus, MembershipWithCardType } from '@mp-pilates/shared'
|
||||
import { TIME_PERIODS } from '@mp-pilates/shared'
|
||||
import { useBookingStore } from '../../stores/booking'
|
||||
@@ -86,7 +89,6 @@ import DateSelector from '../../components/DateSelector.vue'
|
||||
import TimePeriodFilter from '../../components/TimePeriodFilter.vue'
|
||||
import SlotCard from '../../components/SlotCard.vue'
|
||||
import BookingConfirmPopup from '../../components/BookingConfirmPopup.vue'
|
||||
import CustomNavBar from '../../components/CustomNavBar.vue'
|
||||
|
||||
type PeriodKey = keyof typeof TIME_PERIODS | null
|
||||
|
||||
@@ -101,36 +103,47 @@ const showConfirmPopup = ref(false)
|
||||
const pendingSlot = ref<TimeSlotWithBookingStatus | null>(null)
|
||||
const refreshing = ref(false)
|
||||
|
||||
// ─── 微信分享 ───────────────────────────────────────────────
|
||||
onShareAppMessage(() => {
|
||||
return {
|
||||
title: '预约普拉提课程,开启健康新生活',
|
||||
path: '/pages/booking/index',
|
||||
imageUrl: '',
|
||||
}
|
||||
})
|
||||
|
||||
onShareTimeline(() => {
|
||||
return {
|
||||
title: '预约普拉提课程,开启健康新生活',
|
||||
query: '',
|
||||
}
|
||||
})
|
||||
|
||||
// ─── Layout ───────────────────────────────────────────────
|
||||
// Default: statusBar ~20px + 88rpx ≈ 64px; avoid empty string on first render
|
||||
const navBarHeight = ref('64px')
|
||||
const statusBarHeight = ref('20px')
|
||||
const scrollHeight = ref('500px')
|
||||
const stickyHeaderHeight = ref('240rpx')
|
||||
// Heights of static elements above scroll-view (in rpx, converted to px)
|
||||
const PAGE_HEADER_RPX = 88 // title bar height
|
||||
const FILTER_HEADER_RPX = 240 // DateSelector + TimePeriodFilter
|
||||
const TABBAR_RPX = 100
|
||||
|
||||
function updateLayout() {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
const ratio = sysInfo.windowWidth / 750
|
||||
const statusBarPx = sysInfo.statusBarHeight ?? 20
|
||||
const navTitlePx = 88 * ratio
|
||||
const navBarPx = Math.round(statusBarPx + navTitlePx)
|
||||
navBarHeight.value = `${navBarPx}px`
|
||||
statusBarHeight.value = `${statusBarPx}px`
|
||||
|
||||
// Measure sticky header: DateSelector (~160rpx) + TimePeriodFilter (~76rpx) + borders
|
||||
const stickyPx = Math.round(240 * ratio)
|
||||
stickyHeaderHeight.value = `${stickyPx}px`
|
||||
const headerPx = Math.round(PAGE_HEADER_RPX * ratio)
|
||||
const filterPx = Math.round(FILTER_HEADER_RPX * ratio)
|
||||
const tabbarPx = Math.round(TABBAR_RPX * ratio)
|
||||
|
||||
// scrollHeight: from below nav bar to above tabbar
|
||||
const tabbarPx = Math.round(100 * ratio)
|
||||
scrollHeight.value = `${sysInfo.windowHeight - navBarPx - tabbarPx}px`
|
||||
// scroll-view fills remaining space: window - statusBar - pageHeader - filters - tabbar
|
||||
const remaining = sysInfo.windowHeight - statusBarPx - headerPx - filterPx - tabbarPx
|
||||
scrollHeight.value = `${remaining}px`
|
||||
}
|
||||
|
||||
updateLayout()
|
||||
|
||||
// CSS variable for sticky header offset
|
||||
const pageStyle = computed(() => ({
|
||||
'--nav-bar-height': navBarHeight.value,
|
||||
}))
|
||||
|
||||
// ─── Filtered slots ───────────────────────────────────────
|
||||
const filteredSlots = computed<TimeSlotWithBookingStatus[]>(() => {
|
||||
const slots = bookingStore.slots as TimeSlotWithBookingStatus[]
|
||||
@@ -266,21 +279,38 @@ onMounted(async () => {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.booking-page {
|
||||
min-height: 100vh;
|
||||
height: 100vh;
|
||||
background: #f7f4f0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
--nav-bar-height: v-bind(navBarHeight);
|
||||
padding-top: var(--nav-bar-height);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ── Sticky header ─────────────────────────────────── */
|
||||
.sticky-header {
|
||||
position: fixed;
|
||||
top: var(--nav-bar-height);
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
/* ── Status bar ───────────────────────────────────── */
|
||||
.status-bar {
|
||||
flex-shrink: 0;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
/* ── Page header ──────────────────────────────────── */
|
||||
.page-header {
|
||||
flex-shrink: 0;
|
||||
height: 88rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
color: #1a1a2e;
|
||||
}
|
||||
|
||||
/* ── Filter header ────────────────────────────────── */
|
||||
.filter-header {
|
||||
flex-shrink: 0;
|
||||
background: #fff;
|
||||
box-shadow: 0 4rpx 24rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import { onShow, onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app'
|
||||
|
||||
import CustomNavBar from '../../components/CustomNavBar.vue'
|
||||
import BrandBanner from '../../components/BrandBanner.vue'
|
||||
@@ -57,6 +57,22 @@ 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 navBarHeight = ref('64px')
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import { onShow, onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useUserStore } from '../../stores/user'
|
||||
import UserCard from '../../components/UserCard.vue'
|
||||
@@ -46,6 +46,22 @@ const { loggedIn, hasProfile, user, stats, memberships, isAdmin } = storeToRefs(
|
||||
const loginLoading = ref(false)
|
||||
const navBarHeight = ref(64)
|
||||
|
||||
// ─── 微信分享 ───────────────────────────────────────────────
|
||||
onShareAppMessage(() => {
|
||||
return {
|
||||
title: '我的普拉提会所,记录每一次进步',
|
||||
path: '/pages/profile/index',
|
||||
imageUrl: '',
|
||||
}
|
||||
})
|
||||
|
||||
onShareTimeline(() => {
|
||||
return {
|
||||
title: '我的普拉提会所,记录每一次进步',
|
||||
query: '',
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
const statusBarPx = sysInfo.statusBarHeight ?? 20
|
||||
|
||||
Reference in New Issue
Block a user