hookehuyr

refactor(打卡弹窗): 将打卡弹窗统一为通用组件 CheckInDialog

重构 CourseDetailPage、StudyCoursePage 和 StudyDetailPage 中的打卡弹窗逻辑,提取为通用组件 CheckInDialog
组件支持通过 props 传入今日和历史打卡任务列表,并统一处理弹窗显隐和交互逻辑
移除各页面中冗余的打卡相关状态和方法,简化代码结构
...@@ -75,3 +75,16 @@ https://oa-dev.onwall.cn/f/mlaj ...@@ -75,3 +75,16 @@ https://oa-dev.onwall.cn/f/mlaj
75 - 依赖:`pnpm add qrcode`(在 Canvas 内本地生成二维码,避免跨域图片导致画布污染)。 75 - 依赖:`pnpm add qrcode`(在 Canvas 内本地生成二维码,避免跨域图片导致画布污染)。
76 - 跨域:通过 `crossorigin="anonymous"` 加载封面,并追加时间戳防缓存;若封面跨域不允许,则显示降级卡片,仍可长按截图保存。 76 - 跨域:通过 `crossorigin="anonymous"` 加载封面,并追加时间戳防缓存;若封面跨域不允许,则显示降级卡片,仍可长按截图保存。
77 - 文案:使用中文字体并自动换行限制行数,末行超出追加省略号。 77 - 文案:使用中文字体并自动换行限制行数,末行超出追加省略号。
78 +
79 + - 打卡弹窗统一为通用组件 CheckInDialog
80 + - 目的:统一 CourseDetailPage、StudyCoursePage、StudyDetailPage 三处页面的打卡弹窗与交互,避免重复逻辑。
81 + - 组件:`/src/components/ui/CheckInDialog.vue``v-model:show` 控制显隐;支持外部传入任务列表。
82 + - Props:
83 + - `items_today`:今日打卡任务数组(外部传入)。
84 + - `items_history`:历史打卡任务数组(外部传入)。
85 + - 数据结构:每项需包含 `id``title(name)``task_type``checkin`/`upload`)、`is_gray`
86 + - 使用位置:
87 + - `/src/views/courses/CourseDetailPage.vue`
88 + - `/src/views/profile/StudyCoursePage.vue`
89 + - `/src/views/study/StudyDetailPage.vue`
90 + - 清理:上述页面已移除旧弹窗的冗余状态与方法(如 `default_list``showTaskList``showTimeoutTaskList``selectedCheckIn` 等),统一由组件内部处理。
......
...@@ -8,7 +8,10 @@ ...@@ -8,7 +8,10 @@
8 > 8 >
9 <div class="p-4"> 9 <div class="p-4">
10 <div class="flex justify-between items-center mb-3"> 10 <div class="flex justify-between items-center mb-3">
11 - <h3 class="font-medium">今日打卡</h3> 11 + <h3 class="font-medium">
12 + <span :class="{ 'text-green-500' : active_tab === 'today' }" @click="active_tab = 'today'">今日打卡</span>&nbsp;&nbsp;&nbsp;&nbsp;
13 + <span v-if="has_history" :class="{ 'text-green-500' : active_tab === 'history' }" @click="active_tab = 'history'">历史打卡</span>
14 + </h3>
12 <van-icon name="cross" @click="handleClose" /> 15 <van-icon name="cross" @click="handleClose" />
13 </div> 16 </div>
14 17
...@@ -22,7 +25,7 @@ ...@@ -22,7 +25,7 @@
22 <template v-else> 25 <template v-else>
23 <div class="grid grid-cols-2 gap-4 py-2"> <!-- grid-cols-2 强制每行2列,gap控制间距 --> 26 <div class="grid grid-cols-2 gap-4 py-2"> <!-- grid-cols-2 强制每行2列,gap控制间距 -->
24 <button 27 <button
25 - v-for="checkInType in checkInTypes" 28 + v-for="checkInType in active_list"
26 :key="checkInType.id" 29 :key="checkInType.id"
27 class="flex flex-col items-center p-2 rounded-lg border transition-colors 30 class="flex flex-col items-center p-2 rounded-lg border transition-colors
28 bg-white/70 border-gray-100 hover:bg-white" 31 bg-white/70 border-gray-100 hover:bg-white"
...@@ -68,7 +71,7 @@ ...@@ -68,7 +71,7 @@
68 </template> 71 </template>
69 72
70 <script setup> 73 <script setup>
71 -import { ref } from 'vue' 74 +import { ref, computed, onMounted } from 'vue'
72 import { showToast } from 'vant' 75 import { showToast } from 'vant'
73 import { useRoute, useRouter } from 'vue-router' 76 import { useRoute, useRouter } from 'vue-router'
74 import { getTaskListAPI, checkinTaskAPI } from "@/api/checkin"; 77 import { getTaskListAPI, checkinTaskAPI } from "@/api/checkin";
...@@ -80,11 +83,12 @@ const route = useRoute() ...@@ -80,11 +83,12 @@ const route = useRoute()
80 const router = useRouter() 83 const router = useRouter()
81 84
82 const props = defineProps({ 85 const props = defineProps({
83 - show: { 86 + /** 弹窗显隐 */
84 - type: Boolean, 87 + show: { type: Boolean, required: true, default: false },
85 - required: true, 88 + /** 今日打卡任务(外部传入,可选) */
86 - default: false 89 + items_today: { type: Array, default: () => [] },
87 - } 90 + /** 历史打卡任务(外部传入,可选) */
91 + items_history: { type: Array, default: () => [] }
88 }) 92 })
89 93
90 const emit = defineEmits(['update:show', 'check-in-success', 'check-in-data']) 94 const emit = defineEmits(['update:show', 'check-in-success', 'check-in-data'])
...@@ -94,6 +98,36 @@ const checkInContent = ref('') ...@@ -94,6 +98,36 @@ const checkInContent = ref('')
94 const isCheckingIn = ref(false) 98 const isCheckingIn = ref(false)
95 const checkInSuccess = ref(false) 99 const checkInSuccess = ref(false)
96 100
101 +/**
102 + * @var {import('vue').Ref<'today'|'history'>} active_tab
103 + * @description 当前选中的任务标签页:今日或历史。
104 + */
105 +const active_tab = ref('today')
106 +
107 +/**
108 + * @function has_history
109 + * @description 是否存在历史任务(用于显示“历史打卡”标签)。
110 + * @returns {boolean}
111 + */
112 +const has_history = computed(() => {
113 + const list = Array.isArray(props.items_history) ? props.items_history : []
114 + return list.length > 0
115 +})
116 +
117 +/**
118 + * @function active_list
119 + * @description 当前展示的任务列表:优先使用外部传入,未传时回退为组件内部获取的列表。
120 + * @returns {Array}
121 + */
122 +const active_list = computed(() => {
123 + const today = Array.isArray(props.items_today) ? props.items_today : []
124 + const history = Array.isArray(props.items_history) ? props.items_history : []
125 + if (active_tab.value === 'today') {
126 + return today.length ? today : checkInTypes.value
127 + }
128 + return history
129 +})
130 +
97 const handleCheckInSelect = (type) => { 131 const handleCheckInSelect = (type) => {
98 if (type.is_gray && type.task_type === 'checkin') { 132 if (type.is_gray && type.task_type === 'checkin') {
99 showToast('您已经完成了今天的打卡') 133 showToast('您已经完成了今天的打卡')
...@@ -151,18 +185,20 @@ const handleClose = () => { ...@@ -151,18 +185,20 @@ const handleClose = () => {
151 } 185 }
152 186
153 onMounted(async () => { 187 onMounted(async () => {
154 - // 获取签到列表 188 + // 当未从外部传入“今日任务”时,回退为组件内部获取的通用任务列表
155 - const task = await getTaskListAPI() 189 + if (!Array.isArray(props.items_today) || props.items_today.length === 0) {
156 - if (task.code) { 190 + const task = await getTaskListAPI()
157 - emit('check-in-data', task.data) 191 + if (task.code) {
158 - task.data.forEach(item => { 192 + emit('check-in-data', task.data)
159 - checkInTypes.value.push({ 193 + task.data.forEach(item => {
160 - id: item.id, 194 + checkInTypes.value.push({
161 - name: item.title, 195 + id: item.id,
162 - task_type: item.task_type, 196 + name: item.title,
163 - is_gray: item.is_gray 197 + task_type: item.task_type,
164 - }) 198 + is_gray: item.is_gray
165 - }) 199 + })
166 - } 200 + })
201 + }
202 + }
167 }) 203 })
168 </script> 204 </script>
......
...@@ -264,70 +264,13 @@ ...@@ -264,70 +264,13 @@
264 <!-- Review Popup --> 264 <!-- Review Popup -->
265 <ReviewPopup v-model:show="showReviewPopup" title="立即评价" @submit="handleReviewSubmit" /> 265 <ReviewPopup v-model:show="showReviewPopup" title="立即评价" @submit="handleReviewSubmit" />
266 266
267 - <!-- 打卡弹窗 --> 267 + <!-- 打卡弹窗(统一组件) -->
268 - <van-popup 268 + <CheckInDialog
269 - v-model:show="showCheckInDialog" 269 + v-model:show="showCheckInDialog"
270 - round 270 + :items_today="task_list"
271 - position="bottom" 271 + :items_history="timeout_task_list"
272 - @close="closeCheckInDialog" 272 + @check-in-success="handleCheckInSuccess"
273 - :style="{ minHeight: '30%', maxHeight: '80%', width: '100%' }" 273 + />
274 - >
275 - <div class="p-4">
276 - <div class="flex justify-between items-center mb-3">
277 - <h3 class="font-medium">
278 - <span :class="{ 'text-green-500' : showTaskList }" @click="toggleTask('today')">今日打卡</span>&nbsp;&nbsp;&nbsp;&nbsp;
279 - <span :class="{ 'text-green-500' : showTimeoutTaskList }" @click="toggleTask('timeout')">历史打卡</span>
280 - </h3>
281 - <van-icon name="cross" @click="showCheckInDialog = false" />
282 - </div>
283 -
284 - <div v-if="checkInSuccess" class="bg-green-50 border border-green-200 rounded-lg p-4 text-center">
285 - <svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10 text-green-500 mx-auto mb-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
286 - <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
287 - </svg>
288 - <h4 class="text-green-700 font-medium mb-1">打卡成功!</h4>
289 - </div>
290 - <template v-else>
291 - <div class="grid grid-cols-2 gap-4 py-2">
292 - <button
293 - v-for="checkInType in default_list"
294 - :key="checkInType.id"
295 - class="flex flex-col items-center p-2 rounded-lg border transition-colors
296 - bg-white/70 border-gray-100 hover:bg-white"
297 - :class="{
298 - 'bg-green-100 border-green-200': selectedCheckIn?.id === checkInType.id
299 - }"
300 - @click="handleCheckInSelect(checkInType)"
301 - >
302 - <div class="w-12 h-12 rounded-full flex items-center justify-center mb-1 transition-colors
303 - bg-gray-100 text-gray-500"
304 - :class="{
305 - 'bg-green-500 text-white': selectedCheckIn?.id === checkInType.id
306 - }"
307 - >
308 - <van-icon v-if="checkInType.task_type === 'checkin'" name="edit" size="1.5rem" :color="checkInType.is_gray ? 'gray' : ''" />
309 - <van-icon v-if="checkInType.task_type === 'upload'" name="tosend" size="1.5rem" :color="checkInType.is_gray ? 'gray' : ''" />
310 - </div>
311 - <span :class="['text-xs', checkInType.is_gray ? 'text-gray-500' : '']">{{ checkInType.name }}</span>
312 - </button>
313 - </div>
314 -
315 - <div v-if="selectedCheckIn" class="mt-3">
316 - <button
317 - class="mt-2 w-full bg-gradient-to-r from-green-500 to-green-600 text-white py-2 rounded-lg flex items-center justify-center"
318 - @click="handleCheckInSubmit"
319 - :disabled="isCheckingIn"
320 - >
321 - <template v-if="isCheckingIn">
322 - <div class="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin mr-2"></div>
323 - 提交中...
324 - </template>
325 - <template v-else>提交打卡</template>
326 - </button>
327 - </div>
328 - </template>
329 - </div>
330 - </van-popup>
331 274
332 <!-- 咨询弹窗:底部只有关闭按钮,内容支持富文本 --> 275 <!-- 咨询弹窗:底部只有关闭按钮,内容支持富文本 -->
333 <van-popup 276 <van-popup
...@@ -397,11 +340,12 @@ import { sharePage } from '@/composables/useShare.js' ...@@ -397,11 +340,12 @@ import { sharePage } from '@/composables/useShare.js'
397 import AppLayout from '@/components/layout/AppLayout.vue' 340 import AppLayout from '@/components/layout/AppLayout.vue'
398 import FrostedGlass from '@/components/ui/FrostedGlass.vue' 341 import FrostedGlass from '@/components/ui/FrostedGlass.vue'
399 import SharePoster from '@/components/ui/SharePoster.vue' 342 import SharePoster from '@/components/ui/SharePoster.vue'
343 +import CheckInDialog from '@/components/ui/CheckInDialog.vue'
400 344
401 // 导入接口 345 // 导入接口
402 import { getCourseDetailAPI, getGroupCommentListAPI, addGroupCommentAPI } from "@/api/course"; 346 import { getCourseDetailAPI, getGroupCommentListAPI, addGroupCommentAPI } from "@/api/course";
403 import { addFavoriteAPI, cancelFavoriteAPI } from "@/api/favorite"; 347 import { addFavoriteAPI, cancelFavoriteAPI } from "@/api/favorite";
404 -import { checkinTaskAPI } from '@/api/checkin'; 348 +// 已统一使用通用打卡弹窗,移除未使用的打卡提交接口
405 349
406 // 350 //
407 // Open Graph 元标签:进入课程详情页时动态插入,离开页面时移除 351 // Open Graph 元标签:进入课程详情页时动态插入,离开页面时移除
...@@ -568,13 +512,7 @@ const handleIntroduceClick = (event) => { ...@@ -568,13 +512,7 @@ const handleIntroduceClick = (event) => {
568 // 打卡相关状态 512 // 打卡相关状态
569 const task_list = ref([]) 513 const task_list = ref([])
570 const timeout_task_list = ref([]) 514 const timeout_task_list = ref([])
571 -const default_list = ref([])
572 -const showTaskList = ref(true)
573 -const showTimeoutTaskList = ref(false)
574 const showCheckInDialog = ref(false) 515 const showCheckInDialog = ref(false)
575 -const selectedCheckIn = ref(null)
576 -const isCheckingIn = ref(false)
577 -const checkInSuccess = ref(false)
578 516
579 // 咨询弹窗相关状态 517 // 咨询弹窗相关状态
580 /** 518 /**
...@@ -860,7 +798,7 @@ onMounted(async () => { ...@@ -860,7 +798,7 @@ onMounted(async () => {
860 }); 798 });
861 } 799 }
862 800
863 - default_list.value = task_list.value; 801 + // 统一弹窗组件后不再维护 default_list
864 802
865 // 进入详情页时写入 Open Graph 元标签,提升分享预览效果 803 // 进入详情页时写入 Open Graph 元标签,提升分享预览效果
866 set_og_meta({ 804 set_og_meta({
...@@ -979,54 +917,6 @@ const goToStudyDetail = (item) => { ...@@ -979,54 +917,6 @@ const goToStudyDetail = (item) => {
979 * 处理打卡选择 917 * 处理打卡选择
980 * @param {Object} type - 打卡类型对象 918 * @param {Object} type - 打卡类型对象
981 */ 919 */
982 -const handleCheckInSelect = (type) => {
983 - if (type.is_gray && type.task_type === 'checkin') {
984 - showToast('您已经完成了今天的打卡');
985 - return;
986 - }
987 - if (type.task_type === 'upload') {
988 - router.push({
989 - path: '/checkin/index',
990 - query: {
991 - id: type.id
992 - }
993 - });
994 - showCheckInDialog.value = false;
995 - return;
996 - } else {
997 - selectedCheckIn.value = type;
998 - }
999 -};
1000 -
1001 -/**
1002 - * 处理打卡提交
1003 - */
1004 -const handleCheckInSubmit = async () => {
1005 - if (!selectedCheckIn.value) {
1006 - showToast('请选择打卡项目');
1007 - return;
1008 - }
1009 -
1010 - isCheckingIn.value = true;
1011 - try {
1012 - const { code } = await checkinTaskAPI({ task_id: selectedCheckIn.value.id });
1013 - if (code) {
1014 - checkInSuccess.value = true;
1015 - // 重置表单
1016 - setTimeout(() => {
1017 - checkInSuccess.value = false;
1018 - selectedCheckIn.value = null;
1019 - showCheckInDialog.value = false;
1020 - }, 1500);
1021 - }
1022 - } catch (error) {
1023 - console.error('打卡失败:', error);
1024 - showToast('打卡失败,请重试');
1025 - } finally {
1026 - isCheckingIn.value = false;
1027 - }
1028 -};
1029 -
1030 /** 920 /**
1031 * 打开打卡弹窗 921 * 打开打卡弹窗
1032 */ 922 */
...@@ -1041,36 +931,13 @@ const goToCheckin = () => { ...@@ -1041,36 +931,13 @@ const goToCheckin = () => {
1041 }) 931 })
1042 return; 932 return;
1043 } 933 }
1044 - if(!default_list.value.length) { 934 + if(!(task_list.value.length || timeout_task_list.value.length)) {
1045 showToast('暂无打卡任务'); 935 showToast('暂无打卡任务');
1046 return; 936 return;
1047 } 937 }
1048 showCheckInDialog.value = true; 938 showCheckInDialog.value = true;
1049 }; 939 };
1050 940
1051 -/**
1052 - * 切换打卡任务类型
1053 - * @param {string} type - 任务类型 ('today' | 'timeout')
1054 - */
1055 -const toggleTask = (type) => {
1056 - if(type === 'today') {
1057 - showTaskList.value = true;
1058 - showTimeoutTaskList.value = false;
1059 - default_list.value = task_list.value;
1060 - } else {
1061 - showTaskList.value = false;
1062 - showTimeoutTaskList.value = true;
1063 - default_list.value = timeout_task_list.value;
1064 - }
1065 -}
1066 -
1067 -/**
1068 - * 关闭打卡弹窗
1069 - */
1070 -const closeCheckInDialog = () => {
1071 - showCheckInDialog.value = false;
1072 -}
1073 -
1074 setTimeout(() => { 941 setTimeout(() => {
1075 // TAG:微信分享 942 // TAG:微信分享
1076 // 自定义分享内容 943 // 自定义分享内容
...@@ -1108,3 +975,10 @@ setTimeout(() => { ...@@ -1108,3 +975,10 @@ setTimeout(() => {
1108 background-color: #4caf50; 975 background-color: #4caf50;
1109 } 976 }
1110 </style> 977 </style>
978 +/**
979 + * 处理打卡成功
980 + * 注释:统一弹窗触发成功事件后,页面提示成功。
981 + */
982 +const handleCheckInSuccess = () => {
983 + showToast('打卡成功');
984 +}
......
...@@ -118,70 +118,13 @@ ...@@ -118,70 +118,13 @@
118 </div> 118 </div>
119 </div> 119 </div>
120 120
121 - <!-- 打卡弹窗 --> 121 + <!-- 打卡弹窗:统一使用 CheckInDialog 组件 -->
122 - <van-popup 122 + <CheckInDialog
123 v-model:show="showCheckInDialog" 123 v-model:show="showCheckInDialog"
124 - round 124 + :items_today="task_list"
125 - position="bottom" 125 + :items_history="timeout_task_list"
126 - @close="closeCheckInDialog" 126 + @check-in-success="handleCheckInSuccess"
127 - :style="{ minHeight: '30%', maxHeight: '80%', width: '100%' }" 127 + />
128 - >
129 - <div class="p-4">
130 - <div class="flex justify-between items-center mb-3">
131 - <h3 class="font-medium">
132 - <span :class="{ 'text-green-500' : showTaskList }" @click="toggleTask('today')">今日打卡</span>&nbsp;&nbsp;&nbsp;&nbsp;
133 - <span :class="{ 'text-green-500' : showTimeoutTaskList }" @click="toggleTask('timeout')">历史打卡</span>
134 - </h3>
135 - <van-icon name="cross" @click="showCheckInDialog = false" />
136 - </div>
137 -
138 - <div v-if="checkInSuccess" class="bg-green-50 border border-green-200 rounded-lg p-4 text-center">
139 - <svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10 text-green-500 mx-auto mb-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
140 - <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
141 - </svg>
142 - <h4 class="text-green-700 font-medium mb-1">打卡成功!</h4>
143 - </div>
144 - <template v-else>
145 - <div class="grid grid-cols-2 gap-4 py-2">
146 - <button
147 - v-for="checkInType in default_list"
148 - :key="checkInType.id"
149 - class="flex flex-col items-center p-2 rounded-lg border transition-colors
150 - bg-white/70 border-gray-100 hover:bg-white"
151 - :class="{
152 - 'bg-green-100 border-green-200': selectedCheckIn?.id === checkInType.id
153 - }"
154 - @click="handleCheckInSelect(checkInType)"
155 - >
156 - <div class="w-12 h-12 rounded-full flex items-center justify-center mb-1 transition-colors
157 - bg-gray-100 text-gray-500"
158 - :class="{
159 - 'bg-green-500 text-white': selectedCheckIn?.id === checkInType.id
160 - }"
161 - >
162 - <van-icon v-if="checkInType.task_type === 'checkin'" name="edit" size="1.5rem" :color="checkInType.is_gray ? 'gray' : ''" />
163 - <van-icon v-if="checkInType.task_type === 'upload'" name="tosend" size="1.5rem" :color="checkInType.is_gray ? 'gray' : ''" />
164 - </div>
165 - <span :class="['text-xs', checkInType.is_gray ? 'text-gray-500' : '']">{{ checkInType.name }}</span>
166 - </button>
167 - </div>
168 -
169 - <div v-if="selectedCheckIn" class="mt-3">
170 - <button
171 - class="mt-2 w-full bg-gradient-to-r from-green-500 to-green-600 text-white py-2 rounded-lg flex items-center justify-center"
172 - @click="handleCheckInSubmit"
173 - :disabled="isCheckingIn"
174 - >
175 - <template v-if="isCheckingIn">
176 - <div class="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin mr-2"></div>
177 - 提交中...
178 - </template>
179 - <template v-else>提交打卡</template>
180 - </button>
181 - </div>
182 - </template>
183 - </div>
184 - </van-popup>
185 </div> 128 </div>
186 </AppLayout> 129 </AppLayout>
187 </template> 130 </template>
...@@ -193,10 +136,10 @@ import { useRouter } from "vue-router"; ...@@ -193,10 +136,10 @@ import { useRouter } from "vue-router";
193 import dayjs from 'dayjs'; 136 import dayjs from 'dayjs';
194 import { showToast } from 'vant'; 137 import { showToast } from 'vant';
195 import AppLayout from '@/components/layout/AppLayout.vue'; 138 import AppLayout from '@/components/layout/AppLayout.vue';
139 +import CheckInDialog from '@/components/ui/CheckInDialog.vue';
196 140
197 // 导入接口 141 // 导入接口
198 import { getCourseDetailAPI } from '@/api/course'; 142 import { getCourseDetailAPI } from '@/api/course';
199 -import { checkinTaskAPI } from '@/api/checkin';
200 143
201 const router = useRouter(); 144 const router = useRouter();
202 145
...@@ -334,9 +277,6 @@ const course_type_maps = ref({ ...@@ -334,9 +277,6 @@ const course_type_maps = ref({
334 277
335 const task_list = ref([]); 278 const task_list = ref([]);
336 const timeout_task_list = ref([]); 279 const timeout_task_list = ref([]);
337 -const default_list = ref([]);
338 -const showTaskList = ref(true);
339 -const showTimeoutTaskList = ref(false);
340 280
341 onMounted(async () => { 281 onMounted(async () => {
342 /** 282 /**
...@@ -376,8 +316,6 @@ onMounted(async () => { ...@@ -376,8 +316,6 @@ onMounted(async () => {
376 } 316 }
377 317
378 course_lessons.value = data.schedule || []; 318 course_lessons.value = data.schedule || [];
379 - default_list.value = task_list.value;
380 - showTaskList.value = true;
381 } 319 }
382 else { 320 else {
383 // 课程不存在,跳转到课程主页面 321 // 课程不存在,跳转到课程主页面
...@@ -515,83 +453,22 @@ const goToStudyDetail = (lessonId) => { ...@@ -515,83 +453,22 @@ const goToStudyDetail = (lessonId) => {
515 453
516 // 打卡相关状态 454 // 打卡相关状态
517 const showCheckInDialog = ref(false); 455 const showCheckInDialog = ref(false);
518 -const selectedCheckIn = ref(null);
519 -const isCheckingIn = ref(false);
520 -const checkInSuccess = ref(false);
521 456
522 // 处理打卡选择 457 // 处理打卡选择
523 -const handleCheckInSelect = (type) => {
524 - if (type.is_gray && type.task_type === 'checkin') {
525 - showToast('您已经完成了今天的打卡');
526 - return;
527 - }
528 - if (type.task_type === 'upload') {
529 - router.push({
530 - path: '/checkin/index',
531 - query: {
532 - id: type.id
533 - }
534 - });
535 - showCheckInDialog.value = false;
536 - return;
537 - } else {
538 - selectedCheckIn.value = type;
539 - }
540 -};
541 -
542 -// 处理打卡提交
543 -const handleCheckInSubmit = async () => {
544 - if (!selectedCheckIn.value) {
545 - showToast('请选择打卡项目');
546 - return;
547 - }
548 -
549 - isCheckingIn.value = true;
550 - try {
551 - const { code } = await checkinTaskAPI({ task_id: selectedCheckIn.value.id });
552 - if (code) {
553 - checkInSuccess.value = true;
554 - // 重置表单
555 - setTimeout(() => {
556 - checkInSuccess.value = false;
557 - selectedCheckIn.value = null;
558 - showCheckInDialog.value = false;
559 - }, 1500);
560 - }
561 - } catch (error) {
562 - console.error('打卡失败:', error);
563 - showToast('打卡失败,请重试');
564 - } finally {
565 - isCheckingIn.value = false;
566 - }
567 -};
568 -
569 const goToCheckin = () => { 458 const goToCheckin = () => {
570 - if(!default_list.value.length) { 459 + if(!(task_list.value.length || timeout_task_list.value.length)) {
571 showToast('暂无打卡任务'); 460 showToast('暂无打卡任务');
572 return; 461 return;
573 } 462 }
574 showCheckInDialog.value = true; 463 showCheckInDialog.value = true;
575 }; 464 };
576 465
577 -const toggleTask = (type) => { 466 +/**
578 - if(type === 'today') { 467 + * 处理打卡成功
579 - showTaskList.value = true; 468 + * 注释:统一弹窗触发成功事件后,页面提示成功。
580 - showTimeoutTaskList.value = false; 469 + */
581 - default_list.value = task_list.value; 470 +const handleCheckInSuccess = () => {
582 - } else { 471 + showToast('打卡成功');
583 - showTaskList.value = false;
584 - showTimeoutTaskList.value = true;
585 - default_list.value = timeout_task_list.value;
586 - }
587 -}
588 -
589 -const closeCheckInDialog = () => {
590 - showCheckInDialog.value = false;
591 - // 切换到今日任务
592 - showTaskList.value = true;
593 - showTimeoutTaskList.value = false;
594 - default_list.value = task_list.value;
595 } 472 }
596 </script> 473 </script>
597 474
......
This diff is collapsed. Click to expand it.