整合api请求、添加购买会员卡页面、登陆页面

This commit is contained in:
future
2026-06-23 22:17:53 +08:00
parent 1c547a717e
commit 8d8c823616
70 changed files with 7666 additions and 2656 deletions
+136 -75
View File
@@ -99,14 +99,25 @@
<view class="bk-card__footer">
<text class="bk-card__footer-info">{{ item.footerText }}</text>
<view
v-if="item.canCancel"
class="bk-card__cancel"
hover-class="mi-tap-btn--hover"
:hover-stay-time="150"
@tap.stop="cancelBooking(item)"
>
<text class="bk-card__cancel-text">取消预约</text>
<view class="bk-card__actions">
<view
v-if="item.canSignin"
class="bk-card__signin"
hover-class="mi-tap-btn--hover"
:hover-stay-time="150"
@tap.stop="signinCourse(item)"
>
<text class="bk-card__signin-text">签到</text>
</view>
<view
v-if="item.canCancel"
class="bk-card__cancel"
hover-class="mi-tap-btn--hover"
:hover-stay-time="150"
@tap.stop="cancelBooking(item)"
>
<text class="bk-card__cancel-text">取消预约</text>
</view>
</view>
</view>
</view>
@@ -122,83 +133,133 @@
</view>
</template>
<script>
<script setup>
import { ref, computed } from 'vue'
import MemberInfoSubNav from '@/components/memberInfo/MemberInfoSubNav.vue'
import { PAGE, navigateToPage } from '@/common/constants/routes.js'
import { bookingMock } from '@/common/memberInfo/mockData.js'
import { PAGE, navigateToPage, backToMemberCenter } from '@/common/constants/routes.js'
import {
loadMemberStore,
cancelOngoingBooking,
formatUpcomingAlert
} from '@/common/memberInfo/store.js'
import { canCancelBooking } from '@/common/memberInfo/bookingStore.js'
import { subPageMixin } from '@/common/memberInfo/mixins.js'
import { canCancelBooking, canSigninCourse } from '@/common/memberInfo/bookingStore.js'
import { signinGroupCourse, cancelBooking as cancelBookingApi } from '@/api/main.js'
export default {
components: { MemberInfoSubNav },
mixins: [subPageMixin],
data() {
return {
tabs: bookingMock.tabs,
ongoingBookings: [],
historyBookings: [],
activeTab: 'ongoing'
}
},
computed: {
displayedBookings() {
return this.activeTab === 'ongoing'
? this.ongoingBookings
: this.historyBookings
},
upcomingAlert() {
return formatUpcomingAlert(this.ongoingBookings[0])
}
},
onShow() {
this.refreshFromStore()
},
methods: {
refreshFromStore() {
const store = loadMemberStore()
this.ongoingBookings = store.ongoingBookings.map((item) => ({
...item,
canCancel: canCancelBooking(item)
}))
this.historyBookings = store.historyBookings.map((item) => ({ ...item }))
},
goCourseList() {
navigateToPage(PAGE.COURSE_LIST)
},
setActiveTab(tab) {
this.activeTab = tab
},
cancelBooking(item) {
if (!canCancelBooking(item)) {
uni.showToast({ title: '距开课不足2小时,无法取消', icon: 'none' })
return
}
uni.showModal({
title: '取消预约',
content: `确定要取消「${item.title}」吗?`,
success: (res) => {
if (!res.confirm) return
const store = loadMemberStore()
const result = cancelOngoingBooking(store, item.id)
if (!result.ok) {
uni.showToast({ title: result.message, icon: 'none' })
return
}
this.refreshFromStore()
uni.showToast({ title: '已取消', icon: 'success' })
}
})
}
}
const tabs = ref([])
const ongoingBookings = ref([])
const historyBookings = ref([])
const activeTab = ref('ongoing')
const displayedBookings = computed(() => {
return activeTab.value === 'ongoing'
? ongoingBookings.value
: historyBookings.value
})
const upcomingAlert = computed(() => {
return formatUpcomingAlert(ongoingBookings.value[0])
})
function refreshFromStore() {
const store = loadMemberStore()
ongoingBookings.value = store.ongoingBookings.map((item) => ({
...item,
canCancel: canCancelBooking(item),
canSignin: canSigninCourse(item)
}))
historyBookings.value = store.historyBookings.map((item) => ({ ...item }))
}
function goBack() {
backToMemberCenter()
}
function goCourseList() {
navigateToPage(PAGE.COURSE_LIST)
}
function setActiveTab(tab) {
activeTab.value = tab
}
// 签到团课
async function signinCourse(item) {
const store = loadMemberStore()
const memberId = store.memberProfile?.id || store.profile?.id
if (!memberId) {
uni.showToast({ title: '请先登录', icon: 'none' })
return
}
uni.showModal({
title: '确认签到',
content: `确认签到「${item.title}」吗?`,
success: async (res) => {
if (!res.confirm) return
uni.showLoading({ title: '签到中...', mask: true })
try {
const result = await signinGroupCourse(Number(memberId), Number(item.courseId || item.id))
uni.hideLoading()
if (result.code === 200 || result.success) {
uni.showToast({ title: '签到成功', icon: 'success' })
// 更新本地状态
refreshFromStore()
} else {
uni.showToast({ title: result.message || '签到失败', icon: 'none' })
}
} catch (e) {
uni.hideLoading()
console.error('[booking] 签到失败:', e)
uni.showToast({ title: e.message || '签到失败', icon: 'none' })
}
}
})
}
// 取消预约
async function cancelBooking(item) {
if (!canCancelBooking(item)) {
uni.showToast({ title: '距开课不足2小时,无法取消', icon: 'none' })
return
}
uni.showModal({
title: '取消预约',
content: `确定要取消「${item.title}」吗?`,
success: async (res) => {
if (!res.confirm) return
uni.showLoading({ title: '取消中...', mask: true })
try {
// 调用后端API取消预约
const result = await cancelBookingApi(Number(item.id))
uni.hideLoading()
if (result.code === 200 || result.success) {
// 更新本地状态
const store = loadMemberStore()
const localResult = cancelOngoingBooking(store, item.id)
if (localResult.ok) {
refreshFromStore()
}
uni.showToast({ title: '已取消', icon: 'success' })
} else {
uni.showToast({ title: result.message || '取消失败', icon: 'none' })
}
} catch (e) {
uni.hideLoading()
console.error('[booking] 取消预约失败:', e)
uni.showToast({ title: e.message || '取消失败', icon: 'none' })
}
}
})
}
refreshFromStore()
</script>
<style>
<style lang="scss">
@import '@/common/style/base.css';
@import '@/common/style/memberInfo/pages/page-reset.css';
@import '@/common/style/memberInfo/pages/sub-page-base.css';