hookehuyr

feat(统计模块): 添加发布作业统计模型组件

在多个页面中替换感恩模块占位符为新的统计模型组件
添加任务ID字段到打卡逻辑中
完善计数打卡功能的任务选择逻辑
...@@ -28,6 +28,7 @@ declare module 'vue' { ...@@ -28,6 +28,7 @@ declare module 'vue' {
28 OfficeViewer: typeof import('./components/ui/OfficeViewer.vue')['default'] 28 OfficeViewer: typeof import('./components/ui/OfficeViewer.vue')['default']
29 PdfPreview: typeof import('./components/ui/PdfPreview.vue')['default'] 29 PdfPreview: typeof import('./components/ui/PdfPreview.vue')['default']
30 PdfViewer: typeof import('./components/ui/PdfViewer.vue')['default'] 30 PdfViewer: typeof import('./components/ui/PdfViewer.vue')['default']
31 + PostCountModel: typeof import('./components/count/postCountModel.vue')['default']
31 ReviewPopup: typeof import('./components/courses/ReviewPopup.vue')['default'] 32 ReviewPopup: typeof import('./components/courses/ReviewPopup.vue')['default']
32 RouterLink: typeof import('vue-router')['RouterLink'] 33 RouterLink: typeof import('vue-router')['RouterLink']
33 RouterView: typeof import('vue-router')['RouterView'] 34 RouterView: typeof import('vue-router')['RouterView']
......
1 +<!--
2 + * @Date: 2025-12-11 17:26:25
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-12-11 17:29:23
5 + * @FilePath: /mlaj/src/components/count/postCountModel.vue
6 + * @Description: 发布作业统计模型
7 +-->
8 +<template>
9 + <div class="post-count-model">
10 + <!-- TODO 感恩模块还没有做 -->
11 + <div>感恩模块还没有做</div>
12 + </div>
13 +</template>
14 +
15 +<script setup>
16 +import { ref } from 'vue'
17 +import { useRoute, useRouter } from 'vue-router'
18 +
19 +const props = defineProps({
20 + postData: {
21 + type: Object,
22 + default: () => ({})
23 + }
24 +})
25 +
26 +
27 +</script>
28 +
29 +<style lang="less" scoped>
30 +.post-count-model {
31 + padding: 10px;
32 + border: 1px solid #ccc;
33 + border-radius: 5px;
34 +}
35 +</style>
...@@ -21,6 +21,7 @@ export function useCheckin() { ...@@ -21,6 +21,7 @@ export function useCheckin() {
21 const message = ref('') 21 const message = ref('')
22 const fileList = ref([]) 22 const fileList = ref([])
23 const activeType = ref('') // 当前选中的打卡类型 23 const activeType = ref('') // 当前选中的打卡类型
24 + const taskId = ref('') // 当前选中的任务ID
24 const maxCount = ref(5) 25 const maxCount = ref(5)
25 26
26 // 打卡类型 27 // 打卡类型
...@@ -371,6 +372,8 @@ export function useCheckin() { ...@@ -371,6 +372,8 @@ export function useCheckin() {
371 if (code) { 372 if (code) {
372 message.value = data.note || '' 373 message.value = data.note || ''
373 activeType.value = data.file_type || 'text' 374 activeType.value = data.file_type || 'text'
375 + // TODO: 假数据等待真实数据
376 + taskId.value = 'task1'
374 377
375 // 如果有文件数据,初始化文件列表 - 使用data.files而不是data.meta 378 // 如果有文件数据,初始化文件列表 - 使用data.files而不是data.meta
376 if (data.files && data.files.length > 0) { 379 if (data.files && data.files.length > 0) {
...@@ -414,6 +417,7 @@ export function useCheckin() { ...@@ -414,6 +417,7 @@ export function useCheckin() {
414 message, 417 message,
415 fileList, 418 fileList,
416 activeType, 419 activeType,
420 + taskId,
417 maxCount, 421 maxCount,
418 canSubmit, 422 canSubmit,
419 423
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
28 </van-popup> 28 </van-popup>
29 </div> 29 </div>
30 <!-- 计数对象 --> 30 <!-- 计数对象 -->
31 - <div v-if="taskType === 'count'" class="mb-4"> 31 + <div v-if="taskType === 'count' && selectedTaskValue" class="mb-4">
32 <div class="flex justify-between items-center mb-2 mx-2"> 32 <div class="flex justify-between items-center mb-2 mx-2">
33 <div class="text-sm font-bold text-gray-700">{{ dynamicFieldText }}对象</div> 33 <div class="text-sm font-bold text-gray-700">{{ dynamicFieldText }}对象</div>
34 <van-button size="small" type="primary" plain icon="plus" 34 <van-button size="small" type="primary" plain icon="plus"
...@@ -187,7 +187,7 @@ ...@@ -187,7 +187,7 @@
187 </template> 187 </template>
188 188
189 <script setup> 189 <script setup>
190 -import { ref, computed, onMounted, nextTick, reactive } from 'vue' 190 +import { ref, computed, onMounted, nextTick, reactive, watch } from 'vue'
191 import { useRoute, useRouter } from 'vue-router' 191 import { useRoute, useRouter } from 'vue-router'
192 import { getTaskDetailAPI, getUploadTaskInfoAPI } from "@/api/checkin" 192 import { getTaskDetailAPI, getUploadTaskInfoAPI } from "@/api/checkin"
193 import { getTeacherFindSettingsAPI, getTeacherSubtaskListAPI } from '@/api/teacher' 193 import { getTeacherFindSettingsAPI, getTeacherSubtaskListAPI } from '@/api/teacher'
...@@ -195,7 +195,7 @@ import { useTitle } from '@vueuse/core' ...@@ -195,7 +195,7 @@ import { useTitle } from '@vueuse/core'
195 import { useCheckin } from '@/composables/useCheckin' 195 import { useCheckin } from '@/composables/useCheckin'
196 import AudioPlayer from '@/components/ui/AudioPlayer.vue' 196 import AudioPlayer from '@/components/ui/AudioPlayer.vue'
197 import VideoPlayer from '@/components/ui/VideoPlayer.vue' 197 import VideoPlayer from '@/components/ui/VideoPlayer.vue'
198 -import { showToast } from 'vant' 198 +import { showToast, showLoadingToast } from 'vant'
199 import dayjs from 'dayjs' 199 import dayjs from 'dayjs'
200 200
201 const route = useRoute() 201 const route = useRoute()
...@@ -209,6 +209,7 @@ const { ...@@ -209,6 +209,7 @@ const {
209 message, 209 message,
210 fileList, 210 fileList,
211 activeType, 211 activeType,
212 + taskId,
212 maxCount, 213 maxCount,
213 canSubmit, 214 canSubmit,
214 beforeRead, 215 beforeRead,
...@@ -233,23 +234,61 @@ const taskType = computed(() => route.query.task_type) ...@@ -233,23 +234,61 @@ const taskType = computed(() => route.query.task_type)
233 const showTaskPicker = ref(false) 234 const showTaskPicker = ref(false)
234 const selectedTaskText = ref('') 235 const selectedTaskText = ref('')
235 const selectedTaskValue = ref('') 236 const selectedTaskValue = ref('')
236 -const taskOptions = [{ text: '全部作业', value: '' }] 237 +const taskOptions = ref([])
238 +
239 +// TODO: 模拟任务选项数据
240 +const mockData = {
241 + 'task1': [
242 + { name: '张老师', city: '北京', school: '北京大学' },
243 + { name: '李老师', city: '上海', school: '复旦大学' }
244 + ],
245 + 'task2': [
246 + { name: '王老师', city: '广州', school: '中山大学' },
247 + { name: '赵老师', city: '深圳', school: '深圳大学' }
248 + ],
249 + 'task3': [
250 + { name: '孙老师', city: '杭州', school: '浙江大学' }
251 + ]
252 +}
253 +
254 +const fetchTargetList = async (taskId) => {
255 + // 模拟接口调用延迟
256 + setTimeout(() => {
257 + targetList.value = mockData[taskId] || []
258 + showLoadingToast({
259 + message: '加载成功',
260 + type: 'success',
261 + duration: 1000
262 + })
263 + }, 500)
264 +}
237 265
238 const onConfirmTask = ({ selectedOptions }) => { 266 const onConfirmTask = ({ selectedOptions }) => {
239 const option = selectedOptions[0] 267 const option = selectedOptions[0]
240 selectedTaskText.value = option.text 268 selectedTaskText.value = option.text
241 selectedTaskValue.value = option.value 269 selectedTaskValue.value = option.value
242 showTaskPicker.value = false 270 showTaskPicker.value = false
271 +
272 + // 如果是计数打卡,根据选中的作业ID查询计数对象
273 + if (taskType.value === 'count') {
274 + fetchTargetList(option.value)
275 + }
243 } 276 }
244 277
278 +// 监听作业选择变化
279 +watch(selectedTaskValue, (newVal) => {
280 + if (taskType.value === 'count' && newVal) {
281 + fetchTargetList(newVal)
282 + }
283 +})
284 +
285 +/********* TODO: *******/
286 +
245 // 计数打卡相关逻辑 287 // 计数打卡相关逻辑
246 const countValue = ref(1) 288 const countValue = ref(1)
247 const selectedTargets = ref([]) 289 const selectedTargets = ref([])
248 // Mock 老师数据 290 // Mock 老师数据
249 -const targetList = ref([ 291 +const targetList = ref([])
250 - { name: '张老师', city: '北京', school: '北京大学' },
251 - { name: '李老师', city: '上海', school: '复旦大学' }
252 -])
253 const showAddTargetDialog = ref(false) 292 const showAddTargetDialog = ref(false)
254 293
255 // 动态表单字段 Mock 数据 294 // 动态表单字段 Mock 数据
...@@ -783,18 +822,30 @@ onMounted(async () => { ...@@ -783,18 +822,30 @@ onMounted(async () => {
783 getTaskDetail(dayjs().format('YYYY-MM')); 822 getTaskDetail(dayjs().format('YYYY-MM'));
784 } 823 }
785 824
786 - // 获取小作业列表 825 + // TODO: 获取小作业列表
787 const subtask_list = await getTeacherSubtaskListAPI({ task_id: route.query.id }) 826 const subtask_list = await getTeacherSubtaskListAPI({ task_id: route.query.id })
788 if (subtask_list.code) { 827 if (subtask_list.code) {
789 - taskOptions.value = [...taskOptions.value, ...subtask_list.data.map(item => ({ 828 + taskOptions.value = [...subtask_list.data.map(item => ({
790 text: item.title, 829 text: item.title,
791 value: item.id 830 value: item.id
792 })) 831 }))
793 ] 832 ]
794 } 833 }
795 834
835 + // TODO: mock taskOptions 数据
836 + taskOptions.value = [{ text: '作业一:基础练习', value: 'task1' },
837 + { text: '作业二:进阶挑战', value: 'task2' },
838 + { text: '作业三:综合应用', value: 'task3' }]
839 +
796 // 初始化编辑数据 840 // 初始化编辑数据
797 await initEditData() 841 await initEditData()
842 +
843 + // TODO: 临时处理,待后端返回真实数据
844 + if (taskId.value) {
845 + selectedTaskText.value = taskOptions.value.find(option => option.value === taskId.value)?.text || ''
846 + selectedTaskValue.value = taskId.value
847 + console.warn('当前选中的任务ID:', taskId.value);
848 + }
798 }) 849 })
799 </script> 850 </script>
800 851
......
1 <!-- 1 <!--
2 * @Date: 2025-05-29 15:34:17 2 * @Date: 2025-05-29 15:34:17
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-12-11 15:19:52 4 + * @LastEditTime: 2025-12-11 17:28:28
5 * @FilePath: /mlaj/src/views/checkin/IndexCheckInPage.vue 5 * @FilePath: /mlaj/src/views/checkin/IndexCheckInPage.vue
6 * @Description: 文件描述 6 * @Description: 文件描述
7 --> 7 -->
...@@ -99,8 +99,7 @@ ...@@ -99,8 +99,7 @@
99 </van-row> 99 </van-row>
100 </div> 100 </div>
101 <div class="post-content"> 101 <div class="post-content">
102 - <!-- TODO 感恩模块还没有做 --> 102 + <PostCountModel :post-data="post" />
103 - <div>感恩模块还没有做</div>
104 <div class="post-text">{{ post.content }}</div> 103 <div class="post-text">{{ post.content }}</div>
105 <div class="post-media"> 104 <div class="post-media">
106 <div v-if="post.images.length" class="post-images"> 105 <div v-if="post.images.length" class="post-images">
...@@ -192,6 +191,7 @@ import FrostedGlass from "@/components/ui/FrostedGlass.vue"; ...@@ -192,6 +191,7 @@ import FrostedGlass from "@/components/ui/FrostedGlass.vue";
192 import VideoPlayer from "@/components/ui/VideoPlayer.vue"; 191 import VideoPlayer from "@/components/ui/VideoPlayer.vue";
193 import AudioPlayer from "@/components/ui/AudioPlayer.vue"; 192 import AudioPlayer from "@/components/ui/AudioPlayer.vue";
194 import CollapsibleCalendar from "@/components/ui/CollapsibleCalendar.vue"; 193 import CollapsibleCalendar from "@/components/ui/CollapsibleCalendar.vue";
194 +import PostCountModel from "@/components/count/postCountModel.vue";
195 import { useTitle } from '@vueuse/core'; 195 import { useTitle } from '@vueuse/core';
196 import dayjs from 'dayjs'; 196 import dayjs from 'dayjs';
197 197
......
...@@ -102,8 +102,7 @@ ...@@ -102,8 +102,7 @@
102 </van-row> 102 </van-row>
103 </div> 103 </div>
104 <div class="post-content"> 104 <div class="post-content">
105 - <!-- TODO 感恩模块还没有做 --> 105 + <PostCountModel :post-data="post" />
106 - <div>感恩模块还没有做</div>
107 <div class="post-text">{{ post.content }}</div> 106 <div class="post-text">{{ post.content }}</div>
108 <div class="post-media"> 107 <div class="post-media">
109 <div v-if="post.images.length" class="post-images"> 108 <div v-if="post.images.length" class="post-images">
...@@ -197,6 +196,7 @@ import FrostedGlass from "@/components/ui/FrostedGlass.vue"; ...@@ -197,6 +196,7 @@ import FrostedGlass from "@/components/ui/FrostedGlass.vue";
197 import VideoPlayer from "@/components/ui/VideoPlayer.vue"; 196 import VideoPlayer from "@/components/ui/VideoPlayer.vue";
198 import AudioPlayer from "@/components/ui/AudioPlayer.vue"; 197 import AudioPlayer from "@/components/ui/AudioPlayer.vue";
199 import CourseGroupCascader from '@/components/ui/CourseGroupCascader.vue' 198 import CourseGroupCascader from '@/components/ui/CourseGroupCascader.vue'
199 +import PostCountModel from '@/components/count/postCountModel.vue'
200 import { useTitle } from '@vueuse/core'; 200 import { useTitle } from '@vueuse/core';
201 import dayjs from 'dayjs'; 201 import dayjs from 'dayjs';
202 202
......
...@@ -205,8 +205,7 @@ ...@@ -205,8 +205,7 @@
205 </van-row> 205 </van-row>
206 </div> 206 </div>
207 <div class="post-content"> 207 <div class="post-content">
208 - <!-- TODO 感恩模块还没有做 --> 208 + <PostCountModel :post-data="post" />
209 - <div>感恩模块还没有做</div>
210 <div class="post-text">{{ post.content }}</div> 209 <div class="post-text">{{ post.content }}</div>
211 <div class="post-media"> 210 <div class="post-media">
212 <div v-if="post.images.length" class="post-images"> 211 <div v-if="post.images.length" class="post-images">
...@@ -382,6 +381,7 @@ import { useRouter, useRoute } from 'vue-router' ...@@ -382,6 +381,7 @@ import { useRouter, useRoute } from 'vue-router'
382 import { showConfirmDialog, showSuccessToast, showFailToast, showLoadingToast } from 'vant'; 381 import { showConfirmDialog, showSuccessToast, showFailToast, showLoadingToast } from 'vant';
383 import VideoPlayer from "@/components/ui/VideoPlayer.vue"; 382 import VideoPlayer from "@/components/ui/VideoPlayer.vue";
384 import AudioPlayer from "@/components/ui/AudioPlayer.vue"; 383 import AudioPlayer from "@/components/ui/AudioPlayer.vue";
384 +import PostCountModel from '@/components/count/postCountModel.vue'
385 import { useTitle } from '@vueuse/core'; 385 import { useTitle } from '@vueuse/core';
386 import dayjs from 'dayjs'; 386 import dayjs from 'dayjs';
387 387
......
1 <!-- 1 <!--
2 * @Date: 2025-11-19 22:05:00 2 * @Date: 2025-11-19 22:05:00
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-12-11 13:30:57 4 + * @LastEditTime: 2025-12-11 17:30:04
5 * @FilePath: /mlaj/src/views/teacher/studentRecordPage.vue 5 * @FilePath: /mlaj/src/views/teacher/studentRecordPage.vue
6 * @Description: 学生作业记录页面(仅作业记录与点评功能),固定 user_id 与 group_id 6 * @Description: 学生作业记录页面(仅作业记录与点评功能),固定 user_id 与 group_id
7 --> 7 -->
...@@ -27,8 +27,7 @@ ...@@ -27,8 +27,7 @@
27 </van-row> 27 </van-row>
28 </div> 28 </div>
29 <div class="post-content"> 29 <div class="post-content">
30 - <!-- TODO 感恩模块还没有做 --> 30 + <PostCountModel :post-data="post" />
31 - <div>感恩模块还没有做</div>
32 <div class="post-text">{{ post.content }}</div> 31 <div class="post-text">{{ post.content }}</div>
33 <div class="post-media"> 32 <div class="post-media">
34 <div v-if="post.images.length" class="post-images"> 33 <div v-if="post.images.length" class="post-images">
...@@ -122,6 +121,7 @@ import { useTitle } from '@vueuse/core' ...@@ -122,6 +121,7 @@ import { useTitle } from '@vueuse/core'
122 import { showSuccessToast, showFailToast, showLoadingToast } from 'vant' 121 import { showSuccessToast, showFailToast, showLoadingToast } from 'vant'
123 import VideoPlayer from '@/components/ui/VideoPlayer.vue' 122 import VideoPlayer from '@/components/ui/VideoPlayer.vue'
124 import AudioPlayer from '@/components/ui/AudioPlayer.vue' 123 import AudioPlayer from '@/components/ui/AudioPlayer.vue'
124 +import PostCountModel from '@/components/count/postCountModel.vue'
125 import { addCheckinFeedbackAPI } from '@/api/teacher' 125 import { addCheckinFeedbackAPI } from '@/api/teacher'
126 import { likeUploadTaskInfoAPI, dislikeUploadTaskInfoAPI, getCheckinTeacherListAPI } from '@/api/checkin' 126 import { likeUploadTaskInfoAPI, dislikeUploadTaskInfoAPI, getCheckinTeacherListAPI } from '@/api/checkin'
127 127
......