接入学生学习数据接口
This commit is contained in:
+2
-1
@@ -4,6 +4,7 @@
|
||||
|
||||
import { post } from '../utils/request'
|
||||
import type { ApiResponse } from '../utils/request'
|
||||
import { md5 } from '../utils/md5'
|
||||
|
||||
// ==================== 类型定义 ====================
|
||||
|
||||
@@ -85,7 +86,7 @@ export type RefreshTokenResponse = {
|
||||
* 密码登录
|
||||
*/
|
||||
export function apiLogin(params: LoginParams): Promise<ApiResponse<UserInfo> | null> {
|
||||
return post<any>('/api/v1/Parents/Login', { phone: params.phone, password: params.password, loginType: 0 }).then((res) => {
|
||||
return post<any>('/api/v1/Parents/Login', { phone: params.phone, password: md5(params.password), loginType: 0 }).then((res) => {
|
||||
if (!res || !res.data) return null
|
||||
return { code: res.code, message: res.message, data: { userId: String(res.data.parentId), phone: res.data.phone, nickname: res.data.name, avatar: '', token: res.data.token, refreshToken: '' } }
|
||||
})
|
||||
|
||||
+196
-6
@@ -14,7 +14,8 @@ export type StudentInfo = {
|
||||
account: string
|
||||
avatar: string
|
||||
grade: string
|
||||
class: string
|
||||
className: string
|
||||
schoolName: string
|
||||
}
|
||||
|
||||
// 首页当前学生基础信息
|
||||
@@ -24,6 +25,7 @@ export type StudentHomeProfile = {
|
||||
account: string
|
||||
grade: string
|
||||
school: string
|
||||
className: string
|
||||
avatar: string
|
||||
comprehensiveLevel: 'excellent' | 'good' | 'qualified' | 'needs_improvement'
|
||||
}
|
||||
@@ -35,6 +37,7 @@ export type BoundStudentSummary = {
|
||||
account: string
|
||||
grade: string
|
||||
school: string
|
||||
className: string
|
||||
avatar: string
|
||||
}
|
||||
|
||||
@@ -46,6 +49,7 @@ export type StudentHomeMetrics = {
|
||||
learningMinutesToday: number
|
||||
accuracyToday: number | null
|
||||
learningDaysThisWeek: number
|
||||
shouldStudyDaysThisWeek: number
|
||||
pendingHomeworkCount: number
|
||||
updatedAt: string
|
||||
}
|
||||
@@ -156,6 +160,7 @@ export type TodayLearningDetail = {
|
||||
export type VocabularyBook = {
|
||||
id: string
|
||||
name: string
|
||||
cover: string
|
||||
subtitle: string
|
||||
totalWords: number
|
||||
learnedWords: number
|
||||
@@ -228,6 +233,8 @@ export type StudentLearningTasks = {
|
||||
groups: LearningTaskGroup[]
|
||||
}
|
||||
|
||||
export type ParentStudyPackStatus = 0 | 1 | 2 | 3
|
||||
|
||||
export type StudentAlertLevel = 'critical' | 'high' | 'medium' | 'low'
|
||||
|
||||
export type StudentAlert = {
|
||||
@@ -249,6 +256,22 @@ export type StudentAlertList = {
|
||||
}
|
||||
}
|
||||
|
||||
export type StudentLearningWarningItem = {
|
||||
type: number
|
||||
title: string
|
||||
description: string
|
||||
isTriggered: boolean
|
||||
statusName: string
|
||||
}
|
||||
|
||||
export type StudentLearningWarningResult = {
|
||||
userId: string
|
||||
date: string
|
||||
totalCount: number
|
||||
triggeredCount: number
|
||||
items: StudentLearningWarningItem[]
|
||||
}
|
||||
|
||||
// 验证学生二维码请求参数
|
||||
export type VerifyQRCodeParams = {
|
||||
qrCode: string
|
||||
@@ -277,7 +300,7 @@ export function apiVerifyStudentQRCode(params: VerifyQRCodeParams): Promise<ApiR
|
||||
export function apiVerifyStudentAccount(params: VerifyAccountParams): Promise<ApiResponse<StudentInfo> | null> {
|
||||
return get<any>('/api/v1/Parents/VerifyStudent', { RealName: params.name, Account: params.account }).then((res) => {
|
||||
if (!res || !res.data) return null
|
||||
return { code: res.code, message: res.message, data: { id: String(res.data.userId), name: res.data.name, account: res.data.account, avatar: res.data.avatar || '', grade: res.data.gradeLevel || '', class: res.data.className || '' } }
|
||||
return { code: res.code, message: res.message, data: { id: String(res.data.userId), name: res.data.name, account: res.data.account, avatar: res.data.avatar || '', grade: res.data.gradeLevel || '', className: res.data.className || '', schoolName: res.data.schoolName || '' } }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -301,14 +324,83 @@ export function apiGetStudentHomeProfile(studentId?: string, showLoading: boolea
|
||||
* 获取当前家长绑定的全部学生,用于首页切换学生。
|
||||
*/
|
||||
export function apiGetBoundStudents(): Promise<ApiResponse<{ students: BoundStudentSummary[], currentStudentId: string | null }> | null> {
|
||||
return get<{ students: BoundStudentSummary[], currentStudentId: string | null }>('/api/parent/students')
|
||||
return get<any>('/api/v1/Parents/GetBoundStudents').then((res) => {
|
||||
if (!res || !res.data) return null
|
||||
const items = Array.isArray(res.data) ? res.data : res.data.students
|
||||
if (!Array.isArray(items)) return null
|
||||
return {
|
||||
code: res.code,
|
||||
message: res.message,
|
||||
data: {
|
||||
students: items.map((item: any) => ({
|
||||
id: String(item.userId),
|
||||
name: item.name || '',
|
||||
account: item.account || '',
|
||||
grade: item.gradeLevel || '',
|
||||
school: item.schoolName || '',
|
||||
className: item.className || '',
|
||||
avatar: item.avatar || ''
|
||||
})),
|
||||
currentStudentId: null
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定学生的首页今日学习概览。
|
||||
*/
|
||||
export function apiGetStudentHomeMetrics(studentId: string, showLoading: boolean = true): Promise<ApiResponse<StudentHomeMetrics> | null> {
|
||||
return get<StudentHomeMetrics>(`/api/parent/home-metrics?studentId=${studentId}`, undefined, { showLoading })
|
||||
return get<any>(`/api/v1/ParentStudentToday/GetTodayInfo?userId=${studentId}`, undefined, { showLoading }).then((res) => {
|
||||
if (!res || !res.data) return null
|
||||
return {
|
||||
code: res.code,
|
||||
message: res.message,
|
||||
data: {
|
||||
studentId: String(res.data.userId ?? studentId),
|
||||
onlineToday: Boolean(res.data.todayOnline),
|
||||
checkedInToday: Boolean(res.data.todayCheckIn),
|
||||
learningMinutesToday: Math.floor((Number(res.data.todayStudyDuration) || 0) / 60),
|
||||
accuracyToday: Number(res.data.todayAccuracyRate) || 0,
|
||||
learningDaysThisWeek: Number(res.data.weekStudyDays) || 0,
|
||||
shouldStudyDaysThisWeek: Number(res.data.weekShouldStudyDays) || 0,
|
||||
pendingHomeworkCount: Number(res.data.pendingPackCount) || 0,
|
||||
updatedAt: res.data.date || ''
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定学生今日学习预警,仅返回已触发的预警规则。
|
||||
*/
|
||||
export function apiGetStudentLearningWarnings(studentId: string, showLoading: boolean = true): Promise<ApiResponse<StudentAlertList> | null> {
|
||||
return get<any>(`/api/v1/ParentLearningWarning/GetTodayList?userId=${studentId}`, undefined, { showLoading }).then((res) => {
|
||||
if (!res || !res.data || !Array.isArray(res.data.items)) return null
|
||||
const list: StudentAlert[] = res.data.items
|
||||
.filter((item: any) => Boolean(item.isTriggered))
|
||||
.map((item: any, index: number) => ({
|
||||
id: `${studentId}-${item.type ?? index}`,
|
||||
level: 'high',
|
||||
title: item.title || '学习预警',
|
||||
content: item.description || item.statusName || '',
|
||||
occurredAt: res.data.date || '',
|
||||
isRead: false
|
||||
}))
|
||||
return {
|
||||
code: res.code,
|
||||
message: res.message,
|
||||
data: {
|
||||
list,
|
||||
pagination: {
|
||||
page: 1,
|
||||
pageSize: list.length,
|
||||
total: list.length,
|
||||
hasMore: false
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -326,7 +418,37 @@ export function apiGetStudentStudyReport(studentId: string, showLoading: boolean
|
||||
}
|
||||
|
||||
export function apiGetStudentVocabulary(studentId: string, showLoading: boolean = true): Promise<ApiResponse<StudentVocabulary> | null> {
|
||||
return get<StudentVocabulary>(`/api/parent/vocabulary?studentId=${studentId}`, undefined, { showLoading })
|
||||
return get<any>(`/api/v1/Parents/GetAllLexiconBooks/${studentId}`, undefined, { showLoading }).then((res) => {
|
||||
if (!res || !Array.isArray(res.data)) return null
|
||||
const books: VocabularyBook[] = res.data.map((item: any) => {
|
||||
const learnedWords = Number(item.lexiconCount) || 0
|
||||
const totalWords = Number(item.totalCount) || learnedWords
|
||||
const status: VocabularyBook['status'] = totalWords > 0 && learnedWords >= totalWords
|
||||
? 'completed'
|
||||
: item.isStudying
|
||||
? 'learning'
|
||||
: learnedWords > 0
|
||||
? 'unfinished'
|
||||
: 'not_started'
|
||||
return {
|
||||
id: String(item.id ?? item.title ?? ''),
|
||||
name: item.title || '',
|
||||
cover: item.cover || '',
|
||||
subtitle: `总词量 ${totalWords} 词`,
|
||||
totalWords,
|
||||
learnedWords,
|
||||
status,
|
||||
statusText: status === 'completed' ? '已完成' : status === 'learning' ? '学习中' : status === 'unfinished' ? '未学完' : '未开始',
|
||||
levelText: item.slogan || '',
|
||||
color: status === 'learning' ? 'green' : status === 'unfinished' ? 'yellow' : status === 'completed' ? 'blue' : 'gray'
|
||||
}
|
||||
})
|
||||
return {
|
||||
code: res.code,
|
||||
message: res.message,
|
||||
data: { studentId, studentName: '', studentGrade: '', studentClass: '', bookCount: books.length, books }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function apiGetStudentGrowth(studentId: string, showLoading: boolean = true): Promise<ApiResponse<StudentGrowth> | null> {
|
||||
@@ -334,7 +456,75 @@ export function apiGetStudentGrowth(studentId: string, showLoading: boolean = tr
|
||||
}
|
||||
|
||||
export function apiGetStudentLearningTasks(studentId: string, showLoading: boolean = true): Promise<ApiResponse<StudentLearningTasks> | null> {
|
||||
return get<StudentLearningTasks>(`/api/parent/learning-tasks?studentId=${studentId}`, undefined, { showLoading })
|
||||
return apiGetParentStudyPacks(studentId, 0, showLoading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取学生当天学习包任务。Status: 0 全部,1 待完成,2 已完成,3 未通过。
|
||||
*/
|
||||
export function apiGetParentStudyPacks(studentId: string, status: ParentStudyPackStatus = 0, showLoading: boolean = true): Promise<ApiResponse<StudentLearningTasks> | null> {
|
||||
return get<any>(`/api/v1/ParentStudyPack/GetTodayList?UserId=${studentId}&Status=${status}`, undefined, { showLoading }).then((res) => {
|
||||
if (!res || !res.data) return null
|
||||
const statusMap: { [key: number]: LearningTaskItem['status'] } = {
|
||||
0: 'not_started',
|
||||
1: 'in_progress',
|
||||
2: 'success',
|
||||
3: 'failed'
|
||||
}
|
||||
const statusTextMap: { [key: string]: string } = {
|
||||
not_started: '待完成',
|
||||
in_progress: '进行中',
|
||||
success: '已完成',
|
||||
failed: '未通过'
|
||||
}
|
||||
const courses = Array.isArray(res.data.courses) ? res.data.courses : []
|
||||
const groups: LearningTaskGroup[] = courses.map((course: any) => {
|
||||
const tasks: LearningTaskItem[] = (Array.isArray(course.packs) ? course.packs : []).map((pack: any) => {
|
||||
const taskStatus = statusMap[Number(pack.status)] || 'not_started'
|
||||
const accuracy = pack.latestAccuracyRate === null || pack.latestAccuracyRate === undefined ? '--' : `${Number(pack.latestAccuracyRate) || 0}%`
|
||||
return {
|
||||
id: String(pack.packId ?? pack.knowledgePointId ?? ''),
|
||||
title: pack.name || '未命名学习任务',
|
||||
status: taskStatus,
|
||||
statusText: pack.statusName || statusTextMap[taskStatus],
|
||||
accuracyText: accuracy,
|
||||
answerText: pack.completedAttemptCount ? `已完成 ${pack.completedAttemptCount} 次` : '尚未完成',
|
||||
deadlineText: pack.deadlineTime || ''
|
||||
}
|
||||
})
|
||||
return {
|
||||
id: String(course.courseId ?? course.sectionCount ?? ''),
|
||||
subject: course.baseSubjectName || '',
|
||||
unit: course.studyStageName || '',
|
||||
lesson: course.courseName || course.sectionName || '',
|
||||
startText: formatTaskTime(course.planStartTime),
|
||||
endText: formatTaskTime(course.planEndTime),
|
||||
knowledgeCount: Number(course.knowledgePointCount) || tasks.length,
|
||||
note: course.deadlineTime ? `截止时间:${formatTaskTime(course.deadlineTime)}` : '',
|
||||
tasks
|
||||
}
|
||||
})
|
||||
return {
|
||||
code: res.code,
|
||||
message: res.message,
|
||||
data: {
|
||||
studentId: String(res.data.userId ?? studentId),
|
||||
studentName: res.data.userName || '',
|
||||
studentGrade: '',
|
||||
studentClass: '',
|
||||
pendingCount: Number(res.data.pendingCount) || 0,
|
||||
completedCount: Number(res.data.completedCount) || 0,
|
||||
failedCount: Number(res.data.failedCount) || 0,
|
||||
groups
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function formatTaskTime(value: any): string {
|
||||
if (!value) return '--'
|
||||
const text = String(value)
|
||||
return text.length >= 16 ? text.substring(11, 16) : text
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user