feat(海报打卡): 添加加载状态和错误处理,集成API获取海报数据
实现海报打卡页面的加载状态和错误处理逻辑 集成API获取海报详情数据并更新页面状态 添加重新加载功能,优化用户体验 处理海报背景图片上传和保存逻辑
Showing
1 changed file
with
269 additions
and
160 deletions
| 1 | <template> | 1 | <template> |
| 2 | <view class="poster-checkin-page bg-gray-50 h-screen flex flex-col"> | 2 | <view class="poster-checkin-page bg-gray-50 h-screen flex flex-col"> |
| 3 | - <!-- 活动信息区域 --> | 3 | + <!-- 加载状态 --> |
| 4 | - <view v-if="pageState === 'normal' || pageState === 'no-checkin'" class="bg-white mx-4 mt-4 mb-2 rounded-lg shadow-sm p-4"> | 4 | + <view v-if="isLoading" class="flex-1 flex items-center justify-center"> |
| 5 | - <!-- 活动主题 --> | 5 | + <view class="text-center"> |
| 6 | - <view class="text-lg font-bold text-gray-800 mb-2"> | 6 | + <view class="text-gray-500 mb-2">加载中...</view> |
| 7 | - {{ activityInfo.title }} | ||
| 8 | </view> | 7 | </view> |
| 8 | + </view> | ||
| 9 | 9 | ||
| 10 | - <!-- 打卡进度 --> | 10 | + <!-- 错误状态 --> |
| 11 | - <view class="flex items-center mb-2"> | 11 | + <view v-else-if="apiError" class="flex-1 flex items-center justify-center"> |
| 12 | - <view class="flex items-center"> | 12 | + <view class="text-center"> |
| 13 | - <view | 13 | + <view class="text-red-500 mb-2">{{ apiError }}</view> |
| 14 | - v-for="(point, index) in activityInfo.checkPoints" | 14 | + <view @tap="fetchPosterDetail" class="bg-blue-500 text-white px-4 py-2 rounded"> |
| 15 | - :key="index" | 15 | + 重新加载 |
| 16 | - class="w-2 h-2 rounded-full mr-2 flex items-center justify-center" | ||
| 17 | - :class="point.completed ? 'bg-orange-400' : 'bg-gray-300'" | ||
| 18 | - > | ||
| 19 | - </view> | ||
| 20 | </view> | 16 | </view> |
| 21 | - <text class="text-xs text-gray-600">{{ activityInfo.completedCount }}/{{ activityInfo.totalCount }}</text> | ||
| 22 | - </view> | ||
| 23 | - | ||
| 24 | - <!-- 活动截止日期 --> | ||
| 25 | - <view class="text-sm text-gray-500"> | ||
| 26 | - 活动截止日期:{{ activityInfo.endDate }} | ||
| 27 | </view> | 17 | </view> |
| 28 | </view> | 18 | </view> |
| 29 | 19 | ||
| 30 | - <!-- 海报预览区域 - 正常状态 --> | 20 | + <!-- 正常内容 --> |
| 31 | - <view v-if="pageState === 'normal'" class="flex-1 mx-4 relative" style="overflow: visible; padding-bottom: 100rpx;"> | 21 | + <template v-else> |
| 32 | - <view class="h-full relative flex items-center justify-center"> | 22 | + <!-- 活动信息区域 --> |
| 33 | - <view v-if="currentPoster.path" class="w-full h-full relative"> | 23 | + <view v-if="pageState === 'normal' || pageState === 'no-checkin'" class="bg-white mx-4 mt-4 mb-2 rounded-lg shadow-sm p-4"> |
| 34 | - <image | 24 | + <!-- 活动主题 --> |
| 35 | - :src="currentPoster.path" | 25 | + <view class="text-lg font-bold text-gray-800 mb-2"> |
| 36 | - mode="widthFix" | 26 | + {{ activityInfo.title }} |
| 37 | - class="w-full h-full" | ||
| 38 | - /> | ||
| 39 | - <!-- 打卡点标题 --> | ||
| 40 | - <!-- <view class="absolute bottom-2 left-2 bg-blue-500 text-white text-xs px-2 py-1 rounded"> | ||
| 41 | - {{ posterList[currentPosterIndex]?.title || '海报生成中' }} | ||
| 42 | - </view> --> | ||
| 43 | - <!-- 点击预览提示 --> | ||
| 44 | - <!-- <view @tap="previewPoster" class="absolute bottom-2 right-2 bg-black bg-opacity-50 text-white text-xs px-2 py-1 rounded"> | ||
| 45 | - 点击预览 | ||
| 46 | - </view> --> | ||
| 47 | </view> | 27 | </view> |
| 48 | - </view> | ||
| 49 | 28 | ||
| 50 | - <!-- 左箭头按钮 --> | 29 | + <!-- 打卡进度 --> |
| 51 | - <view | 30 | + <view class="flex items-center mb-2"> |
| 52 | - class="absolute top-1/2 transform -translate-y-1/2 w-10 h-10 rounded-full flex items-center justify-center transition-all duration-200 z-10" | 31 | + <view class="flex items-center"> |
| 53 | - :class="currentPosterIndex > 0 ? 'bg-orange-400 text-white shadow-lg' : 'bg-gray-300 text-gray-500'" | 32 | + <view |
| 54 | - style="left: -20rpx;" | 33 | + v-for="(point, index) in activityInfo.checkPoints" |
| 55 | - @tap="previousPoster" | 34 | + :key="index" |
| 56 | - > | 35 | + class="w-2 h-2 rounded-full mr-2 flex items-center justify-center" |
| 57 | - <Left size="16"></Left> | 36 | + :class="point.completed ? 'bg-orange-400' : 'bg-gray-300'" |
| 58 | - </view> | 37 | + > |
| 38 | + </view> | ||
| 39 | + </view> | ||
| 40 | + <text class="text-xs text-gray-600">{{ activityInfo.completedCount }}/{{ activityInfo.totalCount }}</text> | ||
| 41 | + </view> | ||
| 59 | 42 | ||
| 60 | - <!-- 右箭头按钮 --> | 43 | + <!-- 活动截止日期 --> |
| 61 | - <view | 44 | + <view class="text-sm text-gray-500"> |
| 62 | - class="absolute top-1/2 transform -translate-y-1/2 w-10 h-10 rounded-full flex items-center justify-center transition-all duration-200 z-10" | 45 | + 活动截止日期:{{ activityInfo.endDate }} |
| 63 | - :class="currentPosterIndex < posterList.length - 1 ? 'bg-orange-400 text-white shadow-lg' : 'bg-gray-300 text-gray-500'" | 46 | + </view> |
| 64 | - style="right: -20rpx;" | ||
| 65 | - @tap="nextPoster" | ||
| 66 | - > | ||
| 67 | - <Right size="16"></Right> | ||
| 68 | </view> | 47 | </view> |
| 69 | - </view> | ||
| 70 | 48 | ||
| 71 | - <!-- 没有打卡信息的空状态 --> | 49 | + <!-- 海报预览区域 - 正常状态 --> |
| 72 | - <view v-if="pageState === 'no-checkin'" class="flex-1 mx-4 mb-2 flex justify-center"> | 50 | + <view v-if="pageState === 'normal'" class="flex-1 mx-4 relative" style="overflow: visible; padding-bottom: 100rpx;"> |
| 73 | - <view class="bg-white rounded-lg shadow-sm p-8 text-center max-w-sm"> | 51 | + <view class="h-full relative flex items-center justify-center"> |
| 74 | - <view class="text-6xl mb-4">📸</view> | 52 | + <view v-if="currentPoster.path" class="w-full h-full relative"> |
| 75 | - <view class="text-lg font-bold text-gray-800 mb-2">打卡信息为空</view> | 53 | + <image |
| 76 | - <view class="text-sm text-gray-500 mb-4">您还没有打卡记录,请先参加活动打卡后再来生成海报</view> | 54 | + :src="currentPoster.path" |
| 77 | - <view class="text-xs text-orange-500">完成打卡后即可生成专属海报</view> | 55 | + mode="widthFix" |
| 78 | - </view> | 56 | + class="w-full h-full" |
| 79 | - </view> | 57 | + /> |
| 58 | + <!-- 打卡点标题 --> | ||
| 59 | + <!-- <view class="absolute bottom-2 left-2 bg-blue-500 text-white text-xs px-2 py-1 rounded"> | ||
| 60 | + {{ posterList[currentPosterIndex]?.title || '海报生成中' }} | ||
| 61 | + </view> --> | ||
| 62 | + <!-- 点击预览提示 --> | ||
| 63 | + <!-- <view @tap="previewPoster" class="absolute bottom-2 right-2 bg-black bg-opacity-50 text-white text-xs px-2 py-1 rounded"> | ||
| 64 | + 点击预览 | ||
| 65 | + </view> --> | ||
| 66 | + </view> | ||
| 67 | + </view> | ||
| 80 | 68 | ||
| 81 | - <!-- 底部操作按钮 - 仅在正常状态显示 --> | 69 | + <!-- 左箭头按钮 --> |
| 82 | - <view v-if="pageState === 'normal'" class="bg-white border-t border-gray-200 p-4 safe-area-bottom" style="position: fixed; bottom: 0; left: 0; right: 0;"> | ||
| 83 | - <view class="flex gap-4"> | ||
| 84 | <view | 70 | <view |
| 85 | - class="flex-1 bg-gradient-to-r from-orange-400 to-orange-500 text-white py-3 px-6 rounded-lg font-medium shadow-lg active:scale-95 transition-transform duration-150 flex items-center justify-center gap-2" | 71 | + class="absolute top-1/2 transform -translate-y-1/2 w-10 h-10 rounded-full flex items-center justify-center transition-all duration-200 z-10" |
| 86 | - @click="chooseBackgroundImage" | 72 | + :class="currentPosterIndex > 0 ? 'bg-orange-400 text-white shadow-lg' : 'bg-gray-300 text-gray-500'" |
| 73 | + style="left: -20rpx;" | ||
| 74 | + @tap="previousPoster" | ||
| 87 | > | 75 | > |
| 88 | - <text>{{ currentPosterHasCustomBackground ? '更改照片' : '上传照片' }}</text> | 76 | + <Left size="16"></Left> |
| 89 | </view> | 77 | </view> |
| 78 | + | ||
| 79 | + <!-- 右箭头按钮 --> | ||
| 90 | <view | 80 | <view |
| 91 | - class="flex-1 text-white py-3 px-6 rounded-lg font-medium shadow-lg active:scale-95 transition-transform duration-150 flex items-center justify-center gap-2" | 81 | + class="absolute top-1/2 transform -translate-y-1/2 w-10 h-10 rounded-full flex items-center justify-center transition-all duration-200 z-10" |
| 92 | - :class="posterPath ? 'bg-gradient-to-r from-green-400 to-green-500' : (posterGenerateFailed ? 'bg-gradient-to-r from-orange-400 to-orange-500' : 'bg-gray-400')" | 82 | + :class="currentPosterIndex < posterList.length - 1 ? 'bg-orange-400 text-white shadow-lg' : 'bg-gray-300 text-gray-500'" |
| 93 | - @click="handlePosterAction" | 83 | + style="right: -20rpx;" |
| 94 | - :disabled="!posterPath && !posterGenerateFailed" | 84 | + @tap="nextPoster" |
| 95 | > | 85 | > |
| 96 | - <text>{{ posterPath ? '保存海报' : (posterGenerateFailed ? '重新生成' : '生成中...') }}</text> | 86 | + <Right size="16"></Right> |
| 97 | </view> | 87 | </view> |
| 98 | </view> | 88 | </view> |
| 99 | - </view> | 89 | + |
| 90 | + <!-- 没有打卡信息的空状态 --> | ||
| 91 | + <view v-if="pageState === 'no-checkin'" class="flex-1 mx-4 mb-2 flex justify-center"> | ||
| 92 | + <view class="bg-white rounded-lg shadow-sm p-8 text-center max-w-sm"> | ||
| 93 | + <view class="text-6xl mb-4">📸</view> | ||
| 94 | + <view class="text-lg font-bold text-gray-800 mb-2">打卡信息为空</view> | ||
| 95 | + <view class="text-sm text-gray-500 mb-4">您还没有打卡记录,请先参加活动打卡后再来生成海报</view> | ||
| 96 | + <view class="text-xs text-orange-500">完成打卡后即可生成专属海报</view> | ||
| 97 | + </view> | ||
| 98 | + </view> | ||
| 99 | + | ||
| 100 | + <!-- 底部操作按钮 - 仅在正常状态显示 --> | ||
| 101 | + <view v-if="pageState === 'normal'" class="bg-white border-t border-gray-200 p-4 safe-area-bottom" style="position: fixed; bottom: 0; left: 0; right: 0;"> | ||
| 102 | + <view class="flex gap-4"> | ||
| 103 | + <view | ||
| 104 | + class="flex-1 bg-gradient-to-r from-orange-400 to-orange-500 text-white py-3 px-6 rounded-lg font-medium shadow-lg active:scale-95 transition-transform duration-150 flex items-center justify-center gap-2" | ||
| 105 | + @click="chooseBackgroundImage" | ||
| 106 | + > | ||
| 107 | + <text>{{ currentPosterHasCustomBackground ? '更改照片' : '上传照片' }}</text> | ||
| 108 | + </view> | ||
| 109 | + <view | ||
| 110 | + class="flex-1 text-white py-3 px-6 rounded-lg font-medium shadow-lg active:scale-95 transition-transform duration-150 flex items-center justify-center gap-2" | ||
| 111 | + :class="posterPath ? 'bg-gradient-to-r from-green-400 to-green-500' : (posterGenerateFailed ? 'bg-gradient-to-r from-orange-400 to-orange-500' : 'bg-gray-400')" | ||
| 112 | + @click="handlePosterAction" | ||
| 113 | + :disabled="!posterPath && !posterGenerateFailed" | ||
| 114 | + > | ||
| 115 | + <text>{{ posterPath ? '保存海报' : (posterGenerateFailed ? '重新生成' : '生成中...') }}</text> | ||
| 116 | + </view> | ||
| 117 | + </view> | ||
| 118 | + </view> | ||
| 119 | + </template> | ||
| 100 | 120 | ||
| 101 | <!-- 海报生成组件 - 仅在正常状态显示 --> | 121 | <!-- 海报生成组件 - 仅在正常状态显示 --> |
| 102 | <PosterBuilder | 122 | <PosterBuilder |
| ... | @@ -126,7 +146,7 @@ import { Left, Right } from '@nutui/icons-vue-taro' | ... | @@ -126,7 +146,7 @@ import { Left, Right } from '@nutui/icons-vue-taro' |
| 126 | import PosterBuilder from '@/components/PosterBuilder/index.vue' | 146 | import PosterBuilder from '@/components/PosterBuilder/index.vue' |
| 127 | import BASE_URL from '@/utils/config' | 147 | import BASE_URL from '@/utils/config' |
| 128 | // 导入获取海报详情的API | 148 | // 导入获取海报详情的API |
| 129 | -import { getPosterDetailAPI } from '@/api/map' | 149 | +import { getPosterDetailAPI, savePosterBackgroundAPI } from '@/api/map' |
| 130 | // 默认背景图 | 150 | // 默认背景图 |
| 131 | const defaultBackground = 'https://cdn.ipadbiz.cn/lls_prog/images/%E6%B5%B7%E6%8A%A5%E9%BB%98%E8%AE%A4%E8%83%8C%E6%99%AF%E5%9B%BE1.png' | 151 | const defaultBackground = 'https://cdn.ipadbiz.cn/lls_prog/images/%E6%B5%B7%E6%8A%A5%E9%BB%98%E8%AE%A4%E8%83%8C%E6%99%AF%E5%9B%BE1.png' |
| 132 | 152 | ||
| ... | @@ -145,86 +165,158 @@ const pageParams = ref({ | ... | @@ -145,86 +165,158 @@ const pageParams = ref({ |
| 145 | id: '', | 165 | id: '', |
| 146 | }) | 166 | }) |
| 147 | 167 | ||
| 168 | +// API数据状态 | ||
| 169 | +const apiData = ref(null) | ||
| 170 | +const isLoading = ref(false) | ||
| 171 | +const apiError = ref(null) | ||
| 172 | + | ||
| 148 | // TODO: 有真实数据的时候, 通过ID获取具体地点的海报信息, 赋值currentPosterIndex获取特定的海报 | 173 | // TODO: 有真实数据的时候, 通过ID获取具体地点的海报信息, 赋值currentPosterIndex获取特定的海报 |
| 149 | 174 | ||
| 150 | // 图片预览相关 | 175 | // 图片预览相关 |
| 151 | const previewVisible = ref(false) | 176 | const previewVisible = ref(false) |
| 152 | const previewImages = ref([]) | 177 | const previewImages = ref([]) |
| 153 | 178 | ||
| 154 | -// 活动信息数据 - 模拟不同状态,实际使用时从API获取 | 179 | +/** |
| 155 | -const activityInfo = ref({ | 180 | + * 获取海报详情数据 |
| 156 | - title: '南京路乐龄时尚消费主题路线', | 181 | + */ |
| 157 | - checkPoints: [ | 182 | +const fetchPosterDetail = async () => { |
| 158 | - { id: 1, name: '起点签到', completed: true }, | 183 | + try { |
| 159 | - { id: 2, name: '商圈探索', completed: true }, | 184 | + isLoading.value = true |
| 160 | - { id: 3, name: '文化体验', completed: true }, | 185 | + apiError.value = null |
| 161 | - { id: 4, name: '美食品鉴', completed: false }, | ||
| 162 | - { id: 5, name: '终点打卡', completed: false } | ||
| 163 | - ], | ||
| 164 | - completedCount: 3, | ||
| 165 | - totalCount: 5, | ||
| 166 | - endDate: '2025年9月7日' | ||
| 167 | -}) | ||
| 168 | 186 | ||
| 169 | -// activityInfo.value = {}; | 187 | + const response = await getPosterDetailAPI({}) |
| 170 | 188 | ||
| 171 | -// 海报数据列表 - 合并海报基础信息和内容数据,实际使用时从API获取 | 189 | + if (response.code === 1) { |
| 172 | -const posterList = ref([ | 190 | + apiData.value = response.data |
| 173 | - { | 191 | + console.log('获取海报详情成功:', response.data) |
| 174 | - id: 1, | 192 | + |
| 175 | - title: '起点签到', | 193 | + // 根据API数据更新活动信息 |
| 176 | - path: '', | 194 | + updateActivityInfo() |
| 177 | - checkPointId: 1, | 195 | + |
| 178 | - backgroundImage: 'https://cdn.ipadbiz.cn/lls_prog/images/%E6%B5%B7%E6%8A%A5%E9%BB%98%E8%AE%A4%E8%83%8C%E6%99%AF%E5%9B%BE1.png', | 196 | + // 根据API数据更新海报列表 |
| 179 | - // 海报内容数据 | 197 | + updatePosterList() |
| 180 | - user: { | 198 | + |
| 181 | - avatar: 'https://cdn.ipadbiz.cn/icon/tou@2x.png', | 199 | + // 根据pageParams.id设置当前海报索引 |
| 182 | - nickname: '张大爷' | 200 | + setCurrentPosterIndex() |
| 183 | - }, | 201 | + |
| 184 | - family: { | 202 | + } else { |
| 185 | - name: '幸福之家', | 203 | + apiError.value = response.msg || '获取海报详情失败' |
| 186 | - description: '一家人整整齐齐最重要' | 204 | + console.error('获取海报详情失败:', response.msg) |
| 187 | - }, | 205 | + } |
| 188 | - activity: { | 206 | + } catch (error) { |
| 189 | - logo: 'https://cdn.ipadbiz.cn/lls_prog/images/%E6%B5%B7%E6%8A%A5%E5%B7%A6%E4%B8%8A%E8%A7%92logo.png', | 207 | + apiError.value = '网络请求失败' |
| 190 | - name: '南京路时尚citywalk' | 208 | + console.error('获取海报详情异常:', error) |
| 191 | - }, | 209 | + } finally { |
| 192 | - level: { | 210 | + isLoading.value = false |
| 193 | - logo: 'https://cdn.ipadbiz.cn/lls_prog/images/%E6%B5%B7%E6%8A%A5%E5%8F%B3%E4%B8%8B%E8%A7%92icon.png', | 211 | + } |
| 194 | - name: '卡点: 上海市第一百货商店' | 212 | +} |
| 195 | - }, | 213 | + |
| 196 | - qrcode: 'https://cdn.ipadbiz.cn/space/068a790496c87cb8d2ed6e551401c544.png', | 214 | +/** |
| 197 | - qrcodeDesc: '长按识别,来,我们一起打卡!' | 215 | + * 根据API数据更新活动信息 |
| 198 | - }, | 216 | + */ |
| 199 | - { | 217 | +const updateActivityInfo = () => { |
| 200 | - id: 2, | 218 | + if (!apiData.value) return |
| 201 | - title: '商圈探索', | 219 | + |
| 220 | + const { title, end_date, details, show_detail_index } = apiData.value | ||
| 221 | + | ||
| 222 | + // 转换details为checkPoints格式 | ||
| 223 | + const checkPoints = details.map((detail, index) => ({ | ||
| 224 | + id: detail.id, | ||
| 225 | + name: detail.name, | ||
| 226 | + completed: detail.is_checked === true | ||
| 227 | + })) | ||
| 228 | + | ||
| 229 | + // 计算已完成数量 | ||
| 230 | + const completedCount = checkPoints.filter(point => point.completed).length | ||
| 231 | + | ||
| 232 | + activityInfo.value = { | ||
| 233 | + title: title || '海报打卡活动', | ||
| 234 | + checkPoints, | ||
| 235 | + completedCount, | ||
| 236 | + totalCount: checkPoints.length, | ||
| 237 | + endDate: end_date || '', | ||
| 238 | + showDetailIndex: show_detail_index || 0 | ||
| 239 | + } | ||
| 240 | +} | ||
| 241 | + | ||
| 242 | +/** | ||
| 243 | + * 根据API数据更新海报列表 | ||
| 244 | + */ | ||
| 245 | +const updatePosterList = () => { | ||
| 246 | + if (!apiData.value) return | ||
| 247 | + | ||
| 248 | + const { details, family, qrcode_url } = apiData.value | ||
| 249 | + | ||
| 250 | + // 只显示is_checked为真的关卡 | ||
| 251 | + const checkedDetails = details.filter(detail => | ||
| 252 | + detail.is_checked === true | ||
| 253 | + ) | ||
| 254 | + | ||
| 255 | + posterList.value = checkedDetails.map((detail, index) => ({ | ||
| 256 | + id: detail.id, | ||
| 257 | + title: detail.name, | ||
| 202 | path: '', | 258 | path: '', |
| 203 | - checkPointId: 2, | 259 | + checkPointId: detail.id, |
| 204 | - backgroundImage: 'https://cdn.ipadbiz.cn/lls_prog/images/%E5%85%B3%E5%8D%A12-%E5%9B%BE%E5%B1%82%201.png', | 260 | + backgroundImage: detail.background_url || defaultBackground, |
| 205 | // 海报内容数据 | 261 | // 海报内容数据 |
| 206 | user: { | 262 | user: { |
| 207 | - avatar: 'https://cdn.ipadbiz.cn/icon/tou@2x.png', | 263 | + avatar: family?.avatar_url || 'https://cdn.ipadbiz.cn/icon/tou@2x.png', // 默认头像,后续可从用户信息获取 |
| 208 | - nickname: '李奶奶' | 264 | + nickname: '用户昵称' // 默认昵称,后续可从用户信息获取 |
| 209 | }, | 265 | }, |
| 210 | family: { | 266 | family: { |
| 211 | - name: '温馨小家', | 267 | + name: family?.name || '我的家庭', |
| 212 | - description: '健康快乐每一天' | 268 | + description: '' |
| 213 | }, | 269 | }, |
| 214 | activity: { | 270 | activity: { |
| 215 | - logo: 'https://cdn.ipadbiz.cn/lls_prog/images/%E6%B5%B7%E6%8A%A5%E5%B7%A6%E4%B8%8A%E8%A7%92logo.png', | 271 | + logo: detail.main_slogan || 'https://cdn.ipadbiz.cn/lls_prog/images/%E6%B5%B7%E6%8A%A5%E5%B7%A6%E4%B8%8A%E8%A7%92logo.png', |
| 216 | - name: '南京路时尚citywalk' | ||
| 217 | }, | 272 | }, |
| 218 | level: { | 273 | level: { |
| 219 | - logo: 'https://cdn.ipadbiz.cn/lls_prog/images/%E5%85%B3%E5%8D%A12-%E4%BB%8A%E6%9C%9D%E8%B7%9F%E6%97%81%E5%8F%8B%20%E4%B8%80%E9%81%93%E6%9D%A5%E4%B9%B0%E4%B9%B0%E4%B9%B0_1.png', | 274 | + logo: detail.sub_slogan || 'https://cdn.ipadbiz.cn/lls_prog/images/%E6%B5%B7%E6%8A%A5%E5%8F%B3%E4%B8%8B%E8%A7%92icon.png', |
| 220 | - name: '第二关卡' | 275 | + name: detail.name || '海报打卡活动', |
| 221 | }, | 276 | }, |
| 222 | - qrcode: 'https://cdn.ipadbiz.cn/space/068a790496c87cb8d2ed6e551401c544.png', | 277 | + qrcode: qrcode_url, |
| 223 | qrcodeDesc: '长按识别,来,我们一起打卡!' | 278 | qrcodeDesc: '长按识别,来,我们一起打卡!' |
| 224 | - }, | 279 | + })) |
| 225 | -]) | 280 | +} |
| 281 | + | ||
| 282 | +/** | ||
| 283 | + * 根据pageParams.id设置当前海报索引 | ||
| 284 | + */ | ||
| 285 | +const setCurrentPosterIndex = () => { | ||
| 286 | + if (!posterList.value.length) { | ||
| 287 | + currentPosterIndex.value = 0 | ||
| 288 | + return | ||
| 289 | + } | ||
| 290 | + | ||
| 291 | + // 如果有指定的ID,查找对应的海报索引 | ||
| 292 | + if (pageParams.value.id) { | ||
| 293 | + const targetIndex = posterList.value.findIndex(poster => | ||
| 294 | + poster.checkPointId.toString() === pageParams.value.id.toString() | ||
| 295 | + ) | ||
| 296 | + | ||
| 297 | + if (targetIndex !== -1) { | ||
| 298 | + currentPosterIndex.value = targetIndex | ||
| 299 | + return | ||
| 300 | + } | ||
| 301 | + } | ||
| 226 | 302 | ||
| 227 | -// posterList.value = [] | 303 | + // 如果没有ID或没找到对应的关卡ID,默认显示第一个已打卡的项(索引0) |
| 304 | + // 因为posterList已经过滤了is_checked=true的项,所以第一个就是我们要的 | ||
| 305 | + currentPosterIndex.value = 0 | ||
| 306 | +} | ||
| 307 | + | ||
| 308 | +// 活动信息数据 - 将由API数据填充 | ||
| 309 | +const activityInfo = ref({ | ||
| 310 | + title: '', | ||
| 311 | + checkPoints: [], | ||
| 312 | + completedCount: 0, | ||
| 313 | + totalCount: 0, | ||
| 314 | + endDate: '', | ||
| 315 | + showDetailIndex: 0 | ||
| 316 | +}) | ||
| 317 | + | ||
| 318 | +// 海报数据列表 - 将由API数据填充 | ||
| 319 | +const posterList = ref([]) | ||
| 228 | 320 | ||
| 229 | // 数据状态检查 | 321 | // 数据状态检查 |
| 230 | const hasActivityInfo = computed(() => { | 322 | const hasActivityInfo = computed(() => { |
| ... | @@ -416,7 +508,7 @@ const posterConfig = computed(() => { | ... | @@ -416,7 +508,7 @@ const posterConfig = computed(() => { |
| 416 | /** | 508 | /** |
| 417 | * 页面加载时初始化 | 509 | * 页面加载时初始化 |
| 418 | */ | 510 | */ |
| 419 | -onMounted(() => { | 511 | +onMounted(async () => { |
| 420 | Taro.setNavigationBarTitle({ title: '海报打卡' }) | 512 | Taro.setNavigationBarTitle({ title: '海报打卡' }) |
| 421 | 513 | ||
| 422 | // 获取页面参数 | 514 | // 获取页面参数 |
| ... | @@ -429,6 +521,16 @@ onMounted(() => { | ... | @@ -429,6 +521,16 @@ onMounted(() => { |
| 429 | 521 | ||
| 430 | console.log('海报打卡页面接收到的参数:', pageParams.value) | 522 | console.log('海报打卡页面接收到的参数:', pageParams.value) |
| 431 | 523 | ||
| 524 | + // 获取海报详情数据 | ||
| 525 | + await fetchPosterDetail() | ||
| 526 | + | ||
| 527 | + // 数据获取完成后检查页面状态 | ||
| 528 | + if (pageState.value === 'no-activity') { | ||
| 529 | + // 没有活动信息,显示确认对话框 | ||
| 530 | + showNoActivityConfirm() | ||
| 531 | + return | ||
| 532 | + } | ||
| 533 | + | ||
| 432 | // 页面加载时检查是否需要生成当前海报 | 534 | // 页面加载时检查是否需要生成当前海报 |
| 433 | generateCurrentPosterIfNeeded() | 535 | generateCurrentPosterIfNeeded() |
| 434 | }) | 536 | }) |
| ... | @@ -576,9 +678,10 @@ const chooseBackgroundImage = () => { | ... | @@ -576,9 +678,10 @@ const chooseBackgroundImage = () => { |
| 576 | 678 | ||
| 577 | uploadBackgroundImage(tempFile.path) | 679 | uploadBackgroundImage(tempFile.path) |
| 578 | }, | 680 | }, |
| 579 | - fail: () => { | 681 | + fail: (error) => { |
| 682 | + console.error('选择图片失败:', error) | ||
| 580 | // Taro.showToast({ | 683 | // Taro.showToast({ |
| 581 | - // title: '选择图片失败', | 684 | + // title: '选择图片失败,请重试', |
| 582 | // icon: 'none' | 685 | // icon: 'none' |
| 583 | // }) | 686 | // }) |
| 584 | } | 687 | } |
| ... | @@ -595,15 +698,33 @@ const uploadBackgroundImage = (filePath) => { | ... | @@ -595,15 +698,33 @@ const uploadBackgroundImage = (filePath) => { |
| 595 | url: BASE_URL + '/admin/?m=srv&a=upload', | 698 | url: BASE_URL + '/admin/?m=srv&a=upload', |
| 596 | filePath, | 699 | filePath, |
| 597 | name: 'file', | 700 | name: 'file', |
| 598 | - success: (uploadRes) => { | 701 | + success: async (uploadRes) => { |
| 599 | - Taro.hideLoading() | ||
| 600 | const data = JSON.parse(uploadRes.data) | 702 | const data = JSON.parse(uploadRes.data) |
| 601 | if (data.code === 0 && data.data) { | 703 | if (data.code === 0 && data.data) { |
| 602 | const currentIndex = currentPosterIndex.value | 704 | const currentIndex = currentPosterIndex.value |
| 705 | + const currentPosterData = posterList.value[currentIndex] | ||
| 603 | 706 | ||
| 604 | // 为当前海报设置背景图 | 707 | // 为当前海报设置背景图 |
| 605 | backgroundImages.value[currentIndex] = data.data.src | 708 | backgroundImages.value[currentIndex] = data.data.src |
| 606 | 709 | ||
| 710 | + // 调用保存海报背景接口 | ||
| 711 | + try { | ||
| 712 | + const saveResult = await savePosterBackgroundAPI({ | ||
| 713 | + detail_id: currentPosterData.checkPointId, | ||
| 714 | + poster_background_url: data.data.src | ||
| 715 | + }) | ||
| 716 | + | ||
| 717 | + if (saveResult.code === 1) { | ||
| 718 | + console.log('海报背景保存成功:', saveResult.data) | ||
| 719 | + } else { | ||
| 720 | + console.warn('海报背景保存失败:', saveResult.msg) | ||
| 721 | + // 即使保存失败,也继续生成海报,不影响用户体验 | ||
| 722 | + } | ||
| 723 | + } catch (error) { | ||
| 724 | + console.error('保存海报背景异常:', error) | ||
| 725 | + // 即使保存失败,也继续生成海报,不影响用户体验 | ||
| 726 | + } | ||
| 727 | + | ||
| 607 | // 强制标记当前海报需要重新生成 | 728 | // 强制标记当前海报需要重新生成 |
| 608 | posterGeneratedFlags.value[currentIndex] = false | 729 | posterGeneratedFlags.value[currentIndex] = false |
| 609 | delete posterConfigHashes.value[currentIndex] | 730 | delete posterConfigHashes.value[currentIndex] |
| ... | @@ -614,12 +735,15 @@ const uploadBackgroundImage = (filePath) => { | ... | @@ -614,12 +735,15 @@ const uploadBackgroundImage = (filePath) => { |
| 614 | // 立即重新生成海报 | 735 | // 立即重新生成海报 |
| 615 | generateCurrentPoster() | 736 | generateCurrentPoster() |
| 616 | 737 | ||
| 738 | + Taro.hideLoading() | ||
| 617 | Taro.showToast({ title: '上传成功', icon: 'success' }) | 739 | Taro.showToast({ title: '上传成功', icon: 'success' }) |
| 618 | } else { | 740 | } else { |
| 741 | + Taro.hideLoading() | ||
| 619 | Taro.showToast({ title: data.msg || '上传失败', icon: 'none' }) | 742 | Taro.showToast({ title: data.msg || '上传失败', icon: 'none' }) |
| 620 | } | 743 | } |
| 621 | }, | 744 | }, |
| 622 | - fail: () => { | 745 | + fail: (error) => { |
| 746 | + console.error('上传文件失败:', error) | ||
| 623 | Taro.hideLoading() | 747 | Taro.hideLoading() |
| 624 | Taro.showToast({ title: '上传失败,请稍后重试', icon: 'none' }) | 748 | Taro.showToast({ title: '上传失败,请稍后重试', icon: 'none' }) |
| 625 | } | 749 | } |
| ... | @@ -755,22 +879,7 @@ const showNoActivityConfirm = () => { | ... | @@ -755,22 +879,7 @@ const showNoActivityConfirm = () => { |
| 755 | }) | 879 | }) |
| 756 | } | 880 | } |
| 757 | 881 | ||
| 758 | -/** | ||
| 759 | - * 页面初始化 | ||
| 760 | - */ | ||
| 761 | -onMounted(() => { | ||
| 762 | - // 获取页面参数 | ||
| 763 | - const instance = Taro.getCurrentInstance() | ||
| 764 | - if (instance.router && instance.router.params) { | ||
| 765 | - pageParams.value = instance.router.params | ||
| 766 | - } | ||
| 767 | 882 | ||
| 768 | - // 检查页面状态 | ||
| 769 | - if (pageState.value === 'no-activity') { | ||
| 770 | - // 没有活动信息,显示确认对话框 | ||
| 771 | - showNoActivityConfirm() | ||
| 772 | - } | ||
| 773 | -}) | ||
| 774 | </script> | 883 | </script> |
| 775 | 884 | ||
| 776 | <style scoped> | 885 | <style scoped> | ... | ... |
-
Please register or login to post a comment