hookehuyr

feat(教师端): 优化作业详情显示和日历组件交互

重构作业详情显示逻辑,提取格式化函数复用
调整日历组件布局,增加打卡规则查看功能
修改任务筛选器宽度和对齐方式
<template>
<div class="inline-block">
<div @click="show = true" class="text-sm text-gray-500 flex items-center cursor-pointer ml-2">
<div class="inline-block" style="width: 60%;">
<div @click="show = true" class="text-sm text-gray-500 flex items-center justify-end cursor-pointer ml-2">
<span class="mr-1">{{ selectedLabel }}</span>
<van-icon name="arrow-down" />
</div>
......
<!--
* @Date: 2025-01-25 15:34:17
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-12-14 22:08:03
* @LastEditTime: 2025-12-18 22:06:48
* @FilePath: /mlaj/src/components/ui/CollapsibleCalendar.vue
* @Description: 可折叠日历组件
-->
......@@ -20,15 +20,16 @@
<div class="calendar-date-main">{{ formattedCurrentDate }}</div>
<div class="calendar-weekday">{{ formattedWeekday }}</div>
</div> -->
<div class="calendar-title-wrapper" @click="expandCalendar">
<div class="calendar-title-wrapper">
<div class="calendar-title">{{ title }}</div>
<div class="calendar-subtitle">点击切换日期</div>
<!-- <div class="text-xs text-gray-500 mt-1">点击查看打卡规则</div> -->
</div>
</div>
<div class="calendar-content">
<div class="calendar-date-display">
<div class="calendar-date-display" @click="expandCalendar">
<div class="calendar-date-main">{{ formattedCurrentDate }}</div>
<div class="calendar-weekday">{{ formattedWeekday }}</div>
<div class="text-xs text-gray-500 mt-1">点击切换日期</div>
</div>
<!-- <div class="calendar-action">
<div class="calendar-action-text">指定日期</div>
......@@ -48,6 +49,7 @@
</svg>
</div>
</div>
<div v-if="selectedSubtask" class="text-xs text-gray-500 mt-1 cursor-pointer hover:text-green-600 transition-colors" @click.stop="openRulesPopup">点击查看打卡规则</div>
</div>
</div>
</div>
......@@ -93,6 +95,25 @@
:default-index="0"
/>
</van-popup>
<!-- 打卡规则弹窗 -->
<van-popup
v-model:show="showRulesPopup"
round
position="bottom"
closeable
:style="{ maxHeight: '50%' }"
>
<div class="p-4">
<div class="text-lg font-bold mb-4 text-center text-gray-800">打卡规则</div>
<div class="details text-sm text-gray-600 pl-4 border-l-4 border-green-500" v-if="formattedRules">
<div class="mb-2">周期:{{ formattedRules.cycle }}</div>
<div class="mb-2">频次:{{ formattedRules.frequency }}</div>
<div v-if="formattedRules.begin_date" class="mb-2">开始时间:{{ formattedRules.begin_date }}</div>
<div v-if="formattedRules.end_date" class="mb-2">截止时间:{{ formattedRules.end_date }}</div>
<div v-if="formattedRules.attachment_type" class="mb-2">附件类型:{{ formattedRules.attachment_type }}</div>
</div>
</div>
</van-popup>
</div>
</template>
......@@ -131,6 +152,9 @@ const currentDate = ref(props.modelValue || new Date())
// 作业筛选相关
const showCoursePicker = ref(false)
const selectedCourseText = ref('全部作业')
const selectedCourseId = ref('')
const showRulesPopup = ref(false)
const courseColumns = computed(() => {
return [
{ text: '全部作业', value: '' },
......@@ -141,14 +165,58 @@ const courseColumns = computed(() => {
]
})
const selectedSubtask = computed(() => {
if (!selectedCourseId.value) return null
return props.subtaskList.find(item => item.id === selectedCourseId.value)
})
const formattedRules = computed(() => {
if (!selectedSubtask.value) return null
const data = selectedSubtask.value
const cycleMap = {
'0': '本周期',
'30': '每月',
'7': '每周',
'1': '每日'
}
const cycleText = cycleMap[data.cycle] || data.cycle || '每日'
let attachmentText = '文本'
if (Array.isArray(data.attachment_type)) {
const typeMap = {
'text': '文本',
'image': '图片',
'video': '视频',
'audio': '音频'
}
attachmentText = data.attachment_type.map(t => typeMap[t] || t).join('/')
} else if (data.attachment_type) {
attachmentText = data.attachment_type
}
return {
cycle: cycleText,
frequency: `每周期${data.frequency || 1}次`,
begin_date: data.begin_date ? dayjs(data.begin_date).format('YYYY-MM-DD') : '',
end_date: data.end_date ? dayjs(data.end_date).format('YYYY-MM-DD') : '',
attachment_type: attachmentText
}
})
const openCoursePicker = () => {
showCoursePicker.value = true
}
const openRulesPopup = () => {
showRulesPopup.value = true
}
const onConfirmCourse = (result) => {
// 兼容 Vant 3/4
const option = result.selectedOptions ? result.selectedOptions[0] : result
selectedCourseText.value = option.text
selectedCourseId.value = option.value
showCoursePicker.value = false
emit('select-course', option.value)
}
......@@ -298,12 +366,6 @@ defineExpose({
// text-overflow: ellipsis;
// white-space: nowrap;
}
.calendar-subtitle {
font-size: 12px;
color: #7f8c8d;
font-weight: 400;
}
}
.calendar-date-display {
......@@ -322,6 +384,12 @@ defineExpose({
color: #4caf50;
font-weight: 500;
}
.calendar-subtitle {
font-size: 12px;
color: #7f8c8d;
font-weight: 400;
}
}
}
......
<!--
* @Date: 2025-11-19 21:00:00
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-12-15 18:18:51
* @LastEditTime: 2025-12-18 21:54:18
* @FilePath: /mlaj/src/views/teacher/taskHomePage.vue
* @Description: 教师端作业主页(头部介绍、统计、日历与学生完成情况)
-->
......@@ -27,11 +27,11 @@
<div class="intro text-base text-gray-700 leading-relaxed mb-3" v-html="task_intro"></div>
<div class="details text-sm text-gray-600" v-if="!subtask_list.length || selectedSubtaskId">
<div class="detailItem">周期:{{ task_details.cycle }}</div>
<div class="detailItem">频次:{{ task_details.frequency }}</div>
<div v-if="task_details.begin_date" class="detailItem">开始时间:{{ task_details.begin_date }}</div>
<div v-if="task_details.end_date" class="detailItem">截止时间:{{ task_details.end_date }}</div>
<div v-if="task_details.attachment_type.length" class="detailItem">附件类型:{{ task_details.attachment_type
<div class="detailItem">周期:{{ display_details.cycle }}</div>
<div class="detailItem">频次:{{ display_details.frequency }}</div>
<div v-if="display_details.begin_date" class="detailItem">开始时间:{{ display_details.begin_date }}</div>
<div v-if="display_details.end_date" class="detailItem">截止时间:{{ display_details.end_date }}</div>
<div v-if="display_details.attachment_type && display_details.attachment_type.length" class="detailItem">附件类型:{{ display_details.attachment_type
}}</div>
<div v-if="task_type === 'checkin'" class="detailItem">类型:打卡签到</div>
</div>
......@@ -304,6 +304,47 @@ const on_confirm_subtask = ({ selectedOptions }) => {
show_subtask_picker.value = false
}
const formatTaskDetails = (data) => {
const cycleMap = {
'0': '本周期',
'30': '每月',
'7': '每周',
'1': '每日'
}
const cycleText = cycleMap[data.cycle] || data.cycle || '每日'
let attachmentText = '文本'
if (Array.isArray(data.attachment_type)) {
const typeMap = {
'text': '文本',
'image': '图片',
'video': '视频',
'audio': '音频'
}
attachmentText = data.attachment_type.map(t => typeMap[t] || t).join('/')
} else if (data.attachment_type) {
attachmentText = data.attachment_type
}
return {
cycle: cycleText,
frequency: `每周期${data.frequency || 1}次`,
begin_date: data.begin_date ? dayjs(data.begin_date).format('YYYY-MM-DD') : '',
end_date: data.end_date ? dayjs(data.end_date).format('YYYY-MM-DD') : '',
attachment_type: attachmentText
}
}
const display_details = computed(() => {
if (selectedSubtaskId.value && subtask_list.value.length) {
const sub = subtask_list.value.find(item => item.id === selectedSubtaskId.value)
if (sub) {
return formatTaskDetails(sub)
}
}
return task_details.value
})
/**
* 获取作业详情和学生完成情况
*/
......@@ -322,38 +363,7 @@ const fetchData = async () => {
// 小作业列表
subtask_list.value = res.data.subtask_list || []
// 格式化周期显示
const cycleMap = {
'0': '本周期',
'30': '每月',
'7': '每周',
'1': '每日' // 假设 1 代表每日
}
// 如果后端返回的是数字字符串,尝试映射,否则直接显示
const cycleText = cycleMap[res.data.cycle] || res.data.cycle || '每日'
// 格式化附件类型
let attachmentText = '文本'
if (Array.isArray(res.data.attachment_type)) {
const typeMap = {
'text': '文本',
'image': '图片',
'video': '视频',
'audio': '音频'
}
attachmentText = res.data.attachment_type.map(t => typeMap[t] || t).join('/')
} else if (res.data.attachment_type) {
attachmentText = res.data.attachment_type
}
task_details.value = {
cycle: cycleText,
frequency: `每周期${res.data.frequency || 1}次`,
begin_date: res.data.begin_date ? dayjs(res.data.begin_date).format('YYYY-MM-DD') : '',
end_date: res.data.end_date ? dayjs(res.data.end_date).format('YYYY-MM-DD') : '',
// time_range: `${res.data.begin_date || '00:00'} ~ ${res.data.end_date || '23:59'}`, // 注意:API返回的begin_date/end_date可能是日期也可能是时间,这里暂且直接展示
attachment_type: attachmentText
}
task_details.value = formatTaskDetails(res.data)
user_list.value = res.data.user_list || []
......