hookehuyr

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

在多个页面中替换感恩模块占位符为新的统计模型组件
添加任务ID字段到打卡逻辑中
完善计数打卡功能的任务选择逻辑
......@@ -28,6 +28,7 @@ declare module 'vue' {
OfficeViewer: typeof import('./components/ui/OfficeViewer.vue')['default']
PdfPreview: typeof import('./components/ui/PdfPreview.vue')['default']
PdfViewer: typeof import('./components/ui/PdfViewer.vue')['default']
PostCountModel: typeof import('./components/count/postCountModel.vue')['default']
ReviewPopup: typeof import('./components/courses/ReviewPopup.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
......
<!--
* @Date: 2025-12-11 17:26:25
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-12-11 17:29:23
* @FilePath: /mlaj/src/components/count/postCountModel.vue
* @Description: 发布作业统计模型
-->
<template>
<div class="post-count-model">
<!-- TODO 感恩模块还没有做 -->
<div>感恩模块还没有做</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
const props = defineProps({
postData: {
type: Object,
default: () => ({})
}
})
</script>
<style lang="less" scoped>
.post-count-model {
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
</style>
......@@ -21,6 +21,7 @@ export function useCheckin() {
const message = ref('')
const fileList = ref([])
const activeType = ref('') // 当前选中的打卡类型
const taskId = ref('') // 当前选中的任务ID
const maxCount = ref(5)
// 打卡类型
......@@ -371,6 +372,8 @@ export function useCheckin() {
if (code) {
message.value = data.note || ''
activeType.value = data.file_type || 'text'
// TODO: 假数据等待真实数据
taskId.value = 'task1'
// 如果有文件数据,初始化文件列表 - 使用data.files而不是data.meta
if (data.files && data.files.length > 0) {
......@@ -414,6 +417,7 @@ export function useCheckin() {
message,
fileList,
activeType,
taskId,
maxCount,
canSubmit,
......
......@@ -28,7 +28,7 @@
</van-popup>
</div>
<!-- 计数对象 -->
<div v-if="taskType === 'count'" class="mb-4">
<div v-if="taskType === 'count' && selectedTaskValue" class="mb-4">
<div class="flex justify-between items-center mb-2 mx-2">
<div class="text-sm font-bold text-gray-700">{{ dynamicFieldText }}对象</div>
<van-button size="small" type="primary" plain icon="plus"
......@@ -187,7 +187,7 @@
</template>
<script setup>
import { ref, computed, onMounted, nextTick, reactive } from 'vue'
import { ref, computed, onMounted, nextTick, reactive, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { getTaskDetailAPI, getUploadTaskInfoAPI } from "@/api/checkin"
import { getTeacherFindSettingsAPI, getTeacherSubtaskListAPI } from '@/api/teacher'
......@@ -195,7 +195,7 @@ import { useTitle } from '@vueuse/core'
import { useCheckin } from '@/composables/useCheckin'
import AudioPlayer from '@/components/ui/AudioPlayer.vue'
import VideoPlayer from '@/components/ui/VideoPlayer.vue'
import { showToast } from 'vant'
import { showToast, showLoadingToast } from 'vant'
import dayjs from 'dayjs'
const route = useRoute()
......@@ -209,6 +209,7 @@ const {
message,
fileList,
activeType,
taskId,
maxCount,
canSubmit,
beforeRead,
......@@ -233,23 +234,61 @@ const taskType = computed(() => route.query.task_type)
const showTaskPicker = ref(false)
const selectedTaskText = ref('')
const selectedTaskValue = ref('')
const taskOptions = [{ text: '全部作业', value: '' }]
const taskOptions = ref([])
// TODO: 模拟任务选项数据
const mockData = {
'task1': [
{ name: '张老师', city: '北京', school: '北京大学' },
{ name: '李老师', city: '上海', school: '复旦大学' }
],
'task2': [
{ name: '王老师', city: '广州', school: '中山大学' },
{ name: '赵老师', city: '深圳', school: '深圳大学' }
],
'task3': [
{ name: '孙老师', city: '杭州', school: '浙江大学' }
]
}
const fetchTargetList = async (taskId) => {
// 模拟接口调用延迟
setTimeout(() => {
targetList.value = mockData[taskId] || []
showLoadingToast({
message: '加载成功',
type: 'success',
duration: 1000
})
}, 500)
}
const onConfirmTask = ({ selectedOptions }) => {
const option = selectedOptions[0]
selectedTaskText.value = option.text
selectedTaskValue.value = option.value
showTaskPicker.value = false
// 如果是计数打卡,根据选中的作业ID查询计数对象
if (taskType.value === 'count') {
fetchTargetList(option.value)
}
}
// 监听作业选择变化
watch(selectedTaskValue, (newVal) => {
if (taskType.value === 'count' && newVal) {
fetchTargetList(newVal)
}
})
/********* TODO: *******/
// 计数打卡相关逻辑
const countValue = ref(1)
const selectedTargets = ref([])
// Mock 老师数据
const targetList = ref([
{ name: '张老师', city: '北京', school: '北京大学' },
{ name: '李老师', city: '上海', school: '复旦大学' }
])
const targetList = ref([])
const showAddTargetDialog = ref(false)
// 动态表单字段 Mock 数据
......@@ -783,18 +822,30 @@ onMounted(async () => {
getTaskDetail(dayjs().format('YYYY-MM'));
}
// 获取小作业列表
// TODO: 获取小作业列表
const subtask_list = await getTeacherSubtaskListAPI({ task_id: route.query.id })
if (subtask_list.code) {
taskOptions.value = [...taskOptions.value, ...subtask_list.data.map(item => ({
taskOptions.value = [...subtask_list.data.map(item => ({
text: item.title,
value: item.id
}))
]
}
// TODO: mock taskOptions 数据
taskOptions.value = [{ text: '作业一:基础练习', value: 'task1' },
{ text: '作业二:进阶挑战', value: 'task2' },
{ text: '作业三:综合应用', value: 'task3' }]
// 初始化编辑数据
await initEditData()
// TODO: 临时处理,待后端返回真实数据
if (taskId.value) {
selectedTaskText.value = taskOptions.value.find(option => option.value === taskId.value)?.text || ''
selectedTaskValue.value = taskId.value
console.warn('当前选中的任务ID:', taskId.value);
}
})
</script>
......
<!--
* @Date: 2025-05-29 15:34:17
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-12-11 15:19:52
* @LastEditTime: 2025-12-11 17:28:28
* @FilePath: /mlaj/src/views/checkin/IndexCheckInPage.vue
* @Description: 文件描述
-->
......@@ -99,8 +99,7 @@
</van-row>
</div>
<div class="post-content">
<!-- TODO 感恩模块还没有做 -->
<div>感恩模块还没有做</div>
<PostCountModel :post-data="post" />
<div class="post-text">{{ post.content }}</div>
<div class="post-media">
<div v-if="post.images.length" class="post-images">
......@@ -192,6 +191,7 @@ import FrostedGlass from "@/components/ui/FrostedGlass.vue";
import VideoPlayer from "@/components/ui/VideoPlayer.vue";
import AudioPlayer from "@/components/ui/AudioPlayer.vue";
import CollapsibleCalendar from "@/components/ui/CollapsibleCalendar.vue";
import PostCountModel from "@/components/count/postCountModel.vue";
import { useTitle } from '@vueuse/core';
import dayjs from 'dayjs';
......
......@@ -102,8 +102,7 @@
</van-row>
</div>
<div class="post-content">
<!-- TODO 感恩模块还没有做 -->
<div>感恩模块还没有做</div>
<PostCountModel :post-data="post" />
<div class="post-text">{{ post.content }}</div>
<div class="post-media">
<div v-if="post.images.length" class="post-images">
......@@ -197,6 +196,7 @@ import FrostedGlass from "@/components/ui/FrostedGlass.vue";
import VideoPlayer from "@/components/ui/VideoPlayer.vue";
import AudioPlayer from "@/components/ui/AudioPlayer.vue";
import CourseGroupCascader from '@/components/ui/CourseGroupCascader.vue'
import PostCountModel from '@/components/count/postCountModel.vue'
import { useTitle } from '@vueuse/core';
import dayjs from 'dayjs';
......
......@@ -205,8 +205,7 @@
</van-row>
</div>
<div class="post-content">
<!-- TODO 感恩模块还没有做 -->
<div>感恩模块还没有做</div>
<PostCountModel :post-data="post" />
<div class="post-text">{{ post.content }}</div>
<div class="post-media">
<div v-if="post.images.length" class="post-images">
......@@ -382,6 +381,7 @@ import { useRouter, useRoute } from 'vue-router'
import { showConfirmDialog, showSuccessToast, showFailToast, showLoadingToast } from 'vant';
import VideoPlayer from "@/components/ui/VideoPlayer.vue";
import AudioPlayer from "@/components/ui/AudioPlayer.vue";
import PostCountModel from '@/components/count/postCountModel.vue'
import { useTitle } from '@vueuse/core';
import dayjs from 'dayjs';
......
<!--
* @Date: 2025-11-19 22:05:00
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-12-11 13:30:57
* @LastEditTime: 2025-12-11 17:30:04
* @FilePath: /mlaj/src/views/teacher/studentRecordPage.vue
* @Description: 学生作业记录页面(仅作业记录与点评功能),固定 user_id 与 group_id
-->
......@@ -27,8 +27,7 @@
</van-row>
</div>
<div class="post-content">
<!-- TODO 感恩模块还没有做 -->
<div>感恩模块还没有做</div>
<PostCountModel :post-data="post" />
<div class="post-text">{{ post.content }}</div>
<div class="post-media">
<div v-if="post.images.length" class="post-images">
......@@ -122,6 +121,7 @@ import { useTitle } from '@vueuse/core'
import { showSuccessToast, showFailToast, showLoadingToast } from 'vant'
import VideoPlayer from '@/components/ui/VideoPlayer.vue'
import AudioPlayer from '@/components/ui/AudioPlayer.vue'
import PostCountModel from '@/components/count/postCountModel.vue'
import { addCheckinFeedbackAPI } from '@/api/teacher'
import { likeUploadTaskInfoAPI, dislikeUploadTaskInfoAPI, getCheckinTeacherListAPI } from '@/api/checkin'
......