feat(统计模块): 添加发布作业统计模型组件
在多个页面中替换感恩模块占位符为新的统计模型组件 添加任务ID字段到打卡逻辑中 完善计数打卡功能的任务选择逻辑
Showing
8 changed files
with
111 additions
and
20 deletions
| ... | @@ -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'] | ... | ... |
src/components/count/postCountModel.vue
0 → 100644
| 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 | ... | ... |
-
Please register or login to post a comment