hookehuyr

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

- 删除不再使用的Index.vue组件
- 更新各页面路由跳转逻辑,添加条件判断
- 在登录页面添加短信验证码API调用
- 为ID查询和完整信息页面添加默认测试数据
- 调整路由配置,移除旧路由
/*
* @Date: 2025-03-20 20:36:36
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-12-23 15:51:28
* @LastEditTime: 2025-12-23 17:34:57
* @FilePath: /mlaj/src/router/routes.js
* @Description: 路由地址映射配置
*/
......@@ -97,12 +97,6 @@ export const routes = [
meta: { title: '我的活动' },
},
{
path: '/recall',
name: 'Recall',
component: () => import('../views/recall/Index.vue'),
meta: { title: '历史足迹' },
},
{
path: '/recall/login',
name: 'RecallLogin',
component: () => import('../views/recall/login.vue'),
......
......@@ -89,7 +89,7 @@ useTitle(route.meta.title || '完善信息')
const form = reactive({
name: '张开心', // Mock initial data as per design
phone: '15658666022', // Mock initial data as per design
idCard: ''
idCard: '44030619900101001X'
})
// Actions
......@@ -115,8 +115,15 @@ const handleConfirm = () => {
}
// Validation passed
showToast('提交成功')
// Navigate to next step or handle submission logic here
// showToast('提交成功')
// TODO: 完善信息之后, 如果能查看历史数据, 则需要跳转到timeline
const flag = false;
if (flag) {
router.push('/recall/timeline')
return
}
// 如果不能查看历史数据, 则需要跳转到id-query, 继续查数据
router.push('/recall/id-query')
}
</script>
......
......@@ -79,7 +79,7 @@ const route = useRoute()
const router = useRouter()
useTitle('身份证号查询')
const idCard = ref('')
const idCard = ref('44030619900101001X')
// ID Card Validation
const validateIdCard = (id) => {
......@@ -100,8 +100,14 @@ const handleConfirm = () => {
}
// TODO: Submit logic
showToast('验证通过')
// router.push(...)
// showToast('验证通过')
// TODO: 如果能查到数据, 则跳转到timeline, 否则弹出提示
const flag = true;
if (flag) {
router.push('/recall/timeline')
return
}
showToast('时光机没有找到您的历史活动信息')
}
</script>
......
<!--
* @Date: 2025-12-19 15:40:34
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-12-19 16:24:51
* @FilePath: /mlaj/src/views/recall/Index.vue
* @Description: 文件描述
-->
<template>
<div class="h-screen bg-[#F0FBF9] overflow-hidden relative">
<!-- Swiper Container -->
<swiper
:direction="'vertical'"
:modules="[Mousewheel]"
:mousewheel="true"
class="h-full w-full"
@slideChange="onSlideChange"
:speed="800"
@swiper="onSwiper"
>
<!-- 第一屏 -->
<swiper-slide>
<div class="flex flex-col items-center pt-20 pb-10 px-6 relative h-full w-full">
<!-- 头像 -->
<div class="relative mb-6 opacity-0 translate-y-4 transition-all duration-700 delay-100" :class="{ 'opacity-100 translate-y-0': activeIndex === 0 }">
<img
:src="userAvatar || defaultAvatar"
class="w-24 h-24 rounded-full border-4 border-white shadow-lg object-cover"
alt="User Avatar"
/>
</div>
<!-- 欢迎语 -->
<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>
<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>
<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>
<!-- 时间卡片 -->
<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 }">
<p class="text-slate-500 mb-4">您加入Behalo的时间</p>
<div class="text-3xl font-bold text-[#1A3B34] mb-4 tracking-wide">{{ joinDateFormatted }}</div>
<div class="text-slate-500">
已有 <span class="text-[#1A3B34] font-bold text-lg">{{ durationString }}</span>
</div>
</div>
<!-- 底部引导 -->
<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">
<ChevronDoubleDownIcon class="w-6 h-6 text-[#4ADE80] mb-2" />
<span class="text-[#4ADE80] font-medium text-sm">向上滑动,探索更多历程</span>
</div>
</div>
</swiper-slide>
<!-- 第二屏 -->
<swiper-slide>
<div class="flex flex-col items-center pt-10 pb-10 px-6 relative h-full w-full overflow-y-auto overflow-x-hidden">
<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>
<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>
<!-- 活动参与卡片 -->
<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 }">
<div class="flex items-center mb-4">
<div class="w-10 h-10 rounded-full bg-[#E0F2F1] flex items-center justify-center mr-3 text-[#26A69A]">
<CalendarIcon class="w-5 h-5" />
</div>
<span class="font-bold text-[#1A3B34]">参与宇宙活动</span>
</div>
<div class="mb-2">
<span class="text-5xl font-bold text-[#1A3B34]">{{ activityCount }}</span>
<span class="text-slate-500 ml-1">次</span>
</div>
<p class="text-slate-500 text-xs">您积极参与各类教育活动,累计参与{{ activityCount }}次</p>
</div>
<!-- 义工服务卡片 -->
<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 }">
<div class="flex items-center mb-4">
<div class="w-10 h-10 rounded-full bg-[#E8F5E9] flex items-center justify-center mr-3 text-[#4CAF50]">
<HeartIcon class="w-5 h-5" />
</div>
<span class="font-bold text-[#1A3B34]">义工服务活动</span>
</div>
<div class="mb-2">
<span class="text-5xl font-bold text-[#1A3B34]">{{ volunteerCount }}</span>
<span class="text-slate-500 ml-1">次</span>
</div>
<p class="text-slate-500 text-xs">您热心公益,作为义工服务了{{ volunteerCount }}次活动,贡献时长超过{{ volunteerHours }}小时</p>
</div>
<!-- 荣誉徽章 -->
<!--<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 }">
<h3 class="font-bold text-[#1A3B34] mb-4">获得的荣誉徽章</h3>
<div class="flex justify-between px-2">
<!~~ 徽章1 ~~>
<div class="w-16 h-16 rounded-full bg-[#FFF9C4] flex items-center justify-center shadow-sm">
<TrophyIcon class="w-8 h-8 text-[#FBC02D]" />
</div>
<!~~ 徽章2 ~~>
<div class="w-16 h-16 rounded-full bg-[#F3E5F5] flex items-center justify-center shadow-sm">
<StarIcon class="w-8 h-8 text-[#AB47BC]" />
</div>
<!~~ 徽章3 ~~>
<div class="w-16 h-16 rounded-full bg-[#E3F2FD] flex items-center justify-center shadow-sm">
<CheckBadgeIcon class="w-8 h-8 text-[#42A5F5]" />
</div>
<!~~ 徽章4 (锁定) ~~>
<div class="w-16 h-16 rounded-full bg-[#F5F5F5] flex items-center justify-center shadow-sm">
<LockClosedIcon class="w-8 h-8 text-[#BDBDBD]" />
</div>
</div>
</div>-->
</div>
</swiper-slide>
</swiper>
</div>
</template>
<script setup>
import { computed, onMounted, ref } from 'vue';
import { useAuth } from '@/contexts/auth';
import { ChevronDoubleDownIcon, CalendarIcon, HeartIcon, TrophyIcon, StarIcon, CheckBadgeIcon, LockClosedIcon } from '@heroicons/vue/24/solid'; // Added icons
import dayjs from 'dayjs';
import { Swiper, SwiperSlide } from 'swiper/vue';
import { Mousewheel } from 'swiper/modules';
import 'swiper/css';
// 状态
const { currentUser } = useAuth();
// 使用一个通用的默认头像URL,实际项目中应替换为本地资源或配置的默认图
const defaultAvatar = 'https://cdn.ipadbiz.cn/mlaj/images/default_avatar.png';
// Swiper 实例和状态
const swiperInstance = ref(null);
const activeIndex = ref(0);
const onSwiper = (swiper) => {
swiperInstance.value = swiper;
};
const onSlideChange = (swiper) => {
activeIndex.value = swiper.activeIndex;
};
const slideNext = () => {
swiperInstance.value?.slideNext();
};
const userAvatar = computed(() => {
let avatar = currentUser.value?.avatar;
if (avatar && avatar.includes('cdn.ipadbiz.cn')) {
avatar += '?imageMogr2/thumbnail/200x/strip/quality/70';
}
return avatar;
});
const userName = computed(() => currentUser.value?.name || '旅行者');
// 加入时间逻辑
const joinDate = computed(() => {
return currentUser.value?.created_at || currentUser.value?.reg_time || new Date();
});
const joinDateFormatted = computed(() => {
return dayjs(joinDate.value).format('YYYY年M月D日');
});
const durationString = computed(() => {
const start = dayjs(joinDate.value);
const now = dayjs();
const years = now.diff(start, 'year');
const months = now.diff(start, 'month') % 12;
if (years === 0 && months === 0) {
const days = now.diff(start, 'day');
return `${days} 天`;
}
if (years > 0) {
return `${years} 年 ${months} 个月`;
} else {
return `${months} 个月`;
}
});
// Mock Data for Second Screen
const activityCount = ref(28);
const volunteerCount = ref(12);
const volunteerHours = ref(100);
onMounted(() => {
console.log('Recall Page - Current User:', currentUser.value);
});
</script>
<style scoped>
/* 针对不同屏幕尺寸的微调可以放在这里,或者直接使用 Tailwind */
</style>
......@@ -61,7 +61,7 @@ const route = useRoute()
useTitle(route.meta.title)
const handleNext = () => {
router.push({ name: 'RecallLogin' })
router.push({ path: '/recall/complete-info' })
}
</script>
......
......@@ -83,6 +83,7 @@ import { ref, computed } from 'vue'
import { useRouter } from 'vue-router'
import { showToast } from 'vant'
import { useTitle } from '@vueuse/core'
import { smsAPI } from '@/api/common'
import titleImg from '@/assets/images/recall/title01@2x.png'
import bgImg from '@/assets/images/recall/bg01@2x.png'
......@@ -105,7 +106,7 @@ let timer = null
/**
* @description 发送验证码
*/
const handleSendCode = () => {
const handleSendCode = async () => {
if (!phone.value) {
showToast('请输入手机号')
return
......@@ -115,10 +116,14 @@ const handleSendCode = () => {
return
}
try {
const { code } = await smsAPI({ mobile: phone.value })
if (code) {
showToast('验证码已发送')
// Start countdown
counting.value = true
count.value = 60
showToast('验证码已发送')
timer = setInterval(() => {
if (count.value > 0) {
......@@ -129,6 +134,11 @@ const handleSendCode = () => {
timer = null
}
}, 1000)
}
} catch (error) {
console.error('发送验证码失败:', error)
showToast('发送验证码失败,请稍后重试')
}
}
/**
......@@ -145,8 +155,13 @@ const handleLogin = () => {
}
showToast('登录成功')
// Navigate to next page (Index or whatever is next flow)
$router.push('/recall')
// TODO: 登录之后需要判断是否有完善个人信息, 如果没有进入完善流程, 如果完成直接跳到timeline
const info = false;
if (info) {
$router.push('/recall/timeline')
return
}
$router.push('/recall/boot')
}
// Agreement Content
......
......@@ -156,8 +156,7 @@ const activityCount = ref(20)
const volunteerCount = ref(12)
const handleViewHistory = () => {
// Redirect to activities page or profile activities
router.push('/profile/activities')
router.push('/recall/activity-history')
}
</script>
......