hookehuyr

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

添加左右箭头切换不同海报的功能
增加活动信息展示区域,包含活动主题、打卡进度和截止日期
重构海报生成逻辑以支持多海报切换
......@@ -15,6 +15,7 @@ declare module 'vue' {
NutActionSheet: typeof import('@nutui/nutui-taro')['ActionSheet']
NutButton: typeof import('@nutui/nutui-taro')['Button']
NutDatePicker: typeof import('@nutui/nutui-taro')['DatePicker']
NutIcon: typeof import('@nutui/nutui-taro')['Icon']
NutImagePreview: typeof import('@nutui/nutui-taro')['ImagePreview']
NutInput: typeof import('@nutui/nutui-taro')['Input']
NutPicker: typeof import('@nutui/nutui-taro')['Picker']
......
<!--
* @Date: 2022-09-19 14:11:06
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-09-03 14:47:33
* @LastEditTime: 2025-09-03 15:54:59
* @FilePath: /lls_program/src/pages/ActivitiesCover/index.vue
* @Description: 活动海报页面 - 展示活动信息并处理定位授权
-->
......@@ -277,7 +277,7 @@ const onShareActivity = () => {
const onSharePoster = () => {
console.log('分享海报')
Taro.navigateTo({
url: '/pages/CheckinList/index',
url: '/pages/PosterCheckin/index',
})
}
......
<template>
<view class="poster-checkin-page bg-gray-50 h-screen flex flex-col">
<!-- 活动信息区域 -->
<view class="bg-white mx-4 mt-4 mb-2 rounded-lg shadow-sm p-4">
<!-- 活动主题 -->
<view class="text-lg font-bold text-gray-800 mb-2">
{{ activityInfo.title }}
</view>
<!-- 打卡进度 -->
<view class="flex items-center mb-2">
<view class="flex items-center mr-4">
<view
v-for="(point, index) in activityInfo.checkPoints"
:key="index"
class="w-6 h-6 rounded-full mr-2 flex items-center justify-center"
:class="point.completed ? 'bg-orange-400' : 'bg-gray-300'"
>
<text class="text-white text-xs">{{ index + 1 }}</text>
</view>
</view>
<text class="text-sm text-gray-600">{{ activityInfo.completedCount }}/{{ activityInfo.totalCount }}</text>
</view>
<!-- 活动截止日期 -->
<view class="text-sm text-gray-500">
活动截止日期:{{ activityInfo.endDate }}
</view>
</view>
<!-- 海报预览区域 -->
<view class="flex-1 mx-4 mt-4 mb-4 rounded-lg overflow-hidden">
<view class="h-full relative flex items-center justify-center">
<view v-if="posterPath" class="w-full h-full relative">
<view class="flex-1 mx-4 mb-2 bg-white rounded-lg shadow-sm relative" style="overflow: visible;">
<view class="h-full relative bg-gray-100 flex items-center justify-center">
<view v-if="currentPoster.path" class="w-full h-full relative">
<image
:src="posterPath"
:src="currentPoster.path"
mode="aspectFit"
class="w-full h-full"
/>
<!-- 点击预览提示 -->
<view @tap="previewPoster" class="absolute bottom-4 right-4 bg-black bg-opacity-50 text-white text-xs px-2 py-1 rounded">
<view @tap="previewPoster" class="absolute bottom-2 right-2 bg-black bg-opacity-50 text-white text-xs px-2 py-1 rounded">
点击预览
</view>
</view>
<!-- <view v-else class="text-center text-gray-500">
<view class="text-4xl mb-2">🎨</view>
<text class="text-sm">海报生成中...</text>
<view class="mt-2">
<view class="w-8 h-8 border-2 border-blue-500 border-t-transparent rounded-full animate-spin mx-auto"></view>
</view>
</view> -->
<!-- 左箭头按钮 -->
<view
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"
:class="currentPosterIndex > 0 ? 'bg-orange-400 text-white shadow-lg' : 'bg-gray-300 text-gray-500'"
style="left: -20rpx;"
@tap="previousPoster"
>
<Left size="16"></Left>
</view>
<!-- 右箭头按钮 -->
<view
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"
:class="currentPosterIndex < posterList.length - 1 ? 'bg-orange-400 text-white shadow-lg' : 'bg-gray-300 text-gray-500'"
style="right: -20rpx;"
@tap="nextPoster"
>
<Right size="16"></Right>
</view>
</view>
......@@ -86,7 +126,7 @@
<script setup>
import { ref, onMounted, computed, watch } from 'vue'
import Taro from '@tarojs/taro'
import { Left } from '@nutui/icons-vue-taro'
import { Left, Right } from '@nutui/icons-vue-taro'
import PosterBuilder from '@/components/PosterBuilder/index.vue'
import BASE_URL from '@/utils/config'
......@@ -94,11 +134,57 @@ import BASE_URL from '@/utils/config'
const posterPath = ref('') // 生成的海报路径
const backgroundImage = ref('') // 用户上传的背景图
const shouldGeneratePoster = ref(false) // 是否应该生成海报
const currentPosterIndex = ref(0) // 当前显示的海报索引
// 图片预览相关
const previewVisible = ref(false)
const previewImages = ref([])
// 活动信息数据
const activityInfo = ref({
title: '南京路乐龄时尚消费主题路线',
checkPoints: [
{ id: 1, name: '起点签到', completed: true },
{ id: 2, name: '商圈探索', completed: true },
{ id: 3, name: '文化体验', completed: true },
{ id: 4, name: '美食品鉴', completed: false },
{ id: 5, name: '终点打卡', completed: false }
],
completedCount: 3,
totalCount: 5,
endDate: '2025年9月7日'
})
// 海报列表数据
const posterList = ref([
{
id: 1,
title: '起点签到',
path: '',
checkPointId: 1,
backgroundImage: 'https://cdn.ipadbiz.cn/lls_prog/images/welcome_1.png'
},
{
id: 2,
title: '商圈探索',
path: '',
checkPointId: 2,
backgroundImage: 'https://cdn.ipadbiz.cn/lls_prog/images/welcome_1.png'
},
{
id: 3,
title: '文化体验',
path: '',
checkPointId: 3,
backgroundImage: 'https://cdn.ipadbiz.cn/lls_prog/images/welcome_1.png'
}
])
// 当前海报
const currentPoster = computed(() => {
return posterList.value[currentPosterIndex.value] || { path: '', title: '' }
})
// Mock数据
const mockData = ref({
user: {
......@@ -126,7 +212,8 @@ const defaultBackground = 'https://cdn.ipadbiz.cn/lls_prog/images/welcome_1.png'
// 海报配置
const posterConfig = computed(() => {
const bgImage = backgroundImage.value || defaultBackground
const currentPosterData = posterList.value[currentPosterIndex.value]
const bgImage = backgroundImage.value || currentPosterData?.backgroundImage || defaultBackground
return {
width: 750,
......@@ -246,8 +333,8 @@ const posterConfig = computed(() => {
*/
onMounted(() => {
Taro.setNavigationBarTitle({ title: '海报打卡' })
// 页面加载时生成海报
generatePoster()
// 页面加载时生成当前海报
generateCurrentPoster()
})
/**
......@@ -255,11 +342,18 @@ onMounted(() => {
*/
watch(backgroundImage, () => {
if (backgroundImage.value) {
generatePoster()
generateCurrentPoster()
}
})
/**
* 监听当前海报索引变化,切换海报
*/
watch(currentPosterIndex, () => {
generateCurrentPoster()
})
/**
* 返回上一页
*/
const goBack = () => {
......@@ -267,9 +361,9 @@ const goBack = () => {
}
/**
* 生成海报
* 生成当前海报
*/
const generatePoster = () => {
const generateCurrentPoster = () => {
posterPath.value = ''
shouldGeneratePoster.value = false
......@@ -280,6 +374,33 @@ const generatePoster = () => {
}
/**
* 切换海报
*/
const switchPoster = (index) => {
if (index >= 0 && index < posterList.value.length) {
currentPosterIndex.value = index
}
}
/**
* 切换到上一张海报
*/
const previousPoster = () => {
if (currentPosterIndex.value > 0) {
currentPosterIndex.value = currentPosterIndex.value - 1
}
}
/**
* 切换到下一张海报
*/
const nextPoster = () => {
if (currentPosterIndex.value < posterList.value.length - 1) {
currentPosterIndex.value = currentPosterIndex.value + 1
}
}
/**
* 选择背景图片
*/
const chooseBackgroundImage = () => {
......@@ -340,6 +461,10 @@ const uploadBackgroundImage = (filePath) => {
*/
const onPosterSuccess = (result) => {
posterPath.value = result.tempFilePath
// 更新当前海报的路径
if (posterList.value[currentPosterIndex.value]) {
posterList.value[currentPosterIndex.value].path = result.tempFilePath
}
shouldGeneratePoster.value = false
Taro.showToast({ title: '海报生成成功', icon: 'success' })
}
......@@ -357,10 +482,11 @@ const onPosterFail = (error) => {
* 预览海报
*/
const previewPoster = () => {
console.warn('预览海报', posterPath.value)
if (!posterPath.value) return
const currentPath = currentPoster.value.path
console.warn('预览海报', currentPath)
if (!currentPath) return
previewImages.value = [{ src: posterPath.value }]
previewImages.value = [{ src: currentPath }]
previewVisible.value = true
}
......@@ -375,13 +501,14 @@ const closePreview = () => {
* 保存海报到相册
*/
const savePoster = () => {
if (!posterPath.value) {
const currentPath = currentPoster.value.path
if (!currentPath) {
Taro.showToast({ title: '请等待海报生成完成', icon: 'none' })
return
}
Taro.saveImageToPhotosAlbum({
filePath: posterPath.value,
filePath: currentPath,
success: () => {
Taro.showToast({ title: '保存成功', icon: 'success' })
},
......