hookehuyr

feat(路由): 添加keep-alive缓存IndexCheckInPage组件

refactor(路由): 修改滚动行为以记住位置
feat(打卡): 添加打卡后列表自动刷新功能
fix(打卡列表): 解决重复数据加载问题
......@@ -87,7 +87,9 @@ onUnmounted(() => {
<!-- 通过v-slot获取当前路由组件。使用component是为了实现动态组件渲染和加载状态的控制:当Component存在时渲染路由组件,不存在时显示加载动画。直接使用 -->
<router-view v-slot="{ Component }">
<div v-if="Component">
<keep-alive :include="['IndexCheckInPage']">
<component :is="Component" />
</keep-alive>
</div>
<div v-else
class="flex items-center justify-center h-screen bg-gradient-to-br from-green-50 via-teal-50 to-blue-50">
......
......@@ -331,6 +331,18 @@ export function useCheckin() {
if (result.code) {
showToast('提交成功')
// 设置刷新标记,用于列表页更新数据
const refreshType = route.query.status === 'edit' ? 'edit' : 'add';
sessionStorage.setItem('checkin_refresh_flag', refreshType);
if (refreshType === 'edit') {
sessionStorage.setItem('checkin_refresh_id', route.query.post_id);
} else if (result.data) {
// 尝试获取新ID
const newId = result.data.id || (typeof result.data !== 'object' ? result.data : null);
if (newId) sessionStorage.setItem('checkin_refresh_id', newId);
}
router.back()
}
} catch (error) {
......
/*
* @Date: 2025-03-20 20:36:36
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-12-04 11:25:22
* @LastEditTime: 2025-12-19 00:16:42
* @FilePath: /mlaj/src/router/index.js
* @Description: 路由实例创建和导出
*/
......@@ -14,7 +14,10 @@ import { getUserInfoAPI } from '@/api/users'
const router = createRouter({
history: createWebHashHistory(import.meta.env.VITE_BASE || '/'),
routes,
scrollBehavior() {
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
}
// 每次路由切换后,页面滚动到顶部
return { top: 0, left: 0 }
},
......
<!--
* @Date: 2025-03-20 19:55:21
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-12-18 11:22:03
* @LastEditTime: 2025-12-18 23:53:12
* @FilePath: /mlaj/src/views/HomePage.vue
* @Description: 美乐爱觉教育首页组件
*
......@@ -496,7 +496,7 @@ import SummerCampCard from '@/components/ui/SummerCampCard.vue'
import VideoPlayer from '@/components/ui/VideoPlayer.vue'
import CheckInList from '@/components/ui/CheckInList.vue'
// TODO: 导入模拟数据和工具函数
// 导入模拟数据和工具函数
import { liveStreams } from '@/utils/mockData'
import { useTitle } from '@vueuse/core'
import { useAuth } from '@/contexts/auth'
......
<!--
* @Date: 2025-05-29 15:34:17
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-12-18 23:48:47
* @LastEditTime: 2025-12-19 00:12:31
* @FilePath: /mlaj/src/views/checkin/IndexCheckInPage.vue
* @Description: 文件描述
-->
......@@ -137,8 +137,14 @@
</AppLayout>
</template>
<script>
export default {
name: 'IndexCheckInPage'
}
</script>
<script setup>
import { ref, onBeforeUnmount, onMounted, computed, nextTick, getCurrentInstance } from 'vue'
import { ref, onBeforeUnmount, onMounted, computed, nextTick, getCurrentInstance, onActivated, onDeactivated } from 'vue'
import { useRoute, useRouter, onBeforeRouteLeave } from 'vue-router'
import { showConfirmDialog, showSuccessToast, showFailToast, showLoadingToast } from 'vant';
import AppLayout from "@/components/layout/AppLayout.vue";
......@@ -149,7 +155,7 @@ import CheckinCard from "@/components/checkin/CheckinCard.vue";
import { useTitle, useResizeObserver, useScroll } from '@vueuse/core';
import dayjs from 'dayjs';
import { getTaskDetailAPI, getUploadTaskListAPI, delUploadTaskInfoAPI, likeUploadTaskInfoAPI, dislikeUploadTaskInfoAPI } from "@/api/checkin";
import { getTaskDetailAPI, getUploadTaskListAPI, delUploadTaskInfoAPI, likeUploadTaskInfoAPI, dislikeUploadTaskInfoAPI, getUploadTaskInfoAPI } from "@/api/checkin";
import { getTeacherFindSettingsAPI } from "@/api/teacher";
const route = useRoute()
......@@ -595,7 +601,15 @@ const onLoad = async (date) => {
});
if (res.code) {
// 整理数据结构
checkinDataList.value = [...checkinDataList.value, ...formatData(res.data)];
const newItems = formatData(res.data);
// 去重合并
const existingIds = new Set(checkinDataList.value.map(item => item.id));
const uniqueNewItems = newItems.filter(item => !existingIds.has(item.id));
if (uniqueNewItems.length > 0) {
checkinDataList.value.push(...uniqueNewItems);
}
finished.value = res.data.checkin_list.length < limit.value;
page.value = nextPage + 1;
}
......@@ -696,6 +710,67 @@ const formatData = (data) => {
})
return formattedData;
}
// 保存滚动位置
const savedScrollTop = ref(0)
onDeactivated(() => {
savedScrollTop.value = window.scrollY
})
onActivated(async () => {
// 恢复滚动位置
if (savedScrollTop.value > 0) {
setTimeout(() => {
window.scrollTo(0, savedScrollTop.value)
}, 0)
}
// 检查是否有数据刷新标记
const refreshType = sessionStorage.getItem('checkin_refresh_flag')
const refreshId = sessionStorage.getItem('checkin_refresh_id')
if (refreshType) {
// 清除标记
sessionStorage.removeItem('checkin_refresh_flag')
sessionStorage.removeItem('checkin_refresh_id')
if (refreshId) {
try {
// 获取最新的打卡信息
const { code, data } = await getUploadTaskInfoAPI({ i: refreshId })
if (code && data) {
// 构造伪造的 data 对象以适配 formatData
const mockData = { checkin_list: [data] }
const formattedList = formatData(mockData)
if (formattedList && formattedList.length > 0) {
const formattedItem = formattedList[0]
if (refreshType === 'edit') {
// 编辑模式:更新列表中的对应项
const index = checkinDataList.value.findIndex(item => item.id == refreshId)
if (index > -1) {
checkinDataList.value.splice(index, 1, formattedItem)
}
} else if (refreshType === 'add') {
// 新增模式:添加到列表顶部,且去重
const exists = checkinDataList.value.some(item => item.id == formattedItem.id)
if (!exists) {
checkinDataList.value.unshift(formattedItem)
}
// 更新统计数据
const current_date = route.query.date || dayjs().format('YYYY-MM-DD');
getTaskDetail(dayjs(current_date).format('YYYY-MM'));
}
}
}
} catch (error) {
console.error('刷新打卡数据失败:', error)
}
}
}
})
</script>
<style lang="less">
......