157 lines
10 KiB
Plaintext
157 lines
10 KiB
Plaintext
<template>
|
||
<view class="page-container">
|
||
<view class="header-panel">
|
||
<view class="header-back" @tap="goBack"><UniIcons type="back" class="header-back-icon"></UniIcons></view>
|
||
<view class="header-copy"><text class="header-title">今日学习</text><text class="header-subtitle">{{ detail.dateText }} {{ homeStudentProfile?.name ?? detail.studentName }} · {{ homeStudentProfile?.school ?? '' }} {{ homeStudentProfile?.className ?? '' }}</text></view>
|
||
<view class="header-action" @tap="goBack"><UniIcons type="home-filled" class="header-action-icon"></UniIcons></view>
|
||
</view>
|
||
|
||
<scroll-view class="content-scroll" direction="vertical" scroll-y="true">
|
||
<view v-if="isLoading" class="status-panel"><text class="status-text">今日学习信息加载中</text></view>
|
||
<view v-else class="content-panel">
|
||
<view class="overview-card">
|
||
<view class="overview-header"><text class="section-title">今日学习概览</text><text class="checkin-status" :class="detail.checkedIn ? 'checkin-complete' : 'checkin-pending'">{{ detail.checkedIn ? '已打卡' : '未打卡' }}</text></view>
|
||
<view class="metrics-grid">
|
||
<view class="metric-card"><text class="metric-label">学习时长</text><text class="metric-value">{{ detail.learningMinutes }} 分钟</text></view>
|
||
<view class="metric-card"><text class="metric-label">答题时长</text><text class="metric-value">{{ detail.answerMinutes }} 分钟</text></view>
|
||
<view class="metric-card"><text class="metric-label">答题数量</text><text class="metric-value metric-blue">{{ detail.answerCount }} 题</text></view>
|
||
<view class="metric-card"><text class="metric-label">完成学习包</text><text class="metric-value metric-purple">{{ detail.completedPackages }} / {{ detail.totalPackages }}</text></view>
|
||
<view class="metric-card"><text class="metric-label">攻克知识点</text><text class="metric-value metric-green">{{ detail.masteredPoints }} 个</text></view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="courses-card">
|
||
<text class="section-title">今日课表与每课打卡</text>
|
||
<view class="course-list">
|
||
<view v-for="course in detail.courses" :key="course.id" class="course-item">
|
||
<text class="course-time">{{ course.time }}</text>
|
||
<view class="course-copy"><text class="course-subject">{{ course.subject }}</text><text class="course-content">{{ course.content }}</text></view>
|
||
<text class="course-status" :class="course.checkedIn ? 'course-complete' : 'course-pending'">{{ course.checkedIn ? '已打卡' : '未打卡' }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="score-card">
|
||
<text class="section-title">正确率(对比昨日)</text>
|
||
<text class="score-description">整体正确率较昨日全部答题目统计,与昨日对比</text>
|
||
<view class="score-row"><text class="score-label">整体正确率</text><view class="score-values"><text class="score-value">{{ detail.score.accuracy }}%</text><text class="score-change">+{{ detail.score.change }}%</text></view></view>
|
||
<text class="score-yesterday">昨日 {{ detail.score.yesterdayAccuracy }}%</text>
|
||
<view class="score-track"><view class="score-progress" :style="{ width: detail.score.accuracy + '%' }"></view></view>
|
||
</view>
|
||
|
||
<view class="points-card">
|
||
<view class="points-header"><text class="section-title">今日获得积分</text><text class="points-link">积分明细 ›</text></view>
|
||
<view class="points-total"><text class="points-number">+{{ detail.pointsTotal }}</text><text class="points-unit">积分</text></view>
|
||
<view class="points-list">
|
||
<view v-for="item in detail.pointDetails" :key="item.id" class="point-item"><text class="point-label">{{ item.label }}</text><text class="point-value">+{{ item.points }}</text></view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
<MainTabBar active-tab="home"></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 { apiGetTodayLearningDetail, type TodayLearningDetail } from '../../api/student'
|
||
import { homeStudentProfile, selectedStudentChangeVersion } from '../../store/home'
|
||
|
||
const isLoading = ref(true)
|
||
const detail = ref<TodayLearningDetail>({
|
||
studentId: '', studentName: '', dateText: '', checkedIn: false, learningMinutes: 0, answerMinutes: 0,
|
||
answerCount: 0, completedPackages: 0, totalPackages: 0, masteredPoints: 0, courses: [],
|
||
score: { accuracy: 0, yesterdayAccuracy: 0, change: 0 }, pointsTotal: 0, pointDetails: []
|
||
})
|
||
const observedStudentVersion = ref(selectedStudentChangeVersion.value)
|
||
|
||
onLoad(() => {
|
||
loadTodayLearning()
|
||
})
|
||
|
||
onShow(() => {
|
||
if (observedStudentVersion.value !== selectedStudentChangeVersion.value) {
|
||
observedStudentVersion.value = selectedStudentChangeVersion.value
|
||
loadTodayLearning()
|
||
}
|
||
})
|
||
|
||
async function loadTodayLearning() {
|
||
isLoading.value = true
|
||
const studentId = homeStudentProfile.value?.id ?? ''
|
||
if (!studentId) {
|
||
isLoading.value = false
|
||
return
|
||
}
|
||
const res = await apiGetTodayLearningDetail(studentId)
|
||
if (res && res.data) {
|
||
detail.value = res.data
|
||
}
|
||
isLoading.value = false
|
||
}
|
||
|
||
function goBack() {
|
||
uni.navigateBack()
|
||
}
|
||
</script>
|
||
|
||
<style>
|
||
.page-container { display: flex; flex-direction: column; flex: 1; background-color: #f5f7fb; }
|
||
.header-panel { display: flex; flex-direction: row; align-items: center; padding: 12px 18px; background-color: #ffffff; }
|
||
.header-back { display: flex; flex-direction: column; align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: 18px; background-color: #f1f4f8; }
|
||
.header-back-icon { font-size: 18px; color: #66758d; }
|
||
.header-copy { display: flex; flex-direction: column; flex: 1; margin-left: 12px; }
|
||
.header-title { font-size: 17px; font-weight: 700; color: #22283a; }
|
||
.header-subtitle { margin-top: 3px; font-size: 12px; 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: #6558e9; }
|
||
.content-scroll { flex: 1; }
|
||
.status-panel { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 260px; }
|
||
.status-text { font-size: 13px; color: #8992a5; }
|
||
.content-panel { display: flex; flex-direction: column; padding: 16px 18px 24px; }
|
||
.overview-card { display: flex; flex-direction: column; padding: 16px; border-radius: 8px; background-color: #ffffff; }
|
||
.overview-header { display: flex; flex-direction: row; align-items: center; justify-content: space-between; }
|
||
.section-title { font-size: 16px; font-weight: 700; color: #242b3d; }
|
||
.checkin-status { padding: 4px 10px; border-radius: 12px; font-size: 11px; }
|
||
.checkin-complete { color: #00a66a; background-color: #e9fbf3; }
|
||
.checkin-pending { color: #f08a1a; background-color: #fff4e5; }
|
||
.metrics-grid { display: flex; flex-direction: row; flex-wrap: wrap; justify-content: space-between; margin-top: 14px; }
|
||
.metric-card { display: flex; flex-direction: column; width: 48%; margin-bottom: 10px; padding: 12px; border-radius: 8px; background-color: #f6f8fb; }
|
||
.metric-label { font-size: 11px; color: #8992a5; }
|
||
.metric-value { margin-top: 6px; font-size: 15px; font-weight: 700; color: #20283a; }
|
||
.metric-blue { color: #008ddb; }
|
||
.metric-purple { color: #6558e9; }
|
||
.metric-green { color: #00a66a; }
|
||
.courses-card { display: flex; flex-direction: column; margin-top: 16px; padding: 16px; border-radius: 8px; background-color: #ffffff; }
|
||
.course-list { display: flex; flex-direction: column; margin-top: 14px; }
|
||
.course-item { display: flex; flex-direction: row; align-items: center; min-height: 58px; margin-bottom: 10px; padding: 10px 12px; border-radius: 8px; background-color: #f6f8fb; }
|
||
.course-time { width: 56px; font-size: 12px; color: #596275; }
|
||
.course-copy { display: flex; flex-direction: column; flex: 1; }
|
||
.course-subject { font-size: 13px; font-weight: 600; color: #2a3042; }
|
||
.course-content { margin-top: 4px; font-size: 12px; color: #596275; }
|
||
.course-status { padding: 4px 7px; border-radius: 10px; font-size: 10px; }
|
||
.course-complete { color: #00a66a; background-color: #e9fbf3; }
|
||
.course-pending { color: #f08a1a; background-color: #fff4e5; }
|
||
.score-card { display: flex; flex-direction: column; margin-top: 16px; padding: 16px; border-radius: 8px; background-color: #ffffff; }
|
||
.score-description { margin-top: 6px; font-size: 11px; color: #8992a5; }
|
||
.score-row { display: flex; flex-direction: row; align-items: center; justify-content: space-between; margin-top: 14px; }
|
||
.score-label { font-size: 12px; color: #596275; }
|
||
.score-values { display: flex; flex-direction: row; align-items: center; }
|
||
.score-value { font-size: 13px; font-weight: 700; color: #20283a; }
|
||
.score-change { margin-left: 8px; font-size: 12px; color: #00a66a; }
|
||
.score-yesterday { margin-top: 6px; font-size: 12px; color: #8992a5; }
|
||
.score-track { height: 8px; margin-top: 8px; border-radius: 4px; background-color: #edf1f7; }
|
||
.score-progress { height: 8px; border-radius: 4px; background-color: #7659ee; }
|
||
.points-card { display: flex; flex-direction: column; margin-top: 16px; padding: 16px; border-radius: 8px; background-color: #ffffff; }
|
||
.points-header { display: flex; flex-direction: row; align-items: center; justify-content: space-between; }
|
||
.points-link { font-size: 12px; color: #514ce8; }
|
||
.points-total { display: flex; flex-direction: row; align-items: baseline; margin-top: 10px; }
|
||
.points-number { font-size: 24px; font-weight: 700; color: #ee9500; }
|
||
.points-unit { margin-left: 5px; font-size: 12px; color: #8992a5; }
|
||
.points-list { display: flex; flex-direction: column; margin-top: 12px; }
|
||
.point-item { display: flex; flex-direction: row; justify-content: space-between; margin-bottom: 8px; padding: 10px 12px; border-radius: 8px; background-color: #f6f8fb; }
|
||
.point-label { font-size: 12px; color: #596275; }
|
||
.point-value { font-size: 12px; font-weight: 600; color: #ee9500; }
|
||
</style>
|