/** * 网络请求封装 * 支持请求拦截、响应拦截、Loading显示、错误处理 */ // 响应数据类型 export type ApiResponse = { code: number message: string data: T | null } // 请求配置类型 export type RequestConfig = { url: string method?: 'GET' | 'POST' | 'PUT' | 'DELETE' data?: any header?: any showLoading?: boolean loadingText?: string showError?: boolean } // 基础URL配置 - 虚拟接口 const BASE_URL = 'https://localhost:7145' // 请求超时时间 const TIMEOUT = 10000 // 联调前统一使用测试响应,保证所有页面流程可完整验证。 const USE_MOCK_API = true // Mock 接口模拟正常网络环境,响应延迟范围为 0 到 2 秒。 const MOCK_RESPONSE_MAX_DELAY = 2000 // Loading引用计数 let loadingCount = 0 /** * 显示Loading */ function showLoading(text: string = '加载中...') { loadingCount++ if (loadingCount === 1) { uni.showLoading({ title: text, mask: true }) } } /** * 隐藏Loading */ function hideLoading() { loadingCount-- if (loadingCount <= 0) { loadingCount = 0 uni.hideLoading() } } /** * 获取Token */ function getToken(): string { return uni.getStorageSync('token') as string ?? '' } /** * 请求拦截器 - 添加token等 */ function requestInterceptor(config: RequestConfig): RequestConfig { // 添加完整URL if (!config.url.startsWith('http')) { config.url = BASE_URL + config.url } // 添加请求头 const token = getToken() config.header = config.header ?? {} config.header['Content-Type'] = 'application/json' if (token) { config.header['Authorization'] = `Bearer ${token}` } // 显示Loading if (config.showLoading !== false) { showLoading(config.loadingText) } return config } /** * 响应拦截器 - 统一处理响应 */ function responseInterceptor(response: UniNamespace.RequestSuccessCallbackResult, config: RequestConfig): ApiResponse | null { // 隐藏Loading if (config.showLoading !== false) { hideLoading() } // HTTP状态码检查 if (response.statusCode !== 200) { handleError(response.statusCode, '网络请求失败', config.showError) return null } // 解析响应数据 const result = response.data as ApiResponse // 业务状态码检查 if (result.code !== 200) { handleError(result.code, result.message, config.showError) return null } return result } /** * 错误处理 */ function handleError(code: number, message: string, showError: boolean = true) { console.error(`[Request Error] Code: ${code}, Message: ${message}`) if (!showError) { return } // 根据错误码显示不同提示 let errorMsg = message if (code === 401) { errorMsg = '登录已过期,请重新登录' // 清除token并跳转登录页 uni.removeStorageSync('token') uni.removeStorageSync('userInfo') uni.reLaunch({ url: '/pages/login/index' }) } else if (code === 403) { errorMsg = '没有权限访问' } else if (code === 404) { errorMsg = '请求的资源不存在' } else if (code === 500) { errorMsg = '服务器错误,请稍后重试' } uni.showToast({ title: errorMsg, icon: 'none', duration: 2000 }) } /** * 根据接口生成页面流程所需的测试数据。 */ function createMockResponse(config: RequestConfig): ApiResponse { const requestData = (config.data ?? {}) as Record let data: any = null if ( config.url.includes('/api/auth/login') || config.url.includes('/api/auth/login-by-code') || config.url.includes('/api/auth/register') ) { data = { userId: 'test-parent-001', phone: requestData.phone ?? '13800000000', nickname: requestData.parentName ?? '测试家长', avatar: '', token: 'test-token', refreshToken: 'test-refresh-token' } } else if (config.url.includes('/api/auth/send-code')) { data = { expireTime: 300 } } else if (config.url.includes('/api/auth/forgot-password/verify')) { data = { token: 'test-reset-token' } } else if (config.url.includes('/api/auth/refresh-token')) { data = { token: 'test-token', refreshToken: 'test-refresh-token' } } else if ( config.url.includes('/api/student/verify-qrcode') || config.url.includes('/api/student/verify-account') ) { const studentAccount = requestData.account ?? 'test-student' data = { id: `test-student-${studentAccount}`, name: requestData.name ?? '测试学生', account: studentAccount, avatar: '', grade: '三年级', class: '一班' } } else if (config.url.includes('/api/parent/check-phone')) { data = { exists: false } } else if (config.url.includes('/api/parent/students')) { data = { currentStudentId: 'test-student-001', students: [ { id: 'test-student-001', name: '张一诺', account: '20241001', grade: '初二', school: '实验中学', avatar: '' }, { id: 'test-student-002', name: '张一鸣', account: '20241002', grade: '五年级', school: '实验小学', avatar: '' }, { id: 'test-student-003', name: '张一宁', account: '20241003', grade: '高二', school: '实验中学', avatar: '' } ] } } else if (config.url.includes('/api/parent/home-metrics')) { const isSecondStudent = config.url.includes('studentId=test-student-002') data = isSecondStudent ? { studentId: 'test-student-002', onlineToday: true, checkedInToday: false, learningMinutesToday: 78, accuracyToday: 88, learningDaysThisWeek: 4, pendingHomeworkCount: 2, updatedAt: '2026-10-28T08:30:00+08:00' } : { studentId: 'test-student-001', onlineToday: true, checkedInToday: true, learningMinutesToday: 155, accuracyToday: 92, learningDaysThisWeek: 5, pendingHomeworkCount: 1, updatedAt: '2026-10-28T08:30:00+08:00' } } else if (config.url.includes('/api/parent/today-learning')) { const isSecondStudent = config.url.includes('studentId=test-student-002') data = isSecondStudent ? { studentId: 'test-student-002', studentName: '张一鸣', dateText: '10月13日 周一', checkedIn: false, learningMinutes: 78, answerMinutes: 56, answerCount: 42, completedPackages: 3, totalPackages: 6, masteredPoints: 8, score: { accuracy: 84, yesterdayAccuracy: 81, change: 3 }, pointsTotal: 28, pointDetails: [ { id: 'point-201', label: '每日登录', points: 10 }, { id: 'point-202', label: '每日提交作业', points: 8 }, { id: 'point-203', label: '连续打卡', points: 6 }, { id: 'point-204', label: '攻克知识点', points: 4 } ], courses: [ { id: 'course-201', time: '08:30', subject: '语文', content: '古诗词诵读与默写', checkedIn: true }, { id: 'course-202', time: '10:00', subject: '数学', content: '分数应用题练习', checkedIn: true }, { id: 'course-203', time: '15:30', subject: '英语', content: 'Unit 4 单词听写', checkedIn: false } ] } : { studentId: 'test-student-001', studentName: '张一诺', dateText: '10月13日 周一', checkedIn: true, learningMinutes: 155, answerMinutes: 118, answerCount: 68, completedPackages: 5, totalPackages: 6, masteredPoints: 12, score: { accuracy: 89, yesterdayAccuracy: 85, change: 4 }, pointsTotal: 42, pointDetails: [ { id: 'point-101', label: '每日登录', points: 10 }, { id: 'point-102', label: '每日提交作业', points: 12 }, { id: 'point-103', label: '连续打卡', points: 8 }, { id: 'point-104', label: '攻克知识点', points: 12 } ], courses: [ { id: 'course-101', time: '08:00', subject: '语文', content: '《背影》精读与批注', checkedIn: true }, { id: 'course-102', time: '10:20', subject: '数学', content: '一元二次方程专项训练', checkedIn: true }, { id: 'course-103', time: '14:00', subject: '英语', content: 'Unit 5 单词听写与跟读', checkedIn: true }, { id: 'course-104', time: '19:30', subject: '物理', content: '声现象章节测试', checkedIn: false } ] } } else if (config.url.includes('/api/parent/vocabulary')) { const isSecondStudent = config.url.includes('studentId=test-student-002') data = isSecondStudent ? { studentId: 'test-student-002', studentName: '张一鸣', studentGrade: '五年级', studentClass: '二班', bookCount: 4, books: [ { id: 'vocab-201', name: '人教版八年级上册', subtitle: '总词量 620 词', totalWords: 620, learnedWords: 420, status: 'learning', statusText: '在学', levelText: '初中同步', color: 'green' }, { id: 'vocab-202', name: '中考核心词汇 1600', subtitle: '总词量 1600 词', totalWords: 1600, learnedWords: 280, status: 'unfinished', statusText: '未学完', levelText: '中考冲刺', color: 'yellow' }, { id: 'vocab-203', name: '人教版三年级起点·五年级上', subtitle: '总词量 380 词', totalWords: 380, learnedWords: 380, status: 'completed', statusText: '已学完', levelText: '小学基础', color: 'blue' }, { id: 'vocab-204', name: '高中必修 5 核心词汇', subtitle: '总词量 1080 词', totalWords: 1080, learnedWords: 0, status: 'not_started', statusText: '未开始', levelText: '高中同步', color: 'gray' } ] } : { studentId: 'test-student-001', studentName: '张一诺', studentGrade: '初二', studentClass: '3班', bookCount: 4, books: [ { id: 'vocab-101', name: '人教版八年级上册', subtitle: '总词量 620 词', totalWords: 620, learnedWords: 520, status: 'learning', statusText: '在学', levelText: '初中同步', color: 'green' }, { id: 'vocab-102', name: '中考核心词汇 1600', subtitle: '总词量 1600 词', totalWords: 1600, learnedWords: 860, status: 'unfinished', statusText: '未学完', levelText: '中考冲刺', color: 'yellow' }, { id: 'vocab-103', name: '人教版三年级起点·五年级上', subtitle: '总词量 380 词', totalWords: 380, learnedWords: 380, status: 'completed', statusText: '已学完', levelText: '小学基础', color: 'blue' }, { id: 'vocab-104', name: '高中必修 5 核心词汇', subtitle: '总词量 1080 词', totalWords: 1080, learnedWords: 0, status: 'not_started', statusText: '未开始', levelText: '高中同步', color: 'gray' } ] } } else if (config.url.includes('/api/parent/learning-tasks')) { const isSecondStudent = config.url.includes('studentId=test-student-002') data = isSecondStudent ? { studentId: 'test-student-002', studentName: '张一鸣', studentGrade: '五年级', studentClass: '二班', pendingCount: 2, completedCount: 3, failedCount: 0, groups: [ { id: 'task-group-201', subject: '生物', unit: '预习', lesson: '第 3 课节', startText: '今天 14:00', endText: '14:40 开课', knowledgeCount: 0, note: '学生尚未进入课程,暂无学习句与知识点,进入课程后将自动生成', tasks: [] }, { id: 'task-group-202', subject: '物理', unit: '复习', lesson: '第 4 课节', startText: '今天 16:00', endText: '16:40 开课', knowledgeCount: 3, note: '', tasks: [{ id: 'task-201', title: '声音的特性', status: 'success', statusText: '攻克成功', accuracyText: '正确率 92%', answerText: '答题时长 6 分钟', deadlineText: '今天 16:35 完成' }, { id: 'task-202', title: '回声与测距', status: 'in_progress', statusText: '攻克中', accuracyText: '正确率 78%', answerText: '答题时长 12 分钟', deadlineText: '今晚 21:00 截止' }, { id: 'task-203', title: '噪声的防治', status: 'failed', statusText: '攻克失败', accuracyText: '', answerText: '', deadlineText: '' }] } ] } : { studentId: 'test-student-001', studentName: '张一诺', studentGrade: '初二', studentClass: '3班', pendingCount: 3, completedCount: 5, failedCount: 1, groups: [{ id: 'task-group-101', subject: '生物', unit: '预习', lesson: '第 3 课节', startText: '今天 14:00', endText: '14:40 开课', knowledgeCount: 0, note: '学生尚未进入课程,暂无学习句与知识点,进入课程后将自动生成', tasks: [] }, { id: 'task-group-102', subject: '物理', unit: '复习', lesson: '第 4 课节', startText: '今天 16:00', endText: '16:40 开课', knowledgeCount: 3, note: '', tasks: [{ id: 'task-101', title: '声音的特性', status: 'success', statusText: '攻克成功', accuracyText: '正确率 92%', answerText: '答题时长 6 分钟', deadlineText: '今天 16:35 完成' }, { id: 'task-102', title: '回声与测距', status: 'in_progress', statusText: '攻克中', accuracyText: '正确率 78%', answerText: '答题时长 12 分钟', deadlineText: '今晚 21:00 截止' }, { id: 'task-103', title: '噪声的防治', status: 'failed', statusText: '攻克失败', accuracyText: '', answerText: '', deadlineText: '' }] }] } } else if (config.url.includes('/api/parent/growth')) { const isSecondStudent = config.url.includes('studentId=test-student-002') data = isSecondStudent ? { studentId: 'test-student-002', studentName: '张一鸣', studentGrade: '五年级', studentClass: '二班', pointsBalance: 862, level: 4, rank: 128, streakDays: 3, totalPoints: 24, records: [ { id: 'growth-201', title: '每日登录', timeText: '今天 07:35', points: 10, icon: 'calendar' }, { id: 'growth-202', title: '每日提交作业', timeText: '今天 19:22', points: 8, icon: 'assignment' }, { id: 'growth-203', title: '连续打卡', timeText: '今天 07:40', points: 6, icon: 'streak' }, { id: 'growth-204', title: '正确率≥80%', timeText: '昨天 20:05', points: 10, icon: 'accuracy' }, { id: 'growth-205', title: '攻克知识点', timeText: '昨天 19:40', points: 8, icon: 'mastery' }, { id: 'growth-206', title: '上传笔记', timeText: '10月12日 18:20', points: 5, icon: 'note' } ] } : { studentId: 'test-student-001', studentName: '张一诺', studentGrade: '初二', studentClass: '3班', pointsBalance: 1268, level: 7, rank: 86, streakDays: 3, totalPoints: 42, records: [ { id: 'growth-101', title: '每日登录', timeText: '今天 07:35', points: 10, icon: 'calendar' }, { id: 'growth-102', title: '每日提交作业', timeText: '今天 19:22', points: 12, icon: 'assignment' }, { id: 'growth-103', title: '连续打卡', timeText: '今天 07:40', points: 8, icon: 'streak' }, { id: 'growth-104', title: '正确率≥80%', timeText: '昨天 20:05', points: 10, icon: 'accuracy' }, { id: 'growth-105', title: '攻克知识点', timeText: '昨天 19:40', points: 12, icon: 'mastery' }, { id: 'growth-106', title: '上传笔记', timeText: '10月12日 18:20', points: 5, icon: 'note' } ] } } else if (config.url.includes('/api/parent/study-report')) { const isSecondStudent = config.url.includes('studentId=test-student-002') const subjects = [ { id: 'all', name: '全部科目' }, { id: 'math', name: '数学' }, { id: 'chinese', name: '语文' }, { id: 'english', name: '英语' }, { id: 'physics', name: '物理' } ] const studentOneWeekActivity = { summary: '周六学习最投入,周四效率最高。', activeDays: 6, totalDurationText: '14小时20分', points: [{ label: '周一', studyMinutes: 0, plannedMinutes: 120, newMasteredCount: 0, studyHeight: 0, plannedHeight: 49, newMasteredHeight: 0 }, { label: '周二', studyMinutes: 0, plannedMinutes: 95, newMasteredCount: 0, studyHeight: 0, plannedHeight: 39, newMasteredHeight: 0 }, { label: '周三', studyMinutes: 142, plannedMinutes: 120, newMasteredCount: 2, studyHeight: 58, plannedHeight: 49, newMasteredHeight: 34 }, { label: '周四', studyMinutes: 88, plannedMinutes: 88, newMasteredCount: 2, studyHeight: 36, plannedHeight: 36, newMasteredHeight: 34 }, { label: '周五', studyMinutes: 0, plannedMinutes: 148, newMasteredCount: 0, studyHeight: 0, plannedHeight: 61, newMasteredHeight: 0 }, { label: '周六', studyMinutes: 178, plannedMinutes: 166, newMasteredCount: 1, studyHeight: 74, plannedHeight: 68, newMasteredHeight: 17 }, { label: '周日', studyMinutes: 0, plannedMinutes: 152, newMasteredCount: 0, studyHeight: 0, plannedHeight: 62, newMasteredHeight: 0 }] } const studentOneMonthActivity = { summary: '本月学习节奏稳定,周末自主学习投入更高。', activeDays: 24, totalDurationText: '52小时40分', points: [{ label: '第1周', studyMinutes: 610, plannedMinutes: 650, newMasteredCount: 3, studyHeight: 52, plannedHeight: 56, newMasteredHeight: 24 }, { label: '第2周', studyMinutes: 690, plannedMinutes: 660, newMasteredCount: 4, studyHeight: 60, plannedHeight: 57, newMasteredHeight: 33 }, { label: '第3周', studyMinutes: 720, plannedMinutes: 680, newMasteredCount: 4, studyHeight: 64, plannedHeight: 59, newMasteredHeight: 33 }, { label: '第4周', studyMinutes: 780, plannedMinutes: 700, newMasteredCount: 3, studyHeight: 72, plannedHeight: 61, newMasteredHeight: 24 }] } const studentOneWeekMasteries = [{ subjectName: '数学', masteredCount: 38, totalCount: 40, masteredPercent: 95 }, { subjectName: '语文', masteredCount: 26, totalCount: 28, masteredPercent: 93 }, { subjectName: '英语', masteredCount: 18, totalCount: 33, masteredPercent: 55 }, { subjectName: '物理', masteredCount: 9, totalCount: 18, masteredPercent: 50 }] const studentOneMonthMasteries = [{ subjectName: '数学', masteredCount: 112, totalCount: 120, masteredPercent: 93 }, { subjectName: '语文', masteredCount: 84, totalCount: 92, masteredPercent: 91 }, { subjectName: '英语', masteredCount: 68, totalCount: 96, masteredPercent: 71 }, { subjectName: '物理', masteredCount: 42, totalCount: 68, masteredPercent: 62 }] const studentTwoWeekActivity = { summary: '本周学习节奏平稳,周末阅读投入较多。', activeDays: 5, totalDurationText: '11小时10分', points: [{ label: '周一', studyMinutes: 82, plannedMinutes: 100, newMasteredCount: 1, studyHeight: 34, plannedHeight: 42, newMasteredHeight: 17 }, { label: '周二', studyMinutes: 0, plannedMinutes: 92, newMasteredCount: 0, studyHeight: 0, plannedHeight: 38, newMasteredHeight: 0 }, { label: '周三', studyMinutes: 110, plannedMinutes: 105, newMasteredCount: 1, studyHeight: 46, plannedHeight: 43, newMasteredHeight: 17 }, { label: '周四', studyMinutes: 96, plannedMinutes: 100, newMasteredCount: 1, studyHeight: 40, plannedHeight: 42, newMasteredHeight: 17 }, { label: '周五', studyMinutes: 0, plannedMinutes: 110, newMasteredCount: 0, studyHeight: 0, plannedHeight: 46, newMasteredHeight: 0 }, { label: '周六', studyMinutes: 152, plannedMinutes: 128, newMasteredCount: 2, studyHeight: 63, plannedHeight: 53, newMasteredHeight: 34 }, { label: '周日', studyMinutes: 130, plannedMinutes: 126, newMasteredCount: 1, studyHeight: 54, plannedHeight: 52, newMasteredHeight: 17 }] } const studentTwoMonthActivity = { summary: '本月完成大部分学习计划,建议保持阅读习惯。', activeDays: 21, totalDurationText: '45小时30分', points: [{ label: '第1周', studyMinutes: 520, plannedMinutes: 600, newMasteredCount: 2, studyHeight: 44, plannedHeight: 51, newMasteredHeight: 17 }, { label: '第2周', studyMinutes: 590, plannedMinutes: 610, newMasteredCount: 3, studyHeight: 50, plannedHeight: 52, newMasteredHeight: 24 }, { label: '第3周', studyMinutes: 640, plannedMinutes: 620, newMasteredCount: 2, studyHeight: 55, plannedHeight: 53, newMasteredHeight: 17 }, { label: '第4周', studyMinutes: 670, plannedMinutes: 640, newMasteredCount: 3, studyHeight: 58, plannedHeight: 55, newMasteredHeight: 24 }] } const studentTwoWeekMasteries = [{ subjectName: '数学', masteredCount: 32, totalCount: 40, masteredPercent: 80 }, { subjectName: '语文', masteredCount: 24, totalCount: 28, masteredPercent: 86 }, { subjectName: '英语', masteredCount: 16, totalCount: 30, masteredPercent: 53 }, { subjectName: '科学', masteredCount: 12, totalCount: 22, masteredPercent: 55 }] const studentTwoMonthMasteries = [{ subjectName: '数学', masteredCount: 92, totalCount: 120, masteredPercent: 77 }, { subjectName: '语文', masteredCount: 76, totalCount: 92, masteredPercent: 83 }, { subjectName: '英语', masteredCount: 58, totalCount: 90, masteredPercent: 64 }, { subjectName: '科学', masteredCount: 38, totalCount: 68, masteredPercent: 56 }] data = isSecondStudent ? { studentId: 'test-student-002', studentName: '张一鸣', studentGrade: '五年级', studentClass: '二班', updatedAtText: '10月13日 08:30', subjects, week: { rangeText: '10月7日 - 10月13日', statusTitle: '本周学习状态良好', statusDescription: '本周学习完成度稳定,建议保持每天的阅读与口算练习。', newMasteredPoints: 2, consecutiveDays: 8, averageAccuracy: 86, scaleLabels: ['120个', '116个', '112个', '108个'], trends: [ { subjectId: 'all', points: [{ label: '周一', value: 109, average: 110, valueHeight: 24, averageHeight: 30 }, { label: '周二', value: 110, average: 111, valueHeight: 30, averageHeight: 35 }, { label: '周三', value: 111, average: 111, valueHeight: 36, averageHeight: 35 }, { label: '周四', value: 112, average: 112, valueHeight: 42, averageHeight: 42 }, { label: '周五', value: 112, average: 113, valueHeight: 42, averageHeight: 48 }, { label: '周六', value: 114, average: 113, valueHeight: 54, averageHeight: 48 }, { label: '周日', value: 115, average: 114, valueHeight: 60, averageHeight: 54 }] }, { subjectId: 'math', points: [{ label: '周一', value: 108, average: 109, valueHeight: 18, averageHeight: 24 }, { label: '周二', value: 109, average: 110, valueHeight: 24, averageHeight: 30 }, { label: '周三', value: 110, average: 110, valueHeight: 30, averageHeight: 30 }, { label: '周四', value: 111, average: 111, valueHeight: 36, averageHeight: 36 }, { label: '周五', value: 112, average: 112, valueHeight: 42, averageHeight: 42 }, { label: '周六', value: 113, average: 112, valueHeight: 48, averageHeight: 42 }, { label: '周日', value: 114, average: 113, valueHeight: 54, averageHeight: 48 }] } ], activity: studentTwoWeekActivity, subjectMasteries: studentTwoWeekMasteries }, month: { rangeText: '9月14日 - 10月13日', statusTitle: '本月学习状态稳步提升', statusDescription: '本月坚持学习,多个学科的知识点掌握情况有所提升。', newMasteredPoints: 9, consecutiveDays: 8, averageAccuracy: 87, scaleLabels: ['120个', '116个', '112个', '108个'], trends: [{ subjectId: 'all', points: [{ label: '第1周', value: 107, average: 108, valueHeight: 14, averageHeight: 18 }, { label: '第2周', value: 110, average: 109, valueHeight: 30, averageHeight: 24 }, { label: '第3周', value: 113, average: 111, valueHeight: 46, averageHeight: 36 }, { label: '第4周', value: 116, average: 113, valueHeight: 62, averageHeight: 48 }] }], activity: studentTwoMonthActivity, subjectMasteries: studentTwoMonthMasteries } } : { studentId: 'test-student-001', studentName: '张一诺', studentGrade: '初二', studentClass: '3班', updatedAtText: '10月13日 08:30', subjects, week: { rangeText: '10月7日 - 10月13日', statusTitle: '本周学习状态良好', statusDescription: '本周状态良好,数学与英语逐渐稳定,物理薄弱题型进步明显。', newMasteredPoints: 3, consecutiveDays: 12, averageAccuracy: 90, scaleLabels: ['120个', '116个', '112个', '108个'], trends: [ { subjectId: 'all', points: [{ label: '周一', value: 112, average: 111, valueHeight: 32, averageHeight: 26 }, { label: '周二', value: 112, average: 113, valueHeight: 32, averageHeight: 38 }, { label: '周三', value: 113, average: 113, valueHeight: 40, averageHeight: 38 }, { label: '周四', value: 114, average: 114, valueHeight: 48, averageHeight: 48 }, { label: '周五', value: 114, average: 114, valueHeight: 48, averageHeight: 48 }, { label: '周六', value: 116, average: 114, valueHeight: 64, averageHeight: 48 }, { label: '周日', value: 116, average: 115, valueHeight: 64, averageHeight: 56 }] }, { subjectId: 'math', points: [{ label: '周一', value: 110, average: 110, valueHeight: 20, averageHeight: 20 }, { label: '周二', value: 111, average: 111, valueHeight: 28, averageHeight: 28 }, { label: '周三', value: 113, average: 112, valueHeight: 42, averageHeight: 35 }, { label: '周四', value: 114, average: 112, valueHeight: 49, averageHeight: 35 }, { label: '周五', value: 115, average: 113, valueHeight: 56, averageHeight: 42 }, { label: '周六', value: 116, average: 113, valueHeight: 63, averageHeight: 42 }, { label: '周日', value: 117, average: 114, valueHeight: 70, averageHeight: 49 }] } ], activity: studentOneWeekActivity, subjectMasteries: studentOneWeekMasteries }, month: { rangeText: '9月14日 - 10月13日', statusTitle: '本月学习状态持续向好', statusDescription: '本月学习计划完成度高,学科掌握情况保持上升趋势。', newMasteredPoints: 14, consecutiveDays: 12, averageAccuracy: 89, scaleLabels: ['120个', '116个', '112个', '108个'], trends: [{ subjectId: 'all', points: [{ label: '第1周', value: 108, average: 109, valueHeight: 16, averageHeight: 23 }, { label: '第2周', value: 111, average: 111, valueHeight: 38, averageHeight: 38 }, { label: '第3周', value: 114, average: 112, valueHeight: 53, averageHeight: 45 }, { label: '第4周', value: 116, average: 114, valueHeight: 68, averageHeight: 53 }] }], activity: studentOneMonthActivity, subjectMasteries: studentOneMonthMasteries } } } else if (config.url.includes('/api/parent/alerts')) { const isSecondStudent = config.url.includes('studentId=test-student-002') const alerts = isSecondStudent ? [ { id: 'alert-201', level: 'medium', title: '作业未完成', content: '英语《单元词汇练习》还有 2 题未完成', occurredAt: '2026-10-28T09:20:00+08:00', isRead: false }, { id: 'alert-202', level: 'low', title: '学习时长提醒', content: '今日学习时长低于本周日均时长', occurredAt: '2026-10-28T08:40:00+08:00', isRead: true } ] : [ { id: 'alert-101', level: 'medium', title: '作业未完成', content: '物理《声现象章节测试》今晚 20:30 截止,尚未提交', occurredAt: '2026-10-28T09:30:00+08:00', isRead: false }, { id: 'alert-102', level: 'high', title: '正确率下降', content: '今日数学正确率 78%,低于近 7 日均值 12%', occurredAt: '2026-10-28T09:10:00+08:00', isRead: false }, { id: 'alert-103', level: 'high', title: '攻读失败', content: '数学《几何证明》已用满 2 次攻读机会,仍未完成', occurredAt: '2026-10-28T08:50:00+08:00', isRead: true }, { id: 'alert-104', level: 'low', title: '上线提醒', content: '今日首次上线时间较平日延后 30 分钟', occurredAt: '2026-10-28T08:10:00+08:00', isRead: true } ] const list = config.url.includes('limit=3') ? alerts.slice(0, 3) : alerts data = { list, pagination: { page: 1, pageSize: list.length, total: alerts.length, hasMore: false } } } else if (config.url.includes('/api/parent/home-profile')) { const isSecondStudent = config.url.includes('studentId=test-student-002') data = isSecondStudent ? { id: 'test-student-002', name: '张一鸣', account: '20241002', grade: '五年级', school: '实验小学', avatar: '', comprehensiveLevel: 'good' } : { id: 'test-student-001', name: '张一诺', account: '20241001', grade: '初二', school: '实验中学', avatar: '', comprehensiveLevel: 'excellent' } } return { code: 200, message: 'success', data: data as T } } /** * 通用请求方法 */ export function request(config: RequestConfig): Promise | null> { return new Promise((resolve, reject) => { // 请求拦截 const finalConfig = requestInterceptor(config) if (USE_MOCK_API && !finalConfig.url.includes('/api/v1/Parents/')) { const delay = Math.floor(Math.random() * (MOCK_RESPONSE_MAX_DELAY + 1)) setTimeout(() => { if (finalConfig.showLoading !== false) { hideLoading() } resolve(createMockResponse(finalConfig)) }, delay) return } // 发起请求 uni.request({ url: finalConfig.url, method: finalConfig.method ?? 'GET', data: finalConfig.data, header: finalConfig.header, timeout: TIMEOUT, success: (res) => { // 响应拦截 const result = responseInterceptor(res, finalConfig) resolve(result) }, fail: (err) => { // 隐藏Loading if (finalConfig.showLoading !== false) { hideLoading() } // 网络错误处理 handleError(-1, err.errMsg ?? '网络连接失败', finalConfig.showError) // 将网络失败转换为统一的空响应,避免页面事件中出现未处理 Promise 异常。 resolve(null) } }) }) } /** * GET请求 */ export function get(url: string, data?: any, config?: Partial): Promise | null> { return request({ url, method: 'GET', data, ...config }) } /** * POST请求 */ export function post(url: string, data?: any, config?: Partial): Promise | null> { return request({ url, method: 'POST', data, ...config }) } /** * PUT请求 */ export function put(url: string, data?: any, config?: Partial): Promise | null> { return request({ url, method: 'PUT', data, ...config }) } /** * DELETE请求 */ export function del(url: string, data?: any, config?: Partial): Promise | null> { return request({ url, method: 'DELETE', data, ...config }) }