From ed134f0e994f94cbff5a253b3f04e9932afa91dd Mon Sep 17 00:00:00 2001 From: hexi <1056646104@qq.com> Date: Tue, 10 Nov 2026 11:25:04 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=8A=E6=97=A5=E5=AD=A6=E4=B9=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/student.ts | 150 +++++++++++++++++++++++++- pages/today-learning/index.uvue | 185 ++++++++++++++++++++++++++++---- utils/request.ts | 2 +- 3 files changed, 312 insertions(+), 25 deletions(-) diff --git a/api/student.ts b/api/student.ts index f9b4ec2..0b2db36 100644 --- a/api/student.ts +++ b/api/student.ts @@ -71,13 +71,16 @@ export type TodayLearningCourse = { time: string subject: string content: string + stageName: string checkedIn: boolean + statusText: string } export type TodayLearningScore = { - accuracy: number - yesterdayAccuracy: number - change: number + accuracy: number | null + yesterdayAccuracy: number | null + change: number | null + hasYesterdayData: boolean } export type TodayLearningPoint = { @@ -86,6 +89,64 @@ export type TodayLearningPoint = { points: number } +/** 接口原始结构:今日学习概览 */ +export type ParentTodayLearningOverview = { + StudyDurationMinute: number + AnswerDurationMinute: number + AnswerQuestionCount: number + CompletedPackCount: number + TotalPackCount: number + OvercomeKnowledgePointCount: number + IsCheckedIn: boolean + CheckInStatusName: string +} + +/** 接口原始结构:今日课节与打卡 */ +export type ParentTodayCourseItem = { + CourseId: string + BaseSubjectId: string + BaseSubjectName: string + CourseName: string + StudyStage: number + StudyStageName: string + PlanStartTime: string + PlanEndTime: string + TimeText: string + IsCheckedIn: boolean + CheckInStatusName: string +} + +/** 接口原始结构:正确率对比 */ +export type ParentTodayAccuracyComparison = { + TodayAccuracyRate: number | null + YesterdayAccuracyRate: number | null + AccuracyRateDiff: number | null + HasYesterdayData: boolean +} + +/** 接口原始结构:积分明细 */ +export type ParentTodayPointItem = { + Type: number + Name: string + Points: number +} + +/** 接口原始结构:今日获得积分 */ +export type ParentTodayPointResult = { + TotalPoints: number + Items: ParentTodayPointItem[] +} + +/** 接口原始结构:今日学习情况 */ +export type ParentTodayLearningSituationResult = { + UserId: string + Date: string + Overview: ParentTodayLearningOverview + Courses: ParentTodayCourseItem[] + Accuracy: ParentTodayAccuracyComparison + Points: ParentTodayPointResult +} + export type StudyReportSubject = { id: string name: string @@ -200,6 +261,7 @@ export type TodayLearningDetail = { studentName: string dateText: string checkedIn: boolean + checkInStatusName: string learningMinutes: number answerMinutes: number answerCount: number @@ -491,10 +553,88 @@ export function apiGetStudentLearningWarnings(studentId: string, showLoading: bo } /** - * 获取学生今日学习详情及课程打卡记录。 + * 获取学生今日学习情况。 + * 接口字段为 PascalCase,这里统一转换为页面使用的驼峰结构。 */ export function apiGetTodayLearningDetail(studentId: string, showLoading: boolean = true): Promise | null> { - return get(`/api/parent/today-learning?studentId=${studentId}`, undefined, { showLoading }) + return get(`/api/v1/ParentLearningSituation/GetToday?userId=${encodeURIComponent(studentId)}`, undefined, { showLoading }).then((res) => { + if (!res || !res.data) return null + const data = res.data + const overview = data.Overview + const accuracy = data.Accuracy + const points = data.Points + const courses: TodayLearningCourse[] = (Array.isArray(data.Courses) ? data.Courses : []).map((course: ParentTodayCourseItem) => { + const checkedIn = Boolean(course.IsCheckedIn) + return { + id: String(course.CourseId ?? ''), + time: course.TimeText || formatTodayLearningTime(course.PlanStartTime), + subject: course.BaseSubjectName || '', + content: course.CourseName || '', + stageName: course.StudyStageName || '', + checkedIn, + statusText: course.CheckInStatusName || (checkedIn ? '已打卡' : '未打卡') + } + }) + const pointDetails: TodayLearningPoint[] = (Array.isArray(points?.Items) ? points.Items : []).map((item: ParentTodayPointItem) => ({ + id: String(item.Type ?? ''), + label: item.Name || '其他积分', + points: Number(item.Points) || 0 + })) + return { + code: res.code, + message: res.message, + data: { + studentId: String(data.UserId ?? studentId), + studentName: '', + dateText: formatTodayLearningDateText(data.Date), + checkedIn: Boolean(overview?.IsCheckedIn), + checkInStatusName: overview?.CheckInStatusName || (overview?.IsCheckedIn ? '已打卡' : '未打卡'), + learningMinutes: Number(overview?.StudyDurationMinute) || 0, + answerMinutes: Number(overview?.AnswerDurationMinute) || 0, + answerCount: Number(overview?.AnswerQuestionCount) || 0, + completedPackages: Number(overview?.CompletedPackCount) || 0, + totalPackages: Number(overview?.TotalPackCount) || 0, + masteredPoints: Number(overview?.OvercomeKnowledgePointCount) || 0, + courses, + score: { + accuracy: toNullableNumber(accuracy?.TodayAccuracyRate), + yesterdayAccuracy: toNullableNumber(accuracy?.YesterdayAccuracyRate), + change: toNullableNumber(accuracy?.AccuracyRateDiff), + hasYesterdayData: Boolean(accuracy?.HasYesterdayData) + }, + pointsTotal: Number(points?.TotalPoints) || 0, + pointDetails + } + } + }) +} + +function toNullableNumber(value: number | null | undefined): number | null { + if (value === null || value === undefined) return null + return Number(value) +} + +function formatTodayLearningTime(value: string | null | undefined): string { + if (!value) return '--' + const text = String(value) + return text.length >= 16 ? text.substring(11, 16) : text +} + +function formatTodayLearningDateText(value: string | null | undefined): string { + if (!value) return '' + const text = String(value) + if (text.length < 10) return text + const year = Number(text.substring(0, 4)) + const month = Number(text.substring(5, 7)) + const day = Number(text.substring(8, 10)) + return `${month}月${day}日 ${weekdayName(year, month, day)}` +} + +function weekdayName(year: number, month: number, day: number): string { + const offsets = [0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4] + const adjustedYear = month < 3 ? year - 1 : year + const index = (adjustedYear + Math.floor(adjustedYear / 4) - Math.floor(adjustedYear / 100) + Math.floor(adjustedYear / 400) + offsets[month - 1] + day) % 7 + return ['周日', '周一', '周二', '周三', '周四', '周五', '周六'][index] } /** diff --git a/pages/today-learning/index.uvue b/pages/today-learning/index.uvue index c556b4e..c2c0af8 100644 --- a/pages/today-learning/index.uvue +++ b/pages/today-learning/index.uvue @@ -2,15 +2,15 @@ - 今日学习{{ detail.dateText }} {{ homeStudentProfile?.name ?? detail.studentName }} · {{ homeStudentProfile?.school ?? '' }} {{ homeStudentProfile?.className ?? '' }} - + 今日学习{{ headerSubtitle }} + 今日学习信息加载中 - 今日学习概览{{ detail.checkedIn ? '已打卡' : '未打卡' }} + 今日学习概览{{ detail.checkInStatusName }} 学习时长{{ detail.learningMinutes }} 分钟 答题时长{{ detail.answerMinutes }} 分钟 @@ -22,11 +22,18 @@ 今日课表与每课打卡 - + 今日暂无课程 + {{ course.time }} - {{ course.subject }}{{ course.content }} - {{ course.checkedIn ? '已打卡' : '未打卡' }} + + + {{ course.subject }} + {{ course.stageName }} + + {{ course.content }} + + {{ course.statusText }} @@ -34,37 +41,69 @@ 正确率(对比昨日) 整体正确率较昨日全部答题目统计,与昨日对比 - 整体正确率{{ detail.score.accuracy }}%+{{ detail.score.change }}% - 昨日 {{ detail.score.yesterdayAccuracy }}% - + + 整体正确率 + + {{ formatAccuracy(detail.score.accuracy) }} + {{ formatAccuracyDiff(detail.score.change) }} + + + 昨日 {{ formatAccuracy(detail.score.yesterdayAccuracy) }} + 今日获得积分积分明细 › +{{ detail.pointsTotal }}积分 - + 今日暂无积分 + {{ item.label }}+{{ item.points }} + + + + + 切换孩子 + 切换后将刷新当前孩子的今日学习 + + + 学生信息加载中 + 暂无已绑定学生 + + {{ studentPickerInitial(item.name) }} + {{ item.name }}{{ item.school }} {{ item.className }}账号:{{ item.account }} + {{ selectedStudentId === item.id ? '当前查看' : '切换' }} + + + 取消确定 + + diff --git a/utils/request.ts b/utils/request.ts index 235a7b6..a3c3ccd 100644 --- a/utils/request.ts +++ b/utils/request.ts @@ -333,7 +333,7 @@ export function request(config: RequestConfig): Promise | null return new Promise((resolve) => { const finalConfig = requestInterceptor(config) - if (USE_MOCK_API && !finalConfig.url.includes('/api/v1/Parents/') && !finalConfig.url.includes('/api/v1/ParentStudentToday/') && !finalConfig.url.includes('/api/v1/ParentLearningWarning/') && !finalConfig.url.includes('/api/v1/ParentStudyPack/') && !finalConfig.url.includes('/api/v1/ParentLearningReport/')) { + if (USE_MOCK_API && !finalConfig.url.includes('/api/v1/Parents/') && !finalConfig.url.includes('/api/v1/ParentStudentToday/') && !finalConfig.url.includes('/api/v1/ParentLearningWarning/') && !finalConfig.url.includes('/api/v1/ParentStudyPack/') && !finalConfig.url.includes('/api/v1/ParentLearningReport/') && !finalConfig.url.includes('/api/v1/ParentLearningSituation/')) { const delay = Math.floor(Math.random() * (MOCK_RESPONSE_MAX_DELAY + 1)) setTimeout(() => { if (finalConfig.showLoading !== false) {