ai-c/pages/outdoor-activities/outdoor-activities.js
2026-02-02 18:21:32 +08:00

375 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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' })
}
}
})