hookehuyr

feat(PosterCheckin): 添加海报切换功能并优化活动信息展示

添加左右箭头切换不同海报的功能
增加活动信息展示区域,包含活动主题、打卡进度和截止日期
重构海报生成逻辑以支持多海报切换
...@@ -15,6 +15,7 @@ declare module 'vue' { ...@@ -15,6 +15,7 @@ declare module 'vue' {
15 NutActionSheet: typeof import('@nutui/nutui-taro')['ActionSheet'] 15 NutActionSheet: typeof import('@nutui/nutui-taro')['ActionSheet']
16 NutButton: typeof import('@nutui/nutui-taro')['Button'] 16 NutButton: typeof import('@nutui/nutui-taro')['Button']
17 NutDatePicker: typeof import('@nutui/nutui-taro')['DatePicker'] 17 NutDatePicker: typeof import('@nutui/nutui-taro')['DatePicker']
18 + NutIcon: typeof import('@nutui/nutui-taro')['Icon']
18 NutImagePreview: typeof import('@nutui/nutui-taro')['ImagePreview'] 19 NutImagePreview: typeof import('@nutui/nutui-taro')['ImagePreview']
19 NutInput: typeof import('@nutui/nutui-taro')['Input'] 20 NutInput: typeof import('@nutui/nutui-taro')['Input']
20 NutPicker: typeof import('@nutui/nutui-taro')['Picker'] 21 NutPicker: typeof import('@nutui/nutui-taro')['Picker']
......
1 <!-- 1 <!--
2 * @Date: 2022-09-19 14:11:06 2 * @Date: 2022-09-19 14:11:06
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-09-03 14:47:33 4 + * @LastEditTime: 2025-09-03 15:54:59
5 * @FilePath: /lls_program/src/pages/ActivitiesCover/index.vue 5 * @FilePath: /lls_program/src/pages/ActivitiesCover/index.vue
6 * @Description: 活动海报页面 - 展示活动信息并处理定位授权 6 * @Description: 活动海报页面 - 展示活动信息并处理定位授权
7 --> 7 -->
...@@ -277,7 +277,7 @@ const onShareActivity = () => { ...@@ -277,7 +277,7 @@ const onShareActivity = () => {
277 const onSharePoster = () => { 277 const onSharePoster = () => {
278 console.log('分享海报') 278 console.log('分享海报')
279 Taro.navigateTo({ 279 Taro.navigateTo({
280 - url: '/pages/CheckinList/index', 280 + url: '/pages/PosterCheckin/index',
281 }) 281 })
282 } 282 }
283 283
......
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 + <!-- 活动信息区域 -->
4 + <view class="bg-white mx-4 mt-4 mb-2 rounded-lg shadow-sm p-4">
5 + <!-- 活动主题 -->
6 + <view class="text-lg font-bold text-gray-800 mb-2">
7 + {{ activityInfo.title }}
8 + </view>
9 +
10 + <!-- 打卡进度 -->
11 + <view class="flex items-center mb-2">
12 + <view class="flex items-center mr-4">
13 + <view
14 + v-for="(point, index) in activityInfo.checkPoints"
15 + :key="index"
16 + class="w-6 h-6 rounded-full mr-2 flex items-center justify-center"
17 + :class="point.completed ? 'bg-orange-400' : 'bg-gray-300'"
18 + >
19 + <text class="text-white text-xs">{{ index + 1 }}</text>
20 + </view>
21 + </view>
22 + <text class="text-sm text-gray-600">{{ activityInfo.completedCount }}/{{ activityInfo.totalCount }}</text>
23 + </view>
24 +
25 + <!-- 活动截止日期 -->
26 + <view class="text-sm text-gray-500">
27 + 活动截止日期:{{ activityInfo.endDate }}
28 + </view>
29 + </view>
30 +
3 <!-- 海报预览区域 --> 31 <!-- 海报预览区域 -->
4 - <view class="flex-1 mx-4 mt-4 mb-4 rounded-lg overflow-hidden"> 32 + <view class="flex-1 mx-4 mb-2 bg-white rounded-lg shadow-sm relative" style="overflow: visible;">
5 - <view class="h-full relative flex items-center justify-center"> 33 + <view class="h-full relative bg-gray-100 flex items-center justify-center">
6 - <view v-if="posterPath" class="w-full h-full relative"> 34 + <view v-if="currentPoster.path" class="w-full h-full relative">
7 <image 35 <image
8 - :src="posterPath" 36 + :src="currentPoster.path"
9 mode="aspectFit" 37 mode="aspectFit"
10 class="w-full h-full" 38 class="w-full h-full"
11 -
12 /> 39 />
13 <!-- 点击预览提示 --> 40 <!-- 点击预览提示 -->
14 - <view @tap="previewPoster" class="absolute bottom-4 right-4 bg-black bg-opacity-50 text-white text-xs px-2 py-1 rounded"> 41 + <view @tap="previewPoster" class="absolute bottom-2 right-2 bg-black bg-opacity-50 text-white text-xs px-2 py-1 rounded">
15 点击预览 42 点击预览
16 </view> 43 </view>
17 </view> 44 </view>
18 - <!-- <view v-else class="text-center text-gray-500">
19 - <view class="text-4xl mb-2">🎨</view>
20 - <text class="text-sm">海报生成中...</text>
21 - <view class="mt-2">
22 - <view class="w-8 h-8 border-2 border-blue-500 border-t-transparent rounded-full animate-spin mx-auto"></view>
23 </view> 45 </view>
24 - </view> --> 46 +
47 + <!-- 左箭头按钮 -->
48 + <view
49 + 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"
50 + :class="currentPosterIndex > 0 ? 'bg-orange-400 text-white shadow-lg' : 'bg-gray-300 text-gray-500'"
51 + style="left: -20rpx;"
52 + @tap="previousPoster"
53 + >
54 + <Left size="16"></Left>
55 + </view>
56 +
57 + <!-- 右箭头按钮 -->
58 + <view
59 + 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"
60 + :class="currentPosterIndex < posterList.length - 1 ? 'bg-orange-400 text-white shadow-lg' : 'bg-gray-300 text-gray-500'"
61 + style="right: -20rpx;"
62 + @tap="nextPoster"
63 + >
64 + <Right size="16"></Right>
25 </view> 65 </view>
26 </view> 66 </view>
27 67
...@@ -86,7 +126,7 @@ ...@@ -86,7 +126,7 @@
86 <script setup> 126 <script setup>
87 import { ref, onMounted, computed, watch } from 'vue' 127 import { ref, onMounted, computed, watch } from 'vue'
88 import Taro from '@tarojs/taro' 128 import Taro from '@tarojs/taro'
89 -import { Left } from '@nutui/icons-vue-taro' 129 +import { Left, Right } from '@nutui/icons-vue-taro'
90 import PosterBuilder from '@/components/PosterBuilder/index.vue' 130 import PosterBuilder from '@/components/PosterBuilder/index.vue'
91 import BASE_URL from '@/utils/config' 131 import BASE_URL from '@/utils/config'
92 132
...@@ -94,11 +134,57 @@ import BASE_URL from '@/utils/config' ...@@ -94,11 +134,57 @@ import BASE_URL from '@/utils/config'
94 const posterPath = ref('') // 生成的海报路径 134 const posterPath = ref('') // 生成的海报路径
95 const backgroundImage = ref('') // 用户上传的背景图 135 const backgroundImage = ref('') // 用户上传的背景图
96 const shouldGeneratePoster = ref(false) // 是否应该生成海报 136 const shouldGeneratePoster = ref(false) // 是否应该生成海报
137 +const currentPosterIndex = ref(0) // 当前显示的海报索引
97 138
98 // 图片预览相关 139 // 图片预览相关
99 const previewVisible = ref(false) 140 const previewVisible = ref(false)
100 const previewImages = ref([]) 141 const previewImages = ref([])
101 142
143 +// 活动信息数据
144 +const activityInfo = ref({
145 + title: '南京路乐龄时尚消费主题路线',
146 + checkPoints: [
147 + { id: 1, name: '起点签到', completed: true },
148 + { id: 2, name: '商圈探索', completed: true },
149 + { id: 3, name: '文化体验', completed: true },
150 + { id: 4, name: '美食品鉴', completed: false },
151 + { id: 5, name: '终点打卡', completed: false }
152 + ],
153 + completedCount: 3,
154 + totalCount: 5,
155 + endDate: '2025年9月7日'
156 +})
157 +
158 +// 海报列表数据
159 +const posterList = ref([
160 + {
161 + id: 1,
162 + title: '起点签到',
163 + path: '',
164 + checkPointId: 1,
165 + backgroundImage: 'https://cdn.ipadbiz.cn/lls_prog/images/welcome_1.png'
166 + },
167 + {
168 + id: 2,
169 + title: '商圈探索',
170 + path: '',
171 + checkPointId: 2,
172 + backgroundImage: 'https://cdn.ipadbiz.cn/lls_prog/images/welcome_1.png'
173 + },
174 + {
175 + id: 3,
176 + title: '文化体验',
177 + path: '',
178 + checkPointId: 3,
179 + backgroundImage: 'https://cdn.ipadbiz.cn/lls_prog/images/welcome_1.png'
180 + }
181 +])
182 +
183 +// 当前海报
184 +const currentPoster = computed(() => {
185 + return posterList.value[currentPosterIndex.value] || { path: '', title: '' }
186 +})
187 +
102 // Mock数据 188 // Mock数据
103 const mockData = ref({ 189 const mockData = ref({
104 user: { 190 user: {
...@@ -126,7 +212,8 @@ const defaultBackground = 'https://cdn.ipadbiz.cn/lls_prog/images/welcome_1.png' ...@@ -126,7 +212,8 @@ const defaultBackground = 'https://cdn.ipadbiz.cn/lls_prog/images/welcome_1.png'
126 212
127 // 海报配置 213 // 海报配置
128 const posterConfig = computed(() => { 214 const posterConfig = computed(() => {
129 - const bgImage = backgroundImage.value || defaultBackground 215 + const currentPosterData = posterList.value[currentPosterIndex.value]
216 + const bgImage = backgroundImage.value || currentPosterData?.backgroundImage || defaultBackground
130 217
131 return { 218 return {
132 width: 750, 219 width: 750,
...@@ -246,8 +333,8 @@ const posterConfig = computed(() => { ...@@ -246,8 +333,8 @@ const posterConfig = computed(() => {
246 */ 333 */
247 onMounted(() => { 334 onMounted(() => {
248 Taro.setNavigationBarTitle({ title: '海报打卡' }) 335 Taro.setNavigationBarTitle({ title: '海报打卡' })
249 - // 页面加载时生成海报 336 + // 页面加载时生成当前海报
250 - generatePoster() 337 + generateCurrentPoster()
251 }) 338 })
252 339
253 /** 340 /**
...@@ -255,11 +342,18 @@ onMounted(() => { ...@@ -255,11 +342,18 @@ onMounted(() => {
255 */ 342 */
256 watch(backgroundImage, () => { 343 watch(backgroundImage, () => {
257 if (backgroundImage.value) { 344 if (backgroundImage.value) {
258 - generatePoster() 345 + generateCurrentPoster()
259 } 346 }
260 }) 347 })
261 348
262 /** 349 /**
350 + * 监听当前海报索引变化,切换海报
351 + */
352 +watch(currentPosterIndex, () => {
353 + generateCurrentPoster()
354 +})
355 +
356 +/**
263 * 返回上一页 357 * 返回上一页
264 */ 358 */
265 const goBack = () => { 359 const goBack = () => {
...@@ -267,9 +361,9 @@ const goBack = () => { ...@@ -267,9 +361,9 @@ const goBack = () => {
267 } 361 }
268 362
269 /** 363 /**
270 - * 生成海报 364 + * 生成当前海报
271 */ 365 */
272 -const generatePoster = () => { 366 +const generateCurrentPoster = () => {
273 posterPath.value = '' 367 posterPath.value = ''
274 shouldGeneratePoster.value = false 368 shouldGeneratePoster.value = false
275 369
...@@ -280,6 +374,33 @@ const generatePoster = () => { ...@@ -280,6 +374,33 @@ const generatePoster = () => {
280 } 374 }
281 375
282 /** 376 /**
377 + * 切换海报
378 + */
379 +const switchPoster = (index) => {
380 + if (index >= 0 && index < posterList.value.length) {
381 + currentPosterIndex.value = index
382 + }
383 +}
384 +
385 +/**
386 + * 切换到上一张海报
387 + */
388 +const previousPoster = () => {
389 + if (currentPosterIndex.value > 0) {
390 + currentPosterIndex.value = currentPosterIndex.value - 1
391 + }
392 +}
393 +
394 +/**
395 + * 切换到下一张海报
396 + */
397 +const nextPoster = () => {
398 + if (currentPosterIndex.value < posterList.value.length - 1) {
399 + currentPosterIndex.value = currentPosterIndex.value + 1
400 + }
401 +}
402 +
403 +/**
283 * 选择背景图片 404 * 选择背景图片
284 */ 405 */
285 const chooseBackgroundImage = () => { 406 const chooseBackgroundImage = () => {
...@@ -340,6 +461,10 @@ const uploadBackgroundImage = (filePath) => { ...@@ -340,6 +461,10 @@ const uploadBackgroundImage = (filePath) => {
340 */ 461 */
341 const onPosterSuccess = (result) => { 462 const onPosterSuccess = (result) => {
342 posterPath.value = result.tempFilePath 463 posterPath.value = result.tempFilePath
464 + // 更新当前海报的路径
465 + if (posterList.value[currentPosterIndex.value]) {
466 + posterList.value[currentPosterIndex.value].path = result.tempFilePath
467 + }
343 shouldGeneratePoster.value = false 468 shouldGeneratePoster.value = false
344 Taro.showToast({ title: '海报生成成功', icon: 'success' }) 469 Taro.showToast({ title: '海报生成成功', icon: 'success' })
345 } 470 }
...@@ -357,10 +482,11 @@ const onPosterFail = (error) => { ...@@ -357,10 +482,11 @@ const onPosterFail = (error) => {
357 * 预览海报 482 * 预览海报
358 */ 483 */
359 const previewPoster = () => { 484 const previewPoster = () => {
360 - console.warn('预览海报', posterPath.value) 485 + const currentPath = currentPoster.value.path
361 - if (!posterPath.value) return 486 + console.warn('预览海报', currentPath)
487 + if (!currentPath) return
362 488
363 - previewImages.value = [{ src: posterPath.value }] 489 + previewImages.value = [{ src: currentPath }]
364 previewVisible.value = true 490 previewVisible.value = true
365 } 491 }
366 492
...@@ -375,13 +501,14 @@ const closePreview = () => { ...@@ -375,13 +501,14 @@ const closePreview = () => {
375 * 保存海报到相册 501 * 保存海报到相册
376 */ 502 */
377 const savePoster = () => { 503 const savePoster = () => {
378 - if (!posterPath.value) { 504 + const currentPath = currentPoster.value.path
505 + if (!currentPath) {
379 Taro.showToast({ title: '请等待海报生成完成', icon: 'none' }) 506 Taro.showToast({ title: '请等待海报生成完成', icon: 'none' })
380 return 507 return
381 } 508 }
382 509
383 Taro.saveImageToPhotosAlbum({ 510 Taro.saveImageToPhotosAlbum({
384 - filePath: posterPath.value, 511 + filePath: currentPath,
385 success: () => { 512 success: () => {
386 Taro.showToast({ title: '保存成功', icon: 'success' }) 513 Taro.showToast({ title: '保存成功', icon: 'success' })
387 }, 514 },
......