hookehuyr

feat(签到): 实现从后端获取签到类型列表功能

- 新增checkin.js API模块用于获取签到任务列表
- 修改CheckInDialog和HomePage组件,移除硬编码的签到类型
- 根据后端返回的task_type动态显示不同图标
- 添加onMounted钩子异步获取签到列表数据
1 +/*
2 + * @Date: 2025-06-06 09:26:16
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-06-06 09:31:00
5 + * @FilePath: /mlaj/src/api/checkin.js
6 + * @Description: 签到模块相关接口
7 + */
8 +import { fn, fetch } from './fn'
9 +
10 +const Api = {
11 + GET_TASK_LIST: '/srv/?a=task&t=my_list'
12 +}
13 +
14 +/**
15 + * @description: 获取签到类型列表
16 + * @param: keyword 搜索课程名称
17 + * @return: data: [{ id 作业id, title 作业名称, begin_date 开始时间, end_date 结束时间, task_type 任务类型 [checkin=签到 | file=上传附件], is_gray 作业是否应该置灰 }]
18 + */
19 +
20 +export const getTaskListAPI = (params) => fn(fetch.get(Api.GET_TASK_LIST, params))
...@@ -38,10 +38,8 @@ ...@@ -38,10 +38,8 @@
38 ? 'bg-green-500 text-white' 38 ? 'bg-green-500 text-white'
39 : 'bg-gray-100 text-gray-500' 39 : 'bg-gray-100 text-gray-500'
40 ]"> 40 ]">
41 - <van-icon v-if="checkInType.id === 'reflection'" name="coupon-o" size="1.5rem"/> 41 + <van-icon v-if="checkInType.task_type === 'checkin'" name="edit" size="1.5rem" />
42 - <van-icon v-if="checkInType.id === 'reading'" name="edit" size="1.5rem" /> 42 + <van-icon v-if="checkInType.task_type === 'file'" name="tosend" size="1.5rem" />
43 - <van-icon v-if="checkInType.id === 'exercise'" name="tosend" size="1.5rem" />
44 - <van-icon v-if="checkInType.id === 'mix'" name="send-gift-o" size="1.5rem" />
45 </div> 43 </div>
46 <span class="text-xs">{{ checkInType.name }}</span> 44 <span class="text-xs">{{ checkInType.name }}</span>
47 </button> 45 </button>
...@@ -74,14 +72,10 @@ ...@@ -74,14 +72,10 @@
74 import { ref } from 'vue' 72 import { ref } from 'vue'
75 import { showToast } from 'vant' 73 import { showToast } from 'vant'
76 import { useRoute, useRouter } from 'vue-router' 74 import { useRoute, useRouter } from 'vue-router'
75 +import { getTaskListAPI } from "@/api/checkin";
77 76
78 -// TODO: 从后端获取 77 +// 签到列表
79 -const checkInTypes = [ 78 +const checkInTypes = ref([]);
80 - { id: 'reading', name: '课程打卡', icon: 'book', path: '/checkin/reading' },
81 - { id: 'exercise', name: '签到打卡', icon: 'running', path: '/checkin/exercise' },
82 - { id: 'reflection', name: '学习打卡', icon: 'pencil-alt', path: '/checkin/writing' },
83 - { id: 'mix', name: '图文打卡', icon: 'pencil-alt', path: '/checkin/index' }
84 -];
85 79
86 const route = useRoute() 80 const route = useRoute()
87 const router = useRouter() 81 const router = useRouter()
...@@ -102,7 +96,7 @@ const isCheckingIn = ref(false) ...@@ -102,7 +96,7 @@ const isCheckingIn = ref(false)
102 const checkInSuccess = ref(false) 96 const checkInSuccess = ref(false)
103 97
104 const handleCheckInSelect = (type) => { 98 const handleCheckInSelect = (type) => {
105 - if (type.id === 'mix') { 99 + if (type.task_type === 'file') {
106 router.push({ 100 router.push({
107 path: '/checkin/index', 101 path: '/checkin/index',
108 }) 102 })
...@@ -147,4 +141,18 @@ const handleClose = () => { ...@@ -147,4 +141,18 @@ const handleClose = () => {
147 checkInSuccess.value = false 141 checkInSuccess.value = false
148 emit('update:show', false) 142 emit('update:show', false)
149 } 143 }
144 +
145 +onMounted(async () => {
146 + // 获取签到列表
147 + const task = await getTaskListAPI()
148 + if (task.code) {
149 + task.data.forEach(item => {
150 + checkInTypes.value.push({
151 + id: item.id,
152 + name: item.title,
153 + task_type: item.task_type,
154 + })
155 + })
156 + }
157 +})
150 </script> 158 </script>
......
1 <!-- 1 <!--
2 * @Date: 2025-03-20 19:55:21 2 * @Date: 2025-03-20 19:55:21
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-06-04 09:38:40 4 + * @LastEditTime: 2025-06-06 11:10:02
5 * @FilePath: /mlaj/src/views/HomePage.vue 5 * @FilePath: /mlaj/src/views/HomePage.vue
6 * @Description: 美乐爱觉教育首页组件 6 * @Description: 美乐爱觉教育首页组件
7 * 7 *
...@@ -106,10 +106,8 @@ ...@@ -106,10 +106,8 @@
106 ? 'bg-green-500 text-white' 106 ? 'bg-green-500 text-white'
107 : 'bg-gray-100 text-gray-500' 107 : 'bg-gray-100 text-gray-500'
108 ]"> 108 ]">
109 - <van-icon v-if="checkInType.id === 'reflection'" name="coupon-o" size="1.5rem"/> 109 + <van-icon v-if="checkInType.task_type === 'checkin'" name="edit" size="1.5rem" />
110 - <van-icon v-if="checkInType.id === 'reading'" name="edit" size="1.5rem" /> 110 + <van-icon v-if="checkInType.task_type === 'file'" name="tosend" size="1.5rem" />
111 - <van-icon v-if="checkInType.id === 'exercise'" name="tosend" size="1.5rem" />
112 - <van-icon v-if="checkInType.id === 'mix'" name="send-gift-o" size="1.5rem" />
113 </div> 111 </div>
114 <span class="text-xs">{{ checkInType.name }}</span> 112 <span class="text-xs">{{ checkInType.name }}</span>
115 </button> 113 </button>
...@@ -525,6 +523,7 @@ import { showToast } from 'vant' ...@@ -525,6 +523,7 @@ import { showToast } from 'vant'
525 523
526 // 导入接口 524 // 导入接口
527 import { getCourseListAPI } from "@/api/course"; 525 import { getCourseListAPI } from "@/api/course";
526 +import { getTaskListAPI } from "@/api/checkin";
528 527
529 // 视频播放状态管理 528 // 视频播放状态管理
530 const activeVideoIndex = ref(null); // 当前播放的视频索引 529 const activeVideoIndex = ref(null); // 当前播放的视频索引
...@@ -565,14 +564,8 @@ const getRecommendations = (random = false) => { ...@@ -565,14 +564,8 @@ const getRecommendations = (random = false) => {
565 return userRecommendations.value.slice(0, 4) 564 return userRecommendations.value.slice(0, 4)
566 } 565 }
567 566
568 -// TODO: 从后端获取 567 +// 签到列表
569 -const checkInTypes = [ 568 +const checkInTypes = ref([]);
570 - { id: 'reading', name: '课程打卡', icon: 'book', path: '/checkin/reading' },
571 - { id: 'exercise', name: '签到打卡', icon: 'running', path: '/checkin/exercise' },
572 - // { id: 'study', name: '团队打卡', icon: 'graduation-cap', path: '/checkin/study' },
573 - { id: 'reflection', name: '学习打卡', icon: 'pencil-alt', path: '/checkin/writing' },
574 - { id: 'mix', name: '图文打卡', icon: 'pencil-alt', path: '/checkin/index' }
575 -];
576 569
577 // 自动轮播 570 // 自动轮播
578 let carouselInterval 571 let carouselInterval
...@@ -599,6 +592,20 @@ onMounted(async () => { ...@@ -599,6 +592,20 @@ onMounted(async () => {
599 if (res2.code) { 592 if (res2.code) {
600 hotCourses.value = res2.data 593 hotCourses.value = res2.data
601 } 594 }
595 +
596 + // 获取签到列表
597 + if(currentUser.value) {
598 + const task = await getTaskListAPI()
599 + if (task.code) {
600 + task.data.forEach(item => {
601 + checkInTypes.value.push({
602 + id: item.id,
603 + name: item.title,
604 + task_type: item.task_type,
605 + })
606 + })
607 + }
608 + }
602 }) 609 })
603 610
604 onUnmounted(() => { 611 onUnmounted(() => {
...@@ -636,7 +643,7 @@ const rightContent = h(RightContent) ...@@ -636,7 +643,7 @@ const rightContent = h(RightContent)
636 // 工具函数:处理图片加载错误,设置默认头像 643 // 工具函数:处理图片加载错误,设置默认头像
637 const handleImageError = (e) => { 644 const handleImageError = (e) => {
638 e.target.onerror = null // 防止循环触发错误 645 e.target.onerror = null // 防止循环触发错误
639 - e.target.src = 'https://cdn.ipadbiz.cn/mlaj/images/user-avatar-2.jpg' // 设置默认头像 646 + e.target.src = '' // 设置默认头像
640 } 647 }
641 648
642 // 工具函数:格式化今天的日期为中文格式 649 // 工具函数:格式化今天的日期为中文格式
...@@ -660,7 +667,7 @@ const scrollToSlide = (index) => { ...@@ -660,7 +667,7 @@ const scrollToSlide = (index) => {
660 667
661 // 打卡功能:处理打卡类型选择 668 // 打卡功能:处理打卡类型选择
662 const handleCheckInSelect = (checkInType) => { 669 const handleCheckInSelect = (checkInType) => {
663 - if (checkInType.id === 'mix') { 670 + if (checkInType.task_type === 'file') {
664 $router.push({ 671 $router.push({
665 path: '/checkin/index', 672 path: '/checkin/index',
666 }) 673 })
...@@ -697,8 +704,6 @@ const handleCheckInSubmit = () => { ...@@ -697,8 +704,6 @@ const handleCheckInSubmit = () => {
697 }, 1500) 704 }, 1500)
698 } 705 }
699 706
700 -
701 -
702 const contentRef = ref(null) // 内容区域的ref引用 707 const contentRef = ref(null) // 内容区域的ref引用
703 708
704 // 监听activeTab变化,重置内容区域位置 709 // 监听activeTab变化,重置内容区域位置
......