hookehuyr

feat(recall): 重构回忆功能路由和页面逻辑

- 删除不再使用的Index.vue组件
- 更新各页面路由跳转逻辑,添加条件判断
- 在登录页面添加短信验证码API调用
- 为ID查询和完整信息页面添加默认测试数据
- 调整路由配置,移除旧路由
1 /* 1 /*
2 * @Date: 2025-03-20 20:36:36 2 * @Date: 2025-03-20 20:36:36
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-12-23 15:51:28 4 + * @LastEditTime: 2025-12-23 17:34:57
5 * @FilePath: /mlaj/src/router/routes.js 5 * @FilePath: /mlaj/src/router/routes.js
6 * @Description: 路由地址映射配置 6 * @Description: 路由地址映射配置
7 */ 7 */
...@@ -97,12 +97,6 @@ export const routes = [ ...@@ -97,12 +97,6 @@ export const routes = [
97 meta: { title: '我的活动' }, 97 meta: { title: '我的活动' },
98 }, 98 },
99 { 99 {
100 - path: '/recall',
101 - name: 'Recall',
102 - component: () => import('../views/recall/Index.vue'),
103 - meta: { title: '历史足迹' },
104 - },
105 - {
106 path: '/recall/login', 100 path: '/recall/login',
107 name: 'RecallLogin', 101 name: 'RecallLogin',
108 component: () => import('../views/recall/login.vue'), 102 component: () => import('../views/recall/login.vue'),
......
...@@ -89,7 +89,7 @@ useTitle(route.meta.title || '完善信息') ...@@ -89,7 +89,7 @@ useTitle(route.meta.title || '完善信息')
89 const form = reactive({ 89 const form = reactive({
90 name: '张开心', // Mock initial data as per design 90 name: '张开心', // Mock initial data as per design
91 phone: '15658666022', // Mock initial data as per design 91 phone: '15658666022', // Mock initial data as per design
92 - idCard: '' 92 + idCard: '44030619900101001X'
93 }) 93 })
94 94
95 // Actions 95 // Actions
...@@ -115,8 +115,15 @@ const handleConfirm = () => { ...@@ -115,8 +115,15 @@ const handleConfirm = () => {
115 } 115 }
116 116
117 // Validation passed 117 // Validation passed
118 - showToast('提交成功') 118 + // showToast('提交成功')
119 - // Navigate to next step or handle submission logic here 119 + // TODO: 完善信息之后, 如果能查看历史数据, 则需要跳转到timeline
120 + const flag = false;
121 + if (flag) {
122 + router.push('/recall/timeline')
123 + return
124 + }
125 + // 如果不能查看历史数据, 则需要跳转到id-query, 继续查数据
126 + router.push('/recall/id-query')
120 } 127 }
121 </script> 128 </script>
122 129
......
...@@ -79,7 +79,7 @@ const route = useRoute() ...@@ -79,7 +79,7 @@ const route = useRoute()
79 const router = useRouter() 79 const router = useRouter()
80 useTitle('身份证号查询') 80 useTitle('身份证号查询')
81 81
82 -const idCard = ref('') 82 +const idCard = ref('44030619900101001X')
83 83
84 // ID Card Validation 84 // ID Card Validation
85 const validateIdCard = (id) => { 85 const validateIdCard = (id) => {
...@@ -100,8 +100,14 @@ const handleConfirm = () => { ...@@ -100,8 +100,14 @@ const handleConfirm = () => {
100 } 100 }
101 101
102 // TODO: Submit logic 102 // TODO: Submit logic
103 - showToast('验证通过') 103 + // showToast('验证通过')
104 - // router.push(...) 104 + // TODO: 如果能查到数据, 则跳转到timeline, 否则弹出提示
105 + const flag = true;
106 + if (flag) {
107 + router.push('/recall/timeline')
108 + return
109 + }
110 + showToast('时光机没有找到您的历史活动信息')
105 } 111 }
106 </script> 112 </script>
107 113
......
1 -<!--
2 - * @Date: 2025-12-19 15:40:34
3 - * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-12-19 16:24:51
5 - * @FilePath: /mlaj/src/views/recall/Index.vue
6 - * @Description: 文件描述
7 --->
8 -<template>
9 - <div class="h-screen bg-[#F0FBF9] overflow-hidden relative">
10 - <!-- Swiper Container -->
11 - <swiper
12 - :direction="'vertical'"
13 - :modules="[Mousewheel]"
14 - :mousewheel="true"
15 - class="h-full w-full"
16 - @slideChange="onSlideChange"
17 - :speed="800"
18 - @swiper="onSwiper"
19 - >
20 - <!-- 第一屏 -->
21 - <swiper-slide>
22 - <div class="flex flex-col items-center pt-20 pb-10 px-6 relative h-full w-full">
23 - <!-- 头像 -->
24 - <div class="relative mb-6 opacity-0 translate-y-4 transition-all duration-700 delay-100" :class="{ 'opacity-100 translate-y-0': activeIndex === 0 }">
25 - <img
26 - :src="userAvatar || defaultAvatar"
27 - class="w-24 h-24 rounded-full border-4 border-white shadow-lg object-cover"
28 - alt="User Avatar"
29 - />
30 - </div>
31 -
32 - <!-- 欢迎语 -->
33 - <h1 class="text-2xl font-bold text-[#1A3B34] mb-2 text-center opacity-0 translate-y-4 transition-all duration-700 delay-200" :class="{ 'opacity-100 translate-y-0': activeIndex === 0 }">{{ userName }}</h1>
34 - <h2 class="text-xl font-bold text-[#1A3B34] mb-4 text-center opacity-0 translate-y-4 transition-all duration-700 delay-300" :class="{ 'opacity-100 translate-y-0': activeIndex === 0 }">欢迎回归美乐爱觉宇宙</h2>
35 - <p class="text-slate-500 text-sm mb-12 text-center opacity-0 translate-y-4 transition-all duration-700 delay-400" :class="{ 'opacity-100 translate-y-0': activeIndex === 0 }">您与Behalo的故事从这里开始</p>
36 -
37 - <!-- 时间卡片 -->
38 - <div class="bg-white rounded-3xl p-8 shadow-xl w-full max-w-sm text-center mb-auto relative z-10 opacity-0 scale-95 transition-all duration-700 delay-500" :class="{ 'opacity-100 scale-100': activeIndex === 0 }">
39 - <p class="text-slate-500 mb-4">您加入Behalo的时间</p>
40 - <div class="text-3xl font-bold text-[#1A3B34] mb-4 tracking-wide">{{ joinDateFormatted }}</div>
41 - <div class="text-slate-500">
42 - 已有 <span class="text-[#1A3B34] font-bold text-lg">{{ durationString }}</span>
43 - </div>
44 - </div>
45 -
46 - <!-- 底部引导 -->
47 - <div class="flex flex-col items-center mt-12 animate-bounce cursor-pointer opacity-0 transition-opacity duration-1000 delay-1000" :class="{ 'opacity-100': activeIndex === 0 }" @click="slideNext">
48 - <ChevronDoubleDownIcon class="w-6 h-6 text-[#4ADE80] mb-2" />
49 - <span class="text-[#4ADE80] font-medium text-sm">向上滑动,探索更多历程</span>
50 - </div>
51 - </div>
52 - </swiper-slide>
53 -
54 - <!-- 第二屏 -->
55 - <swiper-slide>
56 - <div class="flex flex-col items-center pt-10 pb-10 px-6 relative h-full w-full overflow-y-auto overflow-x-hidden">
57 - <h2 class="text-2xl font-bold text-[#1A3B34] mb-2 text-center opacity-0 translate-y-4 transition-all duration-700 delay-100" :class="{ 'opacity-100 translate-y-0': activeIndex === 1 }">您的Behalo足迹</h2>
58 - <p class="text-slate-500 text-sm mb-8 text-center opacity-0 translate-y-4 transition-all duration-700 delay-200" :class="{ 'opacity-100 translate-y-0': activeIndex === 1 }">2016~2025,您在Behalo留下的印记</p>
59 -
60 - <!-- 活动参与卡片 -->
61 - <div class="bg-white rounded-3xl p-6 shadow-md w-full max-w-sm mb-4 opacity-0 translate-y-4 transition-all duration-700 delay-300" :class="{ 'opacity-100 translate-y-0': activeIndex === 1 }">
62 - <div class="flex items-center mb-4">
63 - <div class="w-10 h-10 rounded-full bg-[#E0F2F1] flex items-center justify-center mr-3 text-[#26A69A]">
64 - <CalendarIcon class="w-5 h-5" />
65 - </div>
66 - <span class="font-bold text-[#1A3B34]">参与宇宙活动</span>
67 - </div>
68 - <div class="mb-2">
69 - <span class="text-5xl font-bold text-[#1A3B34]">{{ activityCount }}</span>
70 - <span class="text-slate-500 ml-1">次</span>
71 - </div>
72 - <p class="text-slate-500 text-xs">您积极参与各类教育活动,累计参与{{ activityCount }}次</p>
73 - </div>
74 -
75 - <!-- 义工服务卡片 -->
76 - <div class="bg-white rounded-3xl p-6 shadow-md w-full max-w-sm mb-8 opacity-0 translate-y-4 transition-all duration-700 delay-500" :class="{ 'opacity-100 translate-y-0': activeIndex === 1 }">
77 - <div class="flex items-center mb-4">
78 - <div class="w-10 h-10 rounded-full bg-[#E8F5E9] flex items-center justify-center mr-3 text-[#4CAF50]">
79 - <HeartIcon class="w-5 h-5" />
80 - </div>
81 - <span class="font-bold text-[#1A3B34]">义工服务活动</span>
82 - </div>
83 - <div class="mb-2">
84 - <span class="text-5xl font-bold text-[#1A3B34]">{{ volunteerCount }}</span>
85 - <span class="text-slate-500 ml-1">次</span>
86 - </div>
87 - <p class="text-slate-500 text-xs">您热心公益,作为义工服务了{{ volunteerCount }}次活动,贡献时长超过{{ volunteerHours }}小时</p>
88 - </div>
89 -
90 - <!-- 荣誉徽章 -->
91 - <!--<div class="w-full max-w-sm opacity-0 translate-y-4 transition-all duration-700 delay-700" :class="{ 'opacity-100 translate-y-0': activeIndex === 1 }">
92 - <h3 class="font-bold text-[#1A3B34] mb-4">获得的荣誉徽章</h3>
93 - <div class="flex justify-between px-2">
94 - <!~~ 徽章1 ~~>
95 - <div class="w-16 h-16 rounded-full bg-[#FFF9C4] flex items-center justify-center shadow-sm">
96 - <TrophyIcon class="w-8 h-8 text-[#FBC02D]" />
97 - </div>
98 - <!~~ 徽章2 ~~>
99 - <div class="w-16 h-16 rounded-full bg-[#F3E5F5] flex items-center justify-center shadow-sm">
100 - <StarIcon class="w-8 h-8 text-[#AB47BC]" />
101 - </div>
102 - <!~~ 徽章3 ~~>
103 - <div class="w-16 h-16 rounded-full bg-[#E3F2FD] flex items-center justify-center shadow-sm">
104 - <CheckBadgeIcon class="w-8 h-8 text-[#42A5F5]" />
105 - </div>
106 - <!~~ 徽章4 (锁定) ~~>
107 - <div class="w-16 h-16 rounded-full bg-[#F5F5F5] flex items-center justify-center shadow-sm">
108 - <LockClosedIcon class="w-8 h-8 text-[#BDBDBD]" />
109 - </div>
110 - </div>
111 - </div>-->
112 - </div>
113 - </swiper-slide>
114 - </swiper>
115 - </div>
116 -</template>
117 -
118 -<script setup>
119 -import { computed, onMounted, ref } from 'vue';
120 -import { useAuth } from '@/contexts/auth';
121 -import { ChevronDoubleDownIcon, CalendarIcon, HeartIcon, TrophyIcon, StarIcon, CheckBadgeIcon, LockClosedIcon } from '@heroicons/vue/24/solid'; // Added icons
122 -import dayjs from 'dayjs';
123 -import { Swiper, SwiperSlide } from 'swiper/vue';
124 -import { Mousewheel } from 'swiper/modules';
125 -import 'swiper/css';
126 -
127 -// 状态
128 -const { currentUser } = useAuth();
129 -// 使用一个通用的默认头像URL,实际项目中应替换为本地资源或配置的默认图
130 -const defaultAvatar = 'https://cdn.ipadbiz.cn/mlaj/images/default_avatar.png';
131 -
132 -// Swiper 实例和状态
133 -const swiperInstance = ref(null);
134 -const activeIndex = ref(0);
135 -
136 -const onSwiper = (swiper) => {
137 - swiperInstance.value = swiper;
138 -};
139 -
140 -const onSlideChange = (swiper) => {
141 - activeIndex.value = swiper.activeIndex;
142 -};
143 -
144 -const slideNext = () => {
145 - swiperInstance.value?.slideNext();
146 -};
147 -
148 -const userAvatar = computed(() => {
149 - let avatar = currentUser.value?.avatar;
150 - if (avatar && avatar.includes('cdn.ipadbiz.cn')) {
151 - avatar += '?imageMogr2/thumbnail/200x/strip/quality/70';
152 - }
153 - return avatar;
154 -});
155 -
156 -const userName = computed(() => currentUser.value?.name || '旅行者');
157 -
158 -// 加入时间逻辑
159 -const joinDate = computed(() => {
160 - return currentUser.value?.created_at || currentUser.value?.reg_time || new Date();
161 -});
162 -
163 -const joinDateFormatted = computed(() => {
164 - return dayjs(joinDate.value).format('YYYY年M月D日');
165 -});
166 -
167 -const durationString = computed(() => {
168 - const start = dayjs(joinDate.value);
169 - const now = dayjs();
170 - const years = now.diff(start, 'year');
171 - const months = now.diff(start, 'month') % 12;
172 -
173 - if (years === 0 && months === 0) {
174 - const days = now.diff(start, 'day');
175 - return `${days} 天`;
176 - }
177 -
178 - if (years > 0) {
179 - return `${years} 年 ${months} 个月`;
180 - } else {
181 - return `${months} 个月`;
182 - }
183 -});
184 -
185 -// Mock Data for Second Screen
186 -const activityCount = ref(28);
187 -const volunteerCount = ref(12);
188 -const volunteerHours = ref(100);
189 -
190 -
191 -onMounted(() => {
192 - console.log('Recall Page - Current User:', currentUser.value);
193 -});
194 -</script>
195 -
196 -<style scoped>
197 -/* 针对不同屏幕尺寸的微调可以放在这里,或者直接使用 Tailwind */
198 -</style>
...@@ -61,7 +61,7 @@ const route = useRoute() ...@@ -61,7 +61,7 @@ const route = useRoute()
61 useTitle(route.meta.title) 61 useTitle(route.meta.title)
62 62
63 const handleNext = () => { 63 const handleNext = () => {
64 - router.push({ name: 'RecallLogin' }) 64 + router.push({ path: '/recall/complete-info' })
65 } 65 }
66 </script> 66 </script>
67 67
......
...@@ -83,6 +83,7 @@ import { ref, computed } from 'vue' ...@@ -83,6 +83,7 @@ import { ref, computed } from 'vue'
83 import { useRouter } from 'vue-router' 83 import { useRouter } from 'vue-router'
84 import { showToast } from 'vant' 84 import { showToast } from 'vant'
85 import { useTitle } from '@vueuse/core' 85 import { useTitle } from '@vueuse/core'
86 +import { smsAPI } from '@/api/common'
86 87
87 import titleImg from '@/assets/images/recall/title01@2x.png' 88 import titleImg from '@/assets/images/recall/title01@2x.png'
88 import bgImg from '@/assets/images/recall/bg01@2x.png' 89 import bgImg from '@/assets/images/recall/bg01@2x.png'
...@@ -105,7 +106,7 @@ let timer = null ...@@ -105,7 +106,7 @@ let timer = null
105 /** 106 /**
106 * @description 发送验证码 107 * @description 发送验证码
107 */ 108 */
108 -const handleSendCode = () => { 109 +const handleSendCode = async () => {
109 if (!phone.value) { 110 if (!phone.value) {
110 showToast('请输入手机号') 111 showToast('请输入手机号')
111 return 112 return
...@@ -115,20 +116,29 @@ const handleSendCode = () => { ...@@ -115,20 +116,29 @@ const handleSendCode = () => {
115 return 116 return
116 } 117 }
117 118
118 - // Start countdown 119 + try {
119 - counting.value = true 120 + const { code } = await smsAPI({ mobile: phone.value })
120 - count.value = 60 121 + if (code) {
121 - showToast('验证码已发送') 122 + showToast('验证码已发送')
122 - 123 +
123 - timer = setInterval(() => { 124 + // Start countdown
124 - if (count.value > 0) { 125 + counting.value = true
125 - count.value-- 126 + count.value = 60
126 - } else { 127 +
127 - clearInterval(timer) 128 + timer = setInterval(() => {
128 - counting.value = false 129 + if (count.value > 0) {
129 - timer = null 130 + count.value--
131 + } else {
132 + clearInterval(timer)
133 + counting.value = false
134 + timer = null
135 + }
136 + }, 1000)
130 } 137 }
131 - }, 1000) 138 + } catch (error) {
139 + console.error('发送验证码失败:', error)
140 + showToast('发送验证码失败,请稍后重试')
141 + }
132 } 142 }
133 143
134 /** 144 /**
...@@ -145,8 +155,13 @@ const handleLogin = () => { ...@@ -145,8 +155,13 @@ const handleLogin = () => {
145 } 155 }
146 156
147 showToast('登录成功') 157 showToast('登录成功')
148 - // Navigate to next page (Index or whatever is next flow) 158 + // TODO: 登录之后需要判断是否有完善个人信息, 如果没有进入完善流程, 如果完成直接跳到timeline
149 - $router.push('/recall') 159 + const info = false;
160 + if (info) {
161 + $router.push('/recall/timeline')
162 + return
163 + }
164 + $router.push('/recall/boot')
150 } 165 }
151 166
152 // Agreement Content 167 // Agreement Content
......
...@@ -156,8 +156,7 @@ const activityCount = ref(20) ...@@ -156,8 +156,7 @@ const activityCount = ref(20)
156 const volunteerCount = ref(12) 156 const volunteerCount = ref(12)
157 157
158 const handleViewHistory = () => { 158 const handleViewHistory = () => {
159 - // Redirect to activities page or profile activities 159 + router.push('/recall/activity-history')
160 - router.push('/profile/activities')
161 } 160 }
162 161
163 </script> 162 </script>
......