feat(教师端): 优化作业详情显示和日历组件交互
重构作业详情显示逻辑,提取格式化函数复用 调整日历组件布局,增加打卡规则查看功能 修改任务筛选器宽度和对齐方式
Showing
3 changed files
with
128 additions
and
50 deletions
| 1 | <template> | 1 | <template> |
| 2 | - <div class="inline-block"> | 2 | + <div class="inline-block" style="width: 60%;"> |
| 3 | - <div @click="show = true" class="text-sm text-gray-500 flex items-center cursor-pointer ml-2"> | 3 | + <div @click="show = true" class="text-sm text-gray-500 flex items-center justify-end cursor-pointer ml-2"> |
| 4 | <span class="mr-1">{{ selectedLabel }}</span> | 4 | <span class="mr-1">{{ selectedLabel }}</span> |
| 5 | <van-icon name="arrow-down" /> | 5 | <van-icon name="arrow-down" /> |
| 6 | </div> | 6 | </div> | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2025-01-25 15:34:17 | 2 | * @Date: 2025-01-25 15:34:17 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-12-14 22:08:03 | 4 | + * @LastEditTime: 2025-12-18 22:06:48 |
| 5 | * @FilePath: /mlaj/src/components/ui/CollapsibleCalendar.vue | 5 | * @FilePath: /mlaj/src/components/ui/CollapsibleCalendar.vue |
| 6 | * @Description: 可折叠日历组件 | 6 | * @Description: 可折叠日历组件 |
| 7 | --> | 7 | --> |
| ... | @@ -20,15 +20,16 @@ | ... | @@ -20,15 +20,16 @@ |
| 20 | <div class="calendar-date-main">{{ formattedCurrentDate }}</div> | 20 | <div class="calendar-date-main">{{ formattedCurrentDate }}</div> |
| 21 | <div class="calendar-weekday">{{ formattedWeekday }}</div> | 21 | <div class="calendar-weekday">{{ formattedWeekday }}</div> |
| 22 | </div> --> | 22 | </div> --> |
| 23 | - <div class="calendar-title-wrapper" @click="expandCalendar"> | 23 | + <div class="calendar-title-wrapper"> |
| 24 | <div class="calendar-title">{{ title }}</div> | 24 | <div class="calendar-title">{{ title }}</div> |
| 25 | - <div class="calendar-subtitle">点击切换日期</div> | 25 | + <!-- <div class="text-xs text-gray-500 mt-1">点击查看打卡规则</div> --> |
| 26 | </div> | 26 | </div> |
| 27 | </div> | 27 | </div> |
| 28 | <div class="calendar-content"> | 28 | <div class="calendar-content"> |
| 29 | - <div class="calendar-date-display"> | 29 | + <div class="calendar-date-display" @click="expandCalendar"> |
| 30 | <div class="calendar-date-main">{{ formattedCurrentDate }}</div> | 30 | <div class="calendar-date-main">{{ formattedCurrentDate }}</div> |
| 31 | <div class="calendar-weekday">{{ formattedWeekday }}</div> | 31 | <div class="calendar-weekday">{{ formattedWeekday }}</div> |
| 32 | + <div class="text-xs text-gray-500 mt-1">点击切换日期</div> | ||
| 32 | </div> | 33 | </div> |
| 33 | <!-- <div class="calendar-action"> | 34 | <!-- <div class="calendar-action"> |
| 34 | <div class="calendar-action-text">指定日期</div> | 35 | <div class="calendar-action-text">指定日期</div> |
| ... | @@ -48,6 +49,7 @@ | ... | @@ -48,6 +49,7 @@ |
| 48 | </svg> | 49 | </svg> |
| 49 | </div> | 50 | </div> |
| 50 | </div> | 51 | </div> |
| 52 | + <div v-if="selectedSubtask" class="text-xs text-gray-500 mt-1 cursor-pointer hover:text-green-600 transition-colors" @click.stop="openRulesPopup">点击查看打卡规则</div> | ||
| 51 | </div> | 53 | </div> |
| 52 | </div> | 54 | </div> |
| 53 | </div> | 55 | </div> |
| ... | @@ -93,6 +95,25 @@ | ... | @@ -93,6 +95,25 @@ |
| 93 | :default-index="0" | 95 | :default-index="0" |
| 94 | /> | 96 | /> |
| 95 | </van-popup> | 97 | </van-popup> |
| 98 | + <!-- 打卡规则弹窗 --> | ||
| 99 | + <van-popup | ||
| 100 | + v-model:show="showRulesPopup" | ||
| 101 | + round | ||
| 102 | + position="bottom" | ||
| 103 | + closeable | ||
| 104 | + :style="{ maxHeight: '50%' }" | ||
| 105 | + > | ||
| 106 | + <div class="p-4"> | ||
| 107 | + <div class="text-lg font-bold mb-4 text-center text-gray-800">打卡规则</div> | ||
| 108 | + <div class="details text-sm text-gray-600 pl-4 border-l-4 border-green-500" v-if="formattedRules"> | ||
| 109 | + <div class="mb-2">周期:{{ formattedRules.cycle }}</div> | ||
| 110 | + <div class="mb-2">频次:{{ formattedRules.frequency }}</div> | ||
| 111 | + <div v-if="formattedRules.begin_date" class="mb-2">开始时间:{{ formattedRules.begin_date }}</div> | ||
| 112 | + <div v-if="formattedRules.end_date" class="mb-2">截止时间:{{ formattedRules.end_date }}</div> | ||
| 113 | + <div v-if="formattedRules.attachment_type" class="mb-2">附件类型:{{ formattedRules.attachment_type }}</div> | ||
| 114 | + </div> | ||
| 115 | + </div> | ||
| 116 | + </van-popup> | ||
| 96 | </div> | 117 | </div> |
| 97 | </template> | 118 | </template> |
| 98 | 119 | ||
| ... | @@ -131,6 +152,9 @@ const currentDate = ref(props.modelValue || new Date()) | ... | @@ -131,6 +152,9 @@ const currentDate = ref(props.modelValue || new Date()) |
| 131 | // 作业筛选相关 | 152 | // 作业筛选相关 |
| 132 | const showCoursePicker = ref(false) | 153 | const showCoursePicker = ref(false) |
| 133 | const selectedCourseText = ref('全部作业') | 154 | const selectedCourseText = ref('全部作业') |
| 155 | +const selectedCourseId = ref('') | ||
| 156 | +const showRulesPopup = ref(false) | ||
| 157 | + | ||
| 134 | const courseColumns = computed(() => { | 158 | const courseColumns = computed(() => { |
| 135 | return [ | 159 | return [ |
| 136 | { text: '全部作业', value: '' }, | 160 | { text: '全部作业', value: '' }, |
| ... | @@ -141,14 +165,58 @@ const courseColumns = computed(() => { | ... | @@ -141,14 +165,58 @@ const courseColumns = computed(() => { |
| 141 | ] | 165 | ] |
| 142 | }) | 166 | }) |
| 143 | 167 | ||
| 168 | +const selectedSubtask = computed(() => { | ||
| 169 | + if (!selectedCourseId.value) return null | ||
| 170 | + return props.subtaskList.find(item => item.id === selectedCourseId.value) | ||
| 171 | +}) | ||
| 172 | + | ||
| 173 | +const formattedRules = computed(() => { | ||
| 174 | + if (!selectedSubtask.value) return null | ||
| 175 | + const data = selectedSubtask.value | ||
| 176 | + | ||
| 177 | + const cycleMap = { | ||
| 178 | + '0': '本周期', | ||
| 179 | + '30': '每月', | ||
| 180 | + '7': '每周', | ||
| 181 | + '1': '每日' | ||
| 182 | + } | ||
| 183 | + const cycleText = cycleMap[data.cycle] || data.cycle || '每日' | ||
| 184 | + | ||
| 185 | + let attachmentText = '文本' | ||
| 186 | + if (Array.isArray(data.attachment_type)) { | ||
| 187 | + const typeMap = { | ||
| 188 | + 'text': '文本', | ||
| 189 | + 'image': '图片', | ||
| 190 | + 'video': '视频', | ||
| 191 | + 'audio': '音频' | ||
| 192 | + } | ||
| 193 | + attachmentText = data.attachment_type.map(t => typeMap[t] || t).join('/') | ||
| 194 | + } else if (data.attachment_type) { | ||
| 195 | + attachmentText = data.attachment_type | ||
| 196 | + } | ||
| 197 | + | ||
| 198 | + return { | ||
| 199 | + cycle: cycleText, | ||
| 200 | + frequency: `每周期${data.frequency || 1}次`, | ||
| 201 | + begin_date: data.begin_date ? dayjs(data.begin_date).format('YYYY-MM-DD') : '', | ||
| 202 | + end_date: data.end_date ? dayjs(data.end_date).format('YYYY-MM-DD') : '', | ||
| 203 | + attachment_type: attachmentText | ||
| 204 | + } | ||
| 205 | +}) | ||
| 206 | + | ||
| 144 | const openCoursePicker = () => { | 207 | const openCoursePicker = () => { |
| 145 | showCoursePicker.value = true | 208 | showCoursePicker.value = true |
| 146 | } | 209 | } |
| 147 | 210 | ||
| 211 | +const openRulesPopup = () => { | ||
| 212 | + showRulesPopup.value = true | ||
| 213 | +} | ||
| 214 | + | ||
| 148 | const onConfirmCourse = (result) => { | 215 | const onConfirmCourse = (result) => { |
| 149 | // 兼容 Vant 3/4 | 216 | // 兼容 Vant 3/4 |
| 150 | const option = result.selectedOptions ? result.selectedOptions[0] : result | 217 | const option = result.selectedOptions ? result.selectedOptions[0] : result |
| 151 | selectedCourseText.value = option.text | 218 | selectedCourseText.value = option.text |
| 219 | + selectedCourseId.value = option.value | ||
| 152 | showCoursePicker.value = false | 220 | showCoursePicker.value = false |
| 153 | emit('select-course', option.value) | 221 | emit('select-course', option.value) |
| 154 | } | 222 | } |
| ... | @@ -298,12 +366,6 @@ defineExpose({ | ... | @@ -298,12 +366,6 @@ defineExpose({ |
| 298 | // text-overflow: ellipsis; | 366 | // text-overflow: ellipsis; |
| 299 | // white-space: nowrap; | 367 | // white-space: nowrap; |
| 300 | } | 368 | } |
| 301 | - | ||
| 302 | - .calendar-subtitle { | ||
| 303 | - font-size: 12px; | ||
| 304 | - color: #7f8c8d; | ||
| 305 | - font-weight: 400; | ||
| 306 | - } | ||
| 307 | } | 369 | } |
| 308 | 370 | ||
| 309 | .calendar-date-display { | 371 | .calendar-date-display { |
| ... | @@ -322,6 +384,12 @@ defineExpose({ | ... | @@ -322,6 +384,12 @@ defineExpose({ |
| 322 | color: #4caf50; | 384 | color: #4caf50; |
| 323 | font-weight: 500; | 385 | font-weight: 500; |
| 324 | } | 386 | } |
| 387 | + | ||
| 388 | + .calendar-subtitle { | ||
| 389 | + font-size: 12px; | ||
| 390 | + color: #7f8c8d; | ||
| 391 | + font-weight: 400; | ||
| 392 | + } | ||
| 325 | } | 393 | } |
| 326 | } | 394 | } |
| 327 | 395 | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2025-11-19 21:00:00 | 2 | * @Date: 2025-11-19 21:00:00 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-12-15 18:18:51 | 4 | + * @LastEditTime: 2025-12-18 21:54:18 |
| 5 | * @FilePath: /mlaj/src/views/teacher/taskHomePage.vue | 5 | * @FilePath: /mlaj/src/views/teacher/taskHomePage.vue |
| 6 | * @Description: 教师端作业主页(头部介绍、统计、日历与学生完成情况) | 6 | * @Description: 教师端作业主页(头部介绍、统计、日历与学生完成情况) |
| 7 | --> | 7 | --> |
| ... | @@ -27,11 +27,11 @@ | ... | @@ -27,11 +27,11 @@ |
| 27 | 27 | ||
| 28 | <div class="intro text-base text-gray-700 leading-relaxed mb-3" v-html="task_intro"></div> | 28 | <div class="intro text-base text-gray-700 leading-relaxed mb-3" v-html="task_intro"></div> |
| 29 | <div class="details text-sm text-gray-600" v-if="!subtask_list.length || selectedSubtaskId"> | 29 | <div class="details text-sm text-gray-600" v-if="!subtask_list.length || selectedSubtaskId"> |
| 30 | - <div class="detailItem">周期:{{ task_details.cycle }}</div> | 30 | + <div class="detailItem">周期:{{ display_details.cycle }}</div> |
| 31 | - <div class="detailItem">频次:{{ task_details.frequency }}</div> | 31 | + <div class="detailItem">频次:{{ display_details.frequency }}</div> |
| 32 | - <div v-if="task_details.begin_date" class="detailItem">开始时间:{{ task_details.begin_date }}</div> | 32 | + <div v-if="display_details.begin_date" class="detailItem">开始时间:{{ display_details.begin_date }}</div> |
| 33 | - <div v-if="task_details.end_date" class="detailItem">截止时间:{{ task_details.end_date }}</div> | 33 | + <div v-if="display_details.end_date" class="detailItem">截止时间:{{ display_details.end_date }}</div> |
| 34 | - <div v-if="task_details.attachment_type.length" class="detailItem">附件类型:{{ task_details.attachment_type | 34 | + <div v-if="display_details.attachment_type && display_details.attachment_type.length" class="detailItem">附件类型:{{ display_details.attachment_type |
| 35 | }}</div> | 35 | }}</div> |
| 36 | <div v-if="task_type === 'checkin'" class="detailItem">类型:打卡签到</div> | 36 | <div v-if="task_type === 'checkin'" class="detailItem">类型:打卡签到</div> |
| 37 | </div> | 37 | </div> |
| ... | @@ -304,6 +304,47 @@ const on_confirm_subtask = ({ selectedOptions }) => { | ... | @@ -304,6 +304,47 @@ const on_confirm_subtask = ({ selectedOptions }) => { |
| 304 | show_subtask_picker.value = false | 304 | show_subtask_picker.value = false |
| 305 | } | 305 | } |
| 306 | 306 | ||
| 307 | +const formatTaskDetails = (data) => { | ||
| 308 | + const cycleMap = { | ||
| 309 | + '0': '本周期', | ||
| 310 | + '30': '每月', | ||
| 311 | + '7': '每周', | ||
| 312 | + '1': '每日' | ||
| 313 | + } | ||
| 314 | + const cycleText = cycleMap[data.cycle] || data.cycle || '每日' | ||
| 315 | + | ||
| 316 | + let attachmentText = '文本' | ||
| 317 | + if (Array.isArray(data.attachment_type)) { | ||
| 318 | + const typeMap = { | ||
| 319 | + 'text': '文本', | ||
| 320 | + 'image': '图片', | ||
| 321 | + 'video': '视频', | ||
| 322 | + 'audio': '音频' | ||
| 323 | + } | ||
| 324 | + attachmentText = data.attachment_type.map(t => typeMap[t] || t).join('/') | ||
| 325 | + } else if (data.attachment_type) { | ||
| 326 | + attachmentText = data.attachment_type | ||
| 327 | + } | ||
| 328 | + | ||
| 329 | + return { | ||
| 330 | + cycle: cycleText, | ||
| 331 | + frequency: `每周期${data.frequency || 1}次`, | ||
| 332 | + begin_date: data.begin_date ? dayjs(data.begin_date).format('YYYY-MM-DD') : '', | ||
| 333 | + end_date: data.end_date ? dayjs(data.end_date).format('YYYY-MM-DD') : '', | ||
| 334 | + attachment_type: attachmentText | ||
| 335 | + } | ||
| 336 | +} | ||
| 337 | + | ||
| 338 | +const display_details = computed(() => { | ||
| 339 | + if (selectedSubtaskId.value && subtask_list.value.length) { | ||
| 340 | + const sub = subtask_list.value.find(item => item.id === selectedSubtaskId.value) | ||
| 341 | + if (sub) { | ||
| 342 | + return formatTaskDetails(sub) | ||
| 343 | + } | ||
| 344 | + } | ||
| 345 | + return task_details.value | ||
| 346 | +}) | ||
| 347 | + | ||
| 307 | /** | 348 | /** |
| 308 | * 获取作业详情和学生完成情况 | 349 | * 获取作业详情和学生完成情况 |
| 309 | */ | 350 | */ |
| ... | @@ -322,38 +363,7 @@ const fetchData = async () => { | ... | @@ -322,38 +363,7 @@ const fetchData = async () => { |
| 322 | // 小作业列表 | 363 | // 小作业列表 |
| 323 | subtask_list.value = res.data.subtask_list || [] | 364 | subtask_list.value = res.data.subtask_list || [] |
| 324 | 365 | ||
| 325 | - // 格式化周期显示 | 366 | + task_details.value = formatTaskDetails(res.data) |
| 326 | - const cycleMap = { | ||
| 327 | - '0': '本周期', | ||
| 328 | - '30': '每月', | ||
| 329 | - '7': '每周', | ||
| 330 | - '1': '每日' // 假设 1 代表每日 | ||
| 331 | - } | ||
| 332 | - // 如果后端返回的是数字字符串,尝试映射,否则直接显示 | ||
| 333 | - const cycleText = cycleMap[res.data.cycle] || res.data.cycle || '每日' | ||
| 334 | - | ||
| 335 | - // 格式化附件类型 | ||
| 336 | - let attachmentText = '文本' | ||
| 337 | - if (Array.isArray(res.data.attachment_type)) { | ||
| 338 | - const typeMap = { | ||
| 339 | - 'text': '文本', | ||
| 340 | - 'image': '图片', | ||
| 341 | - 'video': '视频', | ||
| 342 | - 'audio': '音频' | ||
| 343 | - } | ||
| 344 | - attachmentText = res.data.attachment_type.map(t => typeMap[t] || t).join('/') | ||
| 345 | - } else if (res.data.attachment_type) { | ||
| 346 | - attachmentText = res.data.attachment_type | ||
| 347 | - } | ||
| 348 | - | ||
| 349 | - task_details.value = { | ||
| 350 | - cycle: cycleText, | ||
| 351 | - frequency: `每周期${res.data.frequency || 1}次`, | ||
| 352 | - begin_date: res.data.begin_date ? dayjs(res.data.begin_date).format('YYYY-MM-DD') : '', | ||
| 353 | - end_date: res.data.end_date ? dayjs(res.data.end_date).format('YYYY-MM-DD') : '', | ||
| 354 | - // time_range: `${res.data.begin_date || '00:00'} ~ ${res.data.end_date || '23:59'}`, // 注意:API返回的begin_date/end_date可能是日期也可能是时间,这里暂且直接展示 | ||
| 355 | - attachment_type: attachmentText | ||
| 356 | - } | ||
| 357 | 367 | ||
| 358 | user_list.value = res.data.user_list || [] | 368 | user_list.value = res.data.user_list || [] |
| 359 | 369 | ... | ... |
-
Please register or login to post a comment