86 lines
7.8 KiB
Plaintext
86 lines
7.8 KiB
Plaintext
<template>
|
|
<view class="page-container"><view class="header-panel"><view class="header-copy"><text class="header-title">作业任务</text><text class="header-subtitle">{{ tasks.studentName }} · 待完成 {{ tasks.pendingCount }} 个</text></view><view class="header-action"><UniIcons type="person-filled" class="header-action-icon"></UniIcons></view></view><view class="filter-panel"><view v-for="filter in filters" :key="filter.id" class="filter-tab" :class="activeFilter === filter.id ? 'filter-tab-active' : ''" @tap="setFilter(filter.id)"><text class="filter-text" :class="activeFilter === filter.id ? 'filter-text-active' : ''">{{ filter.name }}</text></view></view><scroll-view class="content-scroll" direction="vertical" scroll-y="true" refresher-enabled="true" :refresher-triggered="isRefreshing" @refresherrefresh="refreshTasks"><view v-if="isLoading" class="status-panel"><text class="status-text">任务加载中</text></view><view v-else class="content-panel"><view v-for="group in visibleGroups" :key="group.id" class="group-card"><view class="group-heading"><view class="group-tags"><text class="subject-tag">{{ group.subject }}</text><text class="unit-tag">{{ group.unit }}</text><text class="lesson-title">{{ group.lesson }}</text></view><text class="knowledge-count">{{ group.knowledgeCount > 0 ? group.knowledgeCount + ' 个知识点' : '暂无学习内容' }}</text></view><text class="lesson-time">● {{ group.startText }}-{{ group.endText }}</text><view v-if="group.note" class="group-note"><text class="group-note-icon">i</text><text class="group-note-text">{{ group.note }}</text></view><view v-for="task in group.tasks" :key="task.id" v-show="matchesFilter(task)" class="detail-task"><view class="detail-task-heading"><text class="detail-task-title">{{ task.title }}</text><text class="detail-task-status" :class="'detail-task-status-' + task.status">{{ task.statusText }}</text></view><text class="detail-task-result" :class="'detail-task-result-' + task.status">● {{ task.statusText }} · 共 {{ task.status === 'success' ? '2' : '3' }} 次</text><text v-if="task.accuracyText" class="detail-task-meta">{{ task.accuracyText }} · {{ task.answerText }} · {{ task.deadlineText }}</text></view></view><view v-if="visibleGroups.length === 0" class="empty-panel"><text class="empty-text">暂无对应任务</text></view></view></scroll-view><MainTabBar active-tab="tasks"></MainTabBar></view>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import MainTabBar from '../../components/main-tab-bar.uvue'
|
|
import UniIcons from '../../uni_modules/uni-icons-x/components/uni-icons/uni-icons.uvue'
|
|
import { apiGetStudentHomeProfile, apiGetStudentLearningTasks, type LearningTaskItem, type StudentLearningTasks } from '../../api/student'
|
|
import { homeStudentProfile, setHomeStudentProfile } from '../../store/home'
|
|
|
|
const filters = [{ id: 'all', name: '全部' }, { id: 'pending', name: '待完成' }, { id: 'completed', name: '已完成' }, { id: 'failed', name: '未通过' }]
|
|
const activeFilter = ref('all')
|
|
const isLoading = ref(true)
|
|
const isRefreshing = ref(false)
|
|
const tasks = ref<StudentLearningTasks>({ studentId: '', studentName: '', studentGrade: '', studentClass: '', pendingCount: 0, completedCount: 0, failedCount: 0, groups: [] })
|
|
const visibleGroups = computed(() => tasks.value.groups.filter((group) => group.tasks.length === 0 || group.tasks.some((task) => matchesFilter(task))))
|
|
|
|
onLoad(() => { loadTasks() })
|
|
|
|
async function loadTasks() {
|
|
let studentId = homeStudentProfile.value?.id ?? ''
|
|
if (!studentId) {
|
|
const profileRes = await apiGetStudentHomeProfile(undefined, false)
|
|
if (profileRes && profileRes.data) { setHomeStudentProfile(profileRes.data); studentId = profileRes.data.id }
|
|
}
|
|
if (studentId) { const res = await apiGetStudentLearningTasks(studentId); if (res && res.data) tasks.value = res.data }
|
|
isLoading.value = false
|
|
}
|
|
|
|
async function refreshTasks() {
|
|
isRefreshing.value = true
|
|
await loadTasks()
|
|
isRefreshing.value = false
|
|
}
|
|
|
|
function setFilter(filter: string) { activeFilter.value = filter }
|
|
function matchesFilter(task: LearningTaskItem): boolean {
|
|
if (activeFilter.value === 'all') return true
|
|
if (activeFilter.value === 'pending') return task.status === 'not_started' || task.status === 'in_progress'
|
|
if (activeFilter.value === 'completed') return task.status === 'success'
|
|
return task.status === 'failed'
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.page-container { display: flex; flex-direction: column; flex: 1; background-color: #f5f7fb; }
|
|
.header-panel { display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding: 16px 20px; background-color: #ffffff; border-bottom: 1px solid #edf0f5; }
|
|
.header-copy { display: flex; flex-direction: column; }
|
|
.header-title { font-size: 18px; font-weight: 700; color: #242b3d; }
|
|
.header-subtitle { margin-top: 4px; font-size: 11px; color: #8992a5; }
|
|
.header-action { display: flex; flex-direction: column; align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: 18px; background-color: #f0f1ff; }
|
|
.header-action-icon { font-size: 18px; color: #554be9; }
|
|
.filter-panel { display: flex; flex-direction: row; padding: 12px 20px; background-color: #ffffff; }
|
|
.filter-tab { display: flex; flex-direction: column; align-items: center; flex: 1; margin-right: 8px; padding: 7px 0; border-radius: 16px; text-align: center; background-color: #f1f4f8; }
|
|
.filter-tab-active { background-color: #554be9; }
|
|
.filter-text { font-size: 11px; color: #64748b; }
|
|
.filter-text-active { color: #ffffff; }
|
|
.content-scroll { flex: 1; }
|
|
.content-panel { display: flex; flex-direction: column; padding: 14px 16px 24px; }
|
|
.status-panel, .empty-panel { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 60px 0; }
|
|
.status-text, .empty-text { font-size: 13px; color: #8992a5; }
|
|
.group-card { display: flex; flex-direction: column; margin-bottom: 12px; padding: 16px; border-radius: 15px; background-color: #ffffff; }
|
|
.group-heading { display: flex; flex-direction: row; align-items: center; justify-content: space-between; }
|
|
.group-tags { display: flex; flex-direction: row; align-items: center; flex: 1; }
|
|
.subject-tag, .unit-tag { padding: 3px 7px; margin-right: 6px; border-radius: 8px; font-size: 10px; color: #554be9; background-color: #eeefff; }
|
|
.unit-tag { color: #1598dc; background-color: #e6f5ff; }
|
|
.lesson-title { font-size: 13px; font-weight: 700; color: #242b3d; }
|
|
.knowledge-count { flex-shrink: 0; font-size: 10px; color: #8992a5; }
|
|
.lesson-time { margin-top: 7px; font-size: 11px; color: #8ca0bd; }
|
|
.group-note { display: flex; flex-direction: row; align-items: flex-start; margin-top: 12px; padding: 12px; border-radius: 12px; background-color: #f6f8fb; }
|
|
.group-note-icon { width: 12px; height: 12px; margin-right: 7px; border-radius: 6px; text-align: center; font-size: 9px; color: #ffffff; background-color: #9aa8bc; }
|
|
.group-note-text { flex: 1; font-size: 11px; line-height: 18px; color: #68778b; }
|
|
.detail-task { display: flex; flex-direction: column; margin-top: 10px; padding: 12px; border: 1px solid #edf0f5; border-radius: 12px; background-color: #f9fbfd; }
|
|
.detail-task-heading { display: flex; flex-direction: row; justify-content: space-between; }
|
|
.detail-task-title { font-size: 13px; color: #242b3d; }
|
|
.detail-task-status { padding: 3px 7px; border-radius: 8px; font-size: 10px; }
|
|
.detail-task-status-success { color: #16a979; background-color: #e8fbf3; }
|
|
.detail-task-status-in_progress { color: #5147e9; background-color: #eeefff; }
|
|
.detail-task-status-failed { color: #f34d54; background-color: #fff0f1; }
|
|
.detail-task-result { margin-top: 8px; font-size: 11px; }
|
|
.detail-task-result-success { color: #16a979; }
|
|
.detail-task-result-in_progress { color: #5147e9; }
|
|
.detail-task-result-failed { color: #f34d54; }
|
|
.detail-task-meta { margin-top: 8px; padding-top: 8px; border-top: 1px solid #e4eaf1; font-size: 10px; color: #8992a5; }
|
|
</style>
|