feat(recall): 添加用户信息缓存功能并优化活动历史逻辑
- 在IDQueryPage和CompleteInfoPage中添加用户信息缓存到localStorage - 修改timeline.vue和ActivityHistoryPage.vue从缓存获取用户信息 - 添加批量活动报名接口并实现收集星球币功能 - 优化活动历史日期显示逻辑
Showing
6 changed files
with
129 additions
and
25 deletions
| 1 | /* | 1 | /* |
| 2 | * @Date: 2025-12-24 12:26:27 | 2 | * @Date: 2025-12-24 12:26:27 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-12-24 12:37:23 | 4 | + * @LastEditTime: 2025-12-24 18:36:29 |
| 5 | * @FilePath: /mlaj/src/api/points.js | 5 | * @FilePath: /mlaj/src/api/points.js |
| 6 | * @Description: 积分相关接口 | 6 | * @Description: 积分相关接口 |
| 7 | */ | 7 | */ |
| ... | @@ -9,6 +9,7 @@ import { fn, fetch } from './fn'; | ... | @@ -9,6 +9,7 @@ import { fn, fetch } from './fn'; |
| 9 | 9 | ||
| 10 | const Api = { | 10 | const Api = { |
| 11 | POINTS_LIST: '/srv/?a=points&t=list', | 11 | POINTS_LIST: '/srv/?a=points&t=list', |
| 12 | + OLD_ACTIVITY_BATCH_ACTIVITY_REGISTRATION: '/srv/?a=points&t=old_activity_batch_activity_registration', | ||
| 12 | } | 13 | } |
| 13 | 14 | ||
| 14 | /** | 15 | /** |
| ... | @@ -25,3 +26,21 @@ const Api = { | ... | @@ -25,3 +26,21 @@ const Api = { |
| 25 | * } | 26 | * } |
| 26 | */ | 27 | */ |
| 27 | export const getPointsListAPI = (params) => fn(fetch.get(Api.POINTS_LIST, params)); | 28 | export const getPointsListAPI = (params) => fn(fetch.get(Api.POINTS_LIST, params)); |
| 29 | + | ||
| 30 | + | ||
| 31 | +/** | ||
| 32 | + * @description 召回老客户-批量活动报名 | ||
| 33 | + * @param {*} params { | ||
| 34 | + * name: 姓名 | ||
| 35 | + * mobile: 手机号 | ||
| 36 | + * idcard: 身份证号 | ||
| 37 | + * activity_ids: 活动id列表, 逗号分隔 | ||
| 38 | + * } | ||
| 39 | + * @returns data: { | ||
| 40 | + * payment_qty: 已报名活动数量 | ||
| 41 | + * volunteer_qty: 已报名志愿者数量 | ||
| 42 | + * record_date: 报名记录日期 | ||
| 43 | + * last_activity_date: 最后活动日期 | ||
| 44 | + * } | ||
| 45 | + */ | ||
| 46 | +export const oldActivityBatchActivityRegistrationAPI = (params) => fn(fetch.post(Api.OLD_ACTIVITY_BATCH_ACTIVITY_REGISTRATION, params)); | ... | ... |
| ... | @@ -4,7 +4,7 @@ | ... | @@ -4,7 +4,7 @@ |
| 4 | <div class="w-full relative h-[180px] overflow-hidden"> | 4 | <div class="w-full relative h-[180px] overflow-hidden"> |
| 5 | <img :src="historyBg" class="w-full h-full object-cover" alt="History Banner" /> | 5 | <img :src="historyBg" class="w-full h-full object-cover" alt="History Banner" /> |
| 6 | <div class="absolute inset-0 flex flex-col justify-center items-center text-center px-4"> | 6 | <div class="absolute inset-0 flex flex-col justify-center items-center text-center px-4"> |
| 7 | - <h1 class="text-[#FFDD01] text-3xl font-bold mb-2 tracking-wider drop-shadow-md mt-3">2020-2025</h1> | 7 | + <h1 class="text-[#FFDD01] text-3xl font-bold mb-2 tracking-wider drop-shadow-md mt-3">{{ recordDate }}</h1> |
| 8 | <h2 class="text-white text-2xl font-bold mb-4 tracking-wider drop-shadow-md">您的活动历史</h2> | 8 | <h2 class="text-white text-2xl font-bold mb-4 tracking-wider drop-shadow-md">您的活动历史</h2> |
| 9 | 9 | ||
| 10 | <div class="w-full max-w-md rounded-lg px-4 py-2" | 10 | <div class="w-full max-w-md rounded-lg px-4 py-2" |
| ... | @@ -63,6 +63,7 @@ | ... | @@ -63,6 +63,7 @@ |
| 63 | 63 | ||
| 64 | <!-- Fixed Bottom Buttons --> | 64 | <!-- Fixed Bottom Buttons --> |
| 65 | <div | 65 | <div |
| 66 | + v-if="!userInfo.has_activity_registration" | ||
| 66 | class="fixed bottom-0 left-0 right-0 bg-white/60 backdrop-blur-md p-4 pb-8 z-30 shadow-[0_-2px_10px_rgba(0,0,0,0.05)]"> | 67 | class="fixed bottom-0 left-0 right-0 bg-white/60 backdrop-blur-md p-4 pb-8 z-30 shadow-[0_-2px_10px_rgba(0,0,0,0.05)]"> |
| 67 | <van-button block color="#0052D9" class="!rounded-lg !mb-3 !h-[44px] !text-base !font-bold" | 68 | <van-button block color="#0052D9" class="!rounded-lg !mb-3 !h-[44px] !text-base !font-bold" |
| 68 | @click="handleCollectCoins"> | 69 | @click="handleCollectCoins"> |
| ... | @@ -106,6 +107,7 @@ import { useTitle } from '@vueuse/core' | ... | @@ -106,6 +107,7 @@ import { useTitle } from '@vueuse/core' |
| 106 | import { showToast } from 'vant' | 107 | import { showToast } from 'vant' |
| 107 | 108 | ||
| 108 | import { userInfoAPI, searchOldActivityAPI } from '@/api/recall_users' | 109 | import { userInfoAPI, searchOldActivityAPI } from '@/api/recall_users' |
| 110 | +import { oldActivityBatchActivityRegistrationAPI } from '@/api/points' | ||
| 109 | 111 | ||
| 110 | const historyBg = 'https://cdn.ipadbiz.cn/mlaj/recall/img/history_bg@2x.png' | 112 | const historyBg = 'https://cdn.ipadbiz.cn/mlaj/recall/img/history_bg@2x.png' |
| 111 | 113 | ||
| ... | @@ -125,9 +127,30 @@ const handleGeneratePoster = (item) => { | ... | @@ -125,9 +127,30 @@ const handleGeneratePoster = (item) => { |
| 125 | // showToast('生成海报: ' + item.title) | 127 | // showToast('生成海报: ' + item.title) |
| 126 | } | 128 | } |
| 127 | 129 | ||
| 128 | -const handleCollectCoins = () => { | 130 | +const handleCollectCoins = async () => { |
| 129 | - // showToast('收集星球币成功') | 131 | + // 从缓存获取用户信息 |
| 130 | - router.push({ path: '/recall/points' }) | 132 | + const cachedUserInfo = localStorage.getItem('cached_user_info') |
| 133 | + if (cachedUserInfo) { | ||
| 134 | + const userInfo = JSON.parse(cachedUserInfo) | ||
| 135 | + // 调用积分接口 | ||
| 136 | + const res = await oldActivityBatchActivityRegistrationAPI({ | ||
| 137 | + name: userInfo.name || '', | ||
| 138 | + mobile: userInfo.phone || '', | ||
| 139 | + idcard: userInfo.idCard || '', | ||
| 140 | + old_activity_data: campaign_info.value | ||
| 141 | + }) | ||
| 142 | + if (res.code) { | ||
| 143 | + showToast({ | ||
| 144 | + message: '收集星球币成功', | ||
| 145 | + icon: 'success' | ||
| 146 | + }) | ||
| 147 | + router.push({ path: '/recall/points' }) | ||
| 148 | + } else { | ||
| 149 | + showToast({ | ||
| 150 | + message: '收集星球币失败', | ||
| 151 | + icon: 'error' | ||
| 152 | + }) | ||
| 153 | + } | ||
| 131 | } | 154 | } |
| 132 | 155 | ||
| 133 | const handleSubmitMissing = () => { | 156 | const handleSubmitMissing = () => { |
| ... | @@ -147,16 +170,47 @@ const handleSubmitMissing = () => { | ... | @@ -147,16 +170,47 @@ const handleSubmitMissing = () => { |
| 147 | missingInfo.value = '' | 170 | missingInfo.value = '' |
| 148 | } | 171 | } |
| 149 | 172 | ||
| 173 | +const userInfo = ref({}); | ||
| 174 | +const campaign_info = ref([]); | ||
| 175 | + | ||
| 176 | +// 记录日期 | ||
| 177 | +const recordDate = ref('') | ||
| 178 | + | ||
| 150 | onMounted(async () => { | 179 | onMounted(async () => { |
| 151 | - // 获取用户信息 | 180 | + // 从缓存获取用户信息 |
| 152 | - const userInfoRes = await userInfoAPI() | 181 | + const cachedUserInfo = localStorage.getItem('cached_user_info') |
| 153 | - if (userInfoRes.code) { | 182 | + if (cachedUserInfo) { |
| 154 | - // 通过用户信息获取活动历史信息 | 183 | + const userInfo = JSON.parse(cachedUserInfo) |
| 155 | - const activityRes = await searchOldActivityAPI({ | 184 | + if (userInfo.name && userInfo.phone && userInfo.idCard) { |
| 156 | - name: userInfoRes.data?.user_name || '', | 185 | + // 通过用户信息获取活动历史信息 |
| 157 | - mobile: userInfoRes.data?.mobile || '', | 186 | + const activityRes = await searchOldActivityAPI({ |
| 158 | - idcard: userInfoRes.data?.idcard || '' | 187 | + name: userInfo.name, |
| 159 | - }) | 188 | + mobile: userInfo.phone, |
| 189 | + idcard: userInfo.idCard | ||
| 190 | + }) | ||
| 191 | + if (activityRes.code) { | ||
| 192 | + // 获取历史列表数据 | ||
| 193 | + const campaign_info = activityRes.data?.campaign_info || [] | ||
| 194 | + if (campaign_info.length) { | ||
| 195 | + campaign_info.value = campaign_info; | ||
| 196 | + activities.value = campaign_info.map(item => ({ | ||
| 197 | + id: item.campaign_id || 0, | ||
| 198 | + stu_uid: item.stu_uid || '', | ||
| 199 | + title: item.campaign_name || '', | ||
| 200 | + price: item.fee_stu || 0, | ||
| 201 | + count: item.stu_cnt || 0, | ||
| 202 | + date: item.create_time?.substring(0, 10) || '', | ||
| 203 | + total: item.fee_stu * item.stu_cnt || 0 | ||
| 204 | + })) | ||
| 205 | + // 遍历日期把列表date字段从小到大排序, 获取到其中日期段 比如 2020-2025 | ||
| 206 | + const sortedDates = activities.value.map(item => item.date).sort((a, b) => new Date(a) - new Date(b)) | ||
| 207 | + recordDate.value = sortedDates[0]?.substring(0, 4) + '-' + sortedDates[sortedDates.length - 1]?.substring(0, 4) || '' | ||
| 208 | + } | ||
| 209 | + } | ||
| 210 | + } | ||
| 211 | + } else { | ||
| 212 | + // 如果是收集完成没有缓存字段的情况, 直接查接口 | ||
| 213 | + const activityRes = await searchOldActivityAPI() | ||
| 160 | if (activityRes.code) { | 214 | if (activityRes.code) { |
| 161 | // 获取历史列表数据 | 215 | // 获取历史列表数据 |
| 162 | const campaign_info = activityRes.data?.campaign_info || [] | 216 | const campaign_info = activityRes.data?.campaign_info || [] |
| ... | @@ -173,6 +227,12 @@ onMounted(async () => { | ... | @@ -173,6 +227,12 @@ onMounted(async () => { |
| 173 | } | 227 | } |
| 174 | } | 228 | } |
| 175 | } | 229 | } |
| 230 | + | ||
| 231 | + // 获取用户信息 | ||
| 232 | + const userInfoRes = await userInfoAPI() | ||
| 233 | + if (userInfoRes.code) { | ||
| 234 | + userInfo.value = userInfoRes.data.user || {}; | ||
| 235 | + } | ||
| 176 | }) | 236 | }) |
| 177 | </script> | 237 | </script> |
| 178 | 238 | ... | ... |
| ... | @@ -135,6 +135,13 @@ const handleConfirm = async () => { | ... | @@ -135,6 +135,13 @@ const handleConfirm = async () => { |
| 135 | // 获取历史列表数据 | 135 | // 获取历史列表数据 |
| 136 | const campaign_info = activityRes.data?.campaign_info || [] | 136 | const campaign_info = activityRes.data?.campaign_info || [] |
| 137 | if (campaign_info.length) { | 137 | if (campaign_info.length) { |
| 138 | + // 有历史数据, 保存用户信息到缓存 | ||
| 139 | + localStorage.setItem('cached_user_info', JSON.stringify({ | ||
| 140 | + name: form.name, | ||
| 141 | + phone: form.phone, | ||
| 142 | + idCard: form.idCard | ||
| 143 | + })); | ||
| 144 | + | ||
| 138 | // 有历史数据, 跳转到timeline | 145 | // 有历史数据, 跳转到timeline |
| 139 | router.push('/recall/timeline') | 146 | router.push('/recall/timeline') |
| 140 | return | 147 | return | ... | ... |
| ... | @@ -160,6 +160,13 @@ const handleConfirm = async () => { | ... | @@ -160,6 +160,13 @@ const handleConfirm = async () => { |
| 160 | // 如果能查到数据, 则跳转到timeline, 否则弹出提示 | 160 | // 如果能查到数据, 则跳转到timeline, 否则弹出提示 |
| 161 | const flag = campaign_info.length > 0; | 161 | const flag = campaign_info.length > 0; |
| 162 | if (flag) { | 162 | if (flag) { |
| 163 | + // 有历史数据, 保存用户信息到缓存 | ||
| 164 | + localStorage.setItem('cached_user_info', JSON.stringify({ | ||
| 165 | + name: name.value, | ||
| 166 | + phone: phone.value, | ||
| 167 | + idCard: idCard.value | ||
| 168 | + })); | ||
| 169 | + | ||
| 163 | router.push('/recall/timeline') | 170 | router.push('/recall/timeline') |
| 164 | return | 171 | return |
| 165 | } | 172 | } | ... | ... |
| ... | @@ -88,10 +88,11 @@ import { ref, computed, onMounted } from 'vue' | ... | @@ -88,10 +88,11 @@ import { ref, computed, onMounted } from 'vue' |
| 88 | import { useRouter, useRoute } from 'vue-router' | 88 | import { useRouter, useRoute } from 'vue-router' |
| 89 | import { showToast } from 'vant' | 89 | import { showToast } from 'vant' |
| 90 | import { useTitle } from '@vueuse/core' | 90 | import { useTitle } from '@vueuse/core' |
| 91 | +// 导入接口 | ||
| 91 | import { smsAPI } from '@/api/common' | 92 | import { smsAPI } from '@/api/common' |
| 92 | import { loginAPI, userInfoAPI } from '@/api/recall_users' | 93 | import { loginAPI, userInfoAPI } from '@/api/recall_users' |
| 93 | import { useTracking } from '@/composables/useTracking' | 94 | import { useTracking } from '@/composables/useTracking' |
| 94 | - | 95 | +// 导入图片 |
| 95 | const titleImg = 'https://cdn.ipadbiz.cn/mlaj/recall/img/title01@2x.png' | 96 | const titleImg = 'https://cdn.ipadbiz.cn/mlaj/recall/img/title01@2x.png' |
| 96 | const bgImg = 'https://cdn.ipadbiz.cn/mlaj/recall/img/bg01@2x.png' | 97 | const bgImg = 'https://cdn.ipadbiz.cn/mlaj/recall/img/bg01@2x.png' |
| 97 | 98 | ... | ... |
| ... | @@ -12,7 +12,7 @@ | ... | @@ -12,7 +12,7 @@ |
| 12 | 12 | ||
| 13 | <!-- Title Section --> | 13 | <!-- Title Section --> |
| 14 | <div class="flex flex-col items-center mb-8 animate-fade-in-down"> | 14 | <div class="flex flex-col items-center mb-8 animate-fade-in-down"> |
| 15 | - <h2 class="text-[#FFDD01] text-2xl font-bold mb-4 tracking-wider">@{{ userInfo.value.name }}</h2> | 15 | + <h2 class="text-[#FFDD01] text-2xl font-bold mb-4 tracking-wider">@{{ userInfo.name }}</h2> |
| 16 | <img :src="title03" class="w-64 object-contain" alt="Welcome Back" /> | 16 | <img :src="title03" class="w-64 object-contain" alt="Welcome Back" /> |
| 17 | </div> | 17 | </div> |
| 18 | 18 | ||
| ... | @@ -46,7 +46,7 @@ | ... | @@ -46,7 +46,7 @@ |
| 46 | <!-- Title Section --> | 46 | <!-- Title Section --> |
| 47 | <div class="flex flex-col items-center mb-10 animate-fade-in-down"> | 47 | <div class="flex flex-col items-center mb-10 animate-fade-in-down"> |
| 48 | <img :src="title04" class="w-64 object-contain mb-4" alt="My Footprints" /> | 48 | <img :src="title04" class="w-64 object-contain mb-4" alt="My Footprints" /> |
| 49 | - <h2 class="text-[#FFDD01] text-2xl font-bold tracking-wider">@{{ userInfo.value.name }}</h2> | 49 | + <h2 class="text-[#FFDD01] text-2xl font-bold tracking-wider">@{{ userInfo.name }}</h2> |
| 50 | </div> | 50 | </div> |
| 51 | 51 | ||
| 52 | <!-- Card Section --> | 52 | <!-- Card Section --> |
| ... | @@ -164,16 +164,17 @@ const handleViewHistory = () => { | ... | @@ -164,16 +164,17 @@ const handleViewHistory = () => { |
| 164 | const userInfo = ref({}) | 164 | const userInfo = ref({}) |
| 165 | 165 | ||
| 166 | onMounted(async () => { | 166 | onMounted(async () => { |
| 167 | - // 获取用户信息 | 167 | + // 从缓存获取用户信息 |
| 168 | - const res = await userInfoAPI() | 168 | + const cachedUserInfo = localStorage.getItem('cached_user_info') |
| 169 | - if (res.code) { | 169 | + if (cachedUserInfo) { |
| 170 | - userInfo.value = res.data || {}; | 170 | + userInfo.value = JSON.parse(cachedUserInfo) |
| 171 | - if (userInfo.value.name && userInfo.value.mobile && userInfo.value.idcard) { | 171 | + |
| 172 | - // 检查是否有历史数据 | 172 | + // 检查是否有历史数据 |
| 173 | + if (userInfo.value.name && userInfo.value.phone && userInfo.value.idCard) { | ||
| 173 | const res = await searchOldActivityAPI({ | 174 | const res = await searchOldActivityAPI({ |
| 174 | name: userInfo.value.name, | 175 | name: userInfo.value.name, |
| 175 | - mobile: userInfo.value.mobile, | 176 | + mobile: userInfo.value.phone, |
| 176 | - idcard: userInfo.value.idcard | 177 | + idcard: userInfo.value.idCard |
| 177 | }) | 178 | }) |
| 178 | if (res.code) { | 179 | if (res.code) { |
| 179 | activityCount.value = res.data?.payment_qty || 0 | 180 | activityCount.value = res.data?.payment_qty || 0 |
| ... | @@ -182,6 +183,15 @@ onMounted(async () => { | ... | @@ -182,6 +183,15 @@ onMounted(async () => { |
| 182 | lastActivityDate.value = res.data?.last_activity_date || '' | 183 | lastActivityDate.value = res.data?.last_activity_date || '' |
| 183 | } | 184 | } |
| 184 | } | 185 | } |
| 186 | + } else { | ||
| 187 | + // 如果是收集完成没有缓存字段的情况, 直接查接口 | ||
| 188 | + const res = await searchOldActivityAPI() | ||
| 189 | + if (res.code) { | ||
| 190 | + activityCount.value = res.data?.payment_qty || 0 | ||
| 191 | + volunteerCount.value = res.data?.volunteer_qty || 0 | ||
| 192 | + recordDate.value = res.data?.record_date || '' | ||
| 193 | + lastActivityDate.value = res.data?.last_activity_date || '' | ||
| 194 | + } | ||
| 185 | } | 195 | } |
| 186 | }) | 196 | }) |
| 187 | 197 | ... | ... |
-
Please register or login to post a comment