375 lines
11 KiB
JavaScript
375 lines
11 KiB
JavaScript
// pages/outdoor-activities/outdoor-activities.js - 户外郊游页面
|
||
const api = require('../../utils/api')
|
||
const app = getApp()
|
||
|
||
Page({
|
||
data: {
|
||
statusBarHeight: 44,
|
||
navBarHeight: 44,
|
||
totalNavHeight: 88,
|
||
loading: false,
|
||
loadingMore: false,
|
||
activeTab: 'featured',
|
||
|
||
// 活动列表
|
||
activityList: [],
|
||
|
||
// 分页相关
|
||
page: 1,
|
||
limit: 20,
|
||
hasMore: true,
|
||
total: 0,
|
||
|
||
// 二维码弹窗
|
||
showQrcodeModal: false,
|
||
qrcodeImageUrl: ''
|
||
},
|
||
|
||
onLoad() {
|
||
// 计算导航栏高度
|
||
const systemInfo = wx.getSystemInfoSync()
|
||
const statusBarHeight = systemInfo.statusBarHeight || 44
|
||
const menuButton = wx.getMenuButtonBoundingClientRect()
|
||
const navBarHeight = menuButton.height + (menuButton.top - statusBarHeight) * 2
|
||
const totalNavHeight = statusBarHeight + navBarHeight
|
||
|
||
this.setData({
|
||
statusBarHeight,
|
||
navBarHeight,
|
||
totalNavHeight
|
||
})
|
||
|
||
this.loadActivityList()
|
||
},
|
||
|
||
/**
|
||
* 返回上一页
|
||
*/
|
||
onBack() {
|
||
wx.navigateBack()
|
||
},
|
||
|
||
/**
|
||
* 切换活动标签
|
||
*/
|
||
onTabChange(e) {
|
||
const tab = e.currentTarget.dataset.tab
|
||
if (tab === this.data.activeTab) return
|
||
|
||
this.setData({
|
||
activeTab: tab,
|
||
activityList: [],
|
||
page: 1,
|
||
hasMore: true
|
||
})
|
||
this.loadActivityList()
|
||
},
|
||
|
||
/**
|
||
* 下拉刷新
|
||
*/
|
||
onPullDownRefresh() {
|
||
this.loadActivityList(false).finally(() => {
|
||
wx.stopPullDownRefresh()
|
||
})
|
||
},
|
||
|
||
/**
|
||
* 上拉加载更多
|
||
*/
|
||
onReachBottom() {
|
||
if (this.data.hasMore && !this.data.loadingMore && !this.data.loading) {
|
||
this.loadActivityList(true)
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 加载活动列表 - 根据categoryName筛选户外郊游(支持分页)
|
||
*/
|
||
async loadActivityList(isLoadMore = false) {
|
||
if (isLoadMore) {
|
||
this.setData({ loadingMore: true })
|
||
} else {
|
||
this.setData({ loading: true, page: 1, hasMore: true, activityList: [] })
|
||
}
|
||
|
||
try {
|
||
const { activeTab, page, limit } = this.data
|
||
const params = {
|
||
category: 'outdoor',
|
||
limit: limit,
|
||
page: page
|
||
}
|
||
|
||
if (activeTab === 'featured') {
|
||
params.tab = 'featured'
|
||
} else if (activeTab === 'free') {
|
||
params.priceType = 'free'
|
||
} else if (activeTab === 'vip') {
|
||
params.is_vip = true
|
||
} else if (activeTab === 'svip') {
|
||
params.is_svip = true
|
||
}
|
||
|
||
const res = await api.activity.getList(params)
|
||
|
||
if (res.success && res.data && res.data.list) {
|
||
const total = res.data.total || 0
|
||
const allActivities = res.data.list
|
||
const outdoorActivities = allActivities.filter(item => item.categoryName === '户外郊游')
|
||
|
||
let clubQrcode = ''
|
||
const firstWithQrcode = outdoorActivities.find(item => item.activityGuideQrcode || item.activity_guide_qrcode)
|
||
if (firstWithQrcode && !isLoadMore) {
|
||
clubQrcode = firstWithQrcode.activityGuideQrcode || firstWithQrcode.activity_guide_qrcode
|
||
}
|
||
|
||
const newActivityList = outdoorActivities.map(item => {
|
||
const heat = item.heat || (item.likes * 2 + (item.views || 0) + (item.current_participants || 0) * 3)
|
||
|
||
return {
|
||
id: item.id,
|
||
title: item.title,
|
||
date: this.formatDate(item.start_date || item.activityDate),
|
||
location: item.location || '',
|
||
venue: item.venue || '',
|
||
image: item.coverImage || item.cover_image || '',
|
||
heat: Math.floor(heat),
|
||
price: item.price_text || item.priceText || '免费',
|
||
priceType: item.is_free || item.priceType === 'free' ? 'free' : 'paid',
|
||
likes: item.likes || item.likesCount || 0,
|
||
participants: item.current_participants || item.currentParticipants || 0,
|
||
isLiked: item.is_liked || item.isLiked || false,
|
||
isSignedUp: item.is_registered || item.isSignedUp || false,
|
||
status: item.status || (item.currentParticipants >= item.maxParticipants && item.maxParticipants > 0 ? 'full' : 'upcoming'),
|
||
activityGuideQrcode: item.activityGuideQrcode || item.activity_guide_qrcode || ''
|
||
}
|
||
})
|
||
|
||
const hasMore = newActivityList.length >= limit && (this.data.activityList.length + newActivityList.length) < total
|
||
|
||
if (isLoadMore) {
|
||
this.setData({
|
||
activityList: [...this.data.activityList, ...newActivityList],
|
||
loadingMore: false,
|
||
hasMore,
|
||
page: this.data.page + 1,
|
||
total
|
||
})
|
||
} else {
|
||
this.setData({
|
||
activityList: newActivityList,
|
||
hasMore,
|
||
total,
|
||
qrcodeImageUrl: clubQrcode || this.data.qrcodeImageUrl || 'https://ai-c.maimanji.com/images/outdoor-group-qrcode.png'
|
||
})
|
||
}
|
||
|
||
console.log('[outdoor-activities] 加载成功,总数:', total, '当前:', this.data.activityList.length, 'hasMore:', hasMore)
|
||
} else {
|
||
if (isLoadMore) {
|
||
this.setData({ loadingMore: false, hasMore: false })
|
||
} else {
|
||
this.setData({ activityList: [], hasMore: false })
|
||
}
|
||
}
|
||
} catch (err) {
|
||
console.error('加载活动列表失败', err)
|
||
if (isLoadMore) {
|
||
this.setData({ loadingMore: false })
|
||
} else {
|
||
this.setData({ activityList: [], loading: false })
|
||
}
|
||
} finally {
|
||
if (!isLoadMore) {
|
||
this.setData({ loading: false })
|
||
}
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 格式化日期
|
||
*/
|
||
formatDate(dateStr) {
|
||
if (!dateStr) return ''
|
||
const date = new Date(dateStr)
|
||
const year = date.getFullYear()
|
||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||
const day = String(date.getDate()).padStart(2, '0')
|
||
return `${year}年${month}月${day}日`
|
||
},
|
||
|
||
/**
|
||
* 点击活动卡片
|
||
*/
|
||
onActivityTap(e) {
|
||
const id = e.currentTarget.dataset.id
|
||
wx.navigateTo({
|
||
url: `/pages/activity-detail/activity-detail?id=${id}`
|
||
})
|
||
},
|
||
|
||
/**
|
||
* 点赞
|
||
*/
|
||
async onLike(e) {
|
||
const id = e.currentTarget.dataset.id
|
||
const index = e.currentTarget.dataset.index
|
||
|
||
if (!app.globalData.isLoggedIn) {
|
||
wx.navigateTo({ url: '/pages/login/login' })
|
||
return
|
||
}
|
||
|
||
try {
|
||
const res = await api.activity.toggleLike(id)
|
||
if (res.success) {
|
||
this.setData({
|
||
[`activityList[${index}].isLiked`]: res.data.isLiked,
|
||
[`activityList[${index}].likes`]: res.data.likesCount
|
||
})
|
||
}
|
||
} catch (err) {
|
||
console.error('点赞失败', err)
|
||
wx.showToast({ title: '操作失败', icon: 'none' })
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 立即报名
|
||
*/
|
||
async onSignUp(e) {
|
||
const id = e.currentTarget.dataset.id
|
||
const index = e.currentTarget.dataset.index
|
||
|
||
if (!app.globalData.isLoggedIn) {
|
||
wx.navigateTo({ url: '/pages/login/login' })
|
||
return
|
||
}
|
||
|
||
const activity = this.data.activityList[index]
|
||
|
||
// 检查活动状态
|
||
if (activity.status === 'full' || activity.status === 'ended') {
|
||
const qrCode = activity.activityGuideQrcode || activity.activity_guide_qrcode || this.data.qrcodeImageUrl || 'https://ai-c.maimanji.com/api/common/qrcode?type=group'
|
||
this.setData({
|
||
qrcodeImageUrl: qrCode,
|
||
showQrcodeModal: true
|
||
})
|
||
return
|
||
}
|
||
|
||
try {
|
||
if (activity.isSignedUp) {
|
||
// 取消报名
|
||
const res = await api.activity.cancelSignup(id)
|
||
if (res.success) {
|
||
wx.showToast({ title: '已取消报名', icon: 'success' })
|
||
this.loadActivityList()
|
||
}
|
||
} else {
|
||
// 报名
|
||
const res = await api.activity.signup(id)
|
||
if (res.success) {
|
||
wx.showToast({ title: '报名成功', icon: 'success' })
|
||
this.loadActivityList()
|
||
} else {
|
||
// 检查是否需要显示二维码(后端开关关闭或活动已结束)
|
||
if (res.code === 'QR_CODE_REQUIRED' || res.error === 'QR_CODE_REQUIRED' || res.code === 'ACTIVITY_ENDED' || res.error === '活动已结束') {
|
||
if (activity.activityGuideQrcode || activity.activity_guide_qrcode) {
|
||
this.setData({ qrcodeImageUrl: activity.activityGuideQrcode || activity.activity_guide_qrcode })
|
||
}
|
||
this.setData({ showQrcodeModal: true })
|
||
} else {
|
||
wx.showToast({
|
||
title: res.error || '操作失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}
|
||
}
|
||
} catch (err) {
|
||
console.error('报名操作失败', err)
|
||
// 捕获特定错误码以显示二维码
|
||
const isQrRequired = err && (err.code === 'QR_CODE_REQUIRED' || (err.data && err.data.code === 'QR_CODE_REQUIRED'))
|
||
const isActivityEnded = err && (err.code === 'ACTIVITY_ENDED' || (err.data && err.data.code === 'ACTIVITY_ENDED') || err.error === '活动已结束')
|
||
|
||
if (isQrRequired || isActivityEnded) {
|
||
if (activity.activityGuideQrcode || activity.activity_guide_qrcode) {
|
||
this.setData({ qrcodeImageUrl: activity.activityGuideQrcode || activity.activity_guide_qrcode })
|
||
}
|
||
this.setData({ showQrcodeModal: true })
|
||
|
||
if (isActivityEnded) {
|
||
wx.showToast({ title: '活动已结束,进群查看更多', icon: 'none' })
|
||
}
|
||
} else {
|
||
wx.showToast({
|
||
title: err.error || err.message || '操作失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 加入户外郊游群
|
||
*/
|
||
onJoinGroup() {
|
||
// 如果没有二维码,尝试获取第一个活动的二维码
|
||
if (!this.data.qrcodeImageUrl && this.data.activityList.length > 0) {
|
||
const firstWithQrcode = this.data.activityList.find(item => item.activityGuideQrcode || item.activity_guide_qrcode)
|
||
if (firstWithQrcode) {
|
||
this.setData({ qrcodeImageUrl: firstWithQrcode.activityGuideQrcode || firstWithQrcode.activity_guide_qrcode })
|
||
}
|
||
}
|
||
this.setData({ showQrcodeModal: true })
|
||
},
|
||
|
||
/**
|
||
* 关闭二维码弹窗
|
||
*/
|
||
onCloseQrcodeModal() {
|
||
this.setData({ showQrcodeModal: false })
|
||
},
|
||
|
||
/**
|
||
* 阻止冒泡
|
||
*/
|
||
preventBubble() {
|
||
return
|
||
},
|
||
|
||
/**
|
||
* 保存二维码
|
||
*/
|
||
async onSaveQrcode() {
|
||
try {
|
||
const { qrcodeImageUrl } = this.data
|
||
const downloadRes = await new Promise((resolve, reject) => {
|
||
wx.downloadFile({
|
||
url: qrcodeImageUrl,
|
||
success: resolve,
|
||
fail: reject
|
||
})
|
||
})
|
||
|
||
if (downloadRes.statusCode !== 200) throw new Error('下载失败')
|
||
|
||
await new Promise((resolve, reject) => {
|
||
wx.saveImageToPhotosAlbum({
|
||
filePath: downloadRes.tempFilePath,
|
||
success: resolve,
|
||
fail: reject
|
||
})
|
||
})
|
||
|
||
wx.showToast({ title: '保存成功', icon: 'success' })
|
||
this.onCloseQrcodeModal()
|
||
} catch (err) {
|
||
console.error('保存失败', err)
|
||
wx.showToast({ title: '保存失败', icon: 'none' })
|
||
}
|
||
}
|
||
})
|