From ab31374e1aca275157782f60e8389ccca6be2d58 Mon Sep 17 00:00:00 2001 From: username Date: Tue, 29 Jul 2025 16:04:50 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=AF=BE=E7=A8=8B=E6=92=AD?= =?UTF-8?q?=E6=94=BE=E8=A7=86=E9=A2=91=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 5 +- src/api/modules/course.ts | 191 +++++++++++++++++++++++++++++--------- src/views/Courses.vue | 22 ++++- src/views/Faculty.vue | 46 +++++---- 4 files changed, 194 insertions(+), 70 deletions(-) diff --git a/index.html b/index.html index 3699cc4..24f2f0e 100644 --- a/index.html +++ b/index.html @@ -7,10 +7,7 @@ 在线学习平台 - - - - +
diff --git a/src/api/modules/course.ts b/src/api/modules/course.ts index df09693..6b8ed79 100644 --- a/src/api/modules/course.ts +++ b/src/api/modules/course.ts @@ -93,9 +93,55 @@ export class CourseApi { // 调用后端API const response = await ApiRequest.get('/lesson/list', queryParams) console.log('课程列表API响应:', response) + console.log('响应数据结构:', response.data) + console.log('响应数据类型:', typeof response.data) + + // 检查是否是axios响应格式还是我们的ApiResponse格式 + let actualData: any + let actualCode: number + let actualMessage: string + + // 使用类型断言来处理不同的响应格式 + const responseAny = response as any + + if (responseAny.data && typeof responseAny.data === 'object' && 'data' in responseAny.data) { + // 这是我们期望的ApiResponse格式: { code, message, data: { list, total } } + actualData = responseAny.data.data + actualCode = responseAny.data.code + actualMessage = responseAny.data.message + console.log('检测到ApiResponse格式') + } else { + // 这可能是直接的axios响应格式: { list, total } + actualData = responseAny.data + actualCode = responseAny.status || 200 + actualMessage = responseAny.statusText || 'OK' + console.log('检测到直接响应格式') + } + + console.log('实际数据:', actualData) + console.log('实际数据的list字段:', actualData?.list) + console.log('list是否为数组:', Array.isArray(actualData?.list)) + + // 检查响应数据的有效性 + if (!actualData || !Array.isArray(actualData.list)) { + console.warn('API响应数据格式异常') + console.warn('期望的格式: { list: [], total: number }') + console.warn('实际收到的格式:', actualData) + return { + code: actualCode || 0, + message: actualMessage || '数据格式异常', + data: { + list: [], + total: 0, + page: params?.page || 1, + pageSize: params?.pageSize || 10, + totalPages: 0 + } + } + } // 适配后端响应格式为前端期望的格式 - const adaptedCourses: Course[] = response.data.list.map((backendCourse: BackendCourse) => ({ + const adaptedCourses: Course[] = actualData.list.map((backendCourse: BackendCourse) => ({ id: backendCourse.id, title: backendCourse.name, description: backendCourse.description, @@ -140,20 +186,33 @@ export class CourseApi { })) const adaptedResponse: ApiResponse> = { - code: response.code, - message: response.message, + code: actualCode, + message: actualMessage, data: { list: adaptedCourses, - total: response.data.total, + total: actualData.total || 0, page: params?.page || 1, pageSize: params?.pageSize || 10, - totalPages: Math.ceil(response.data.total / (params?.pageSize || 10)) + totalPages: Math.ceil((actualData.total || 0) / (params?.pageSize || 10)) } } return adaptedResponse } catch (error) { - throw error + console.error('课程API调用失败:', error) + + // 返回空数据而不是抛出错误,确保应用不会崩溃 + return { + code: 500, + message: '获取课程列表失败', + data: { + list: [], + total: 0, + page: params?.page || 1, + pageSize: params?.pageSize || 10, + totalPages: 0 + } + } } } @@ -183,55 +242,77 @@ export class CourseApi { // 调用后端课程详情接口 const response = await ApiRequest.get('/lesson/detail', { id }) + // 检查是否是axios响应格式还是我们的ApiResponse格式 + let actualData: any + let actualCode: number + let actualMessage: string + + // 使用类型断言来处理不同的响应格式 + const responseAny = response as any + + if (responseAny.data && typeof responseAny.data === 'object' && 'data' in responseAny.data) { + // 这是我们期望的ApiResponse格式: { code, message, data: BackendCourse } + actualData = responseAny.data.data + actualCode = responseAny.data.code + actualMessage = responseAny.data.message + console.log('检测到ApiResponse格式') + } else { + // 这可能是直接的axios响应格式: BackendCourse + actualData = responseAny.data + actualCode = responseAny.status || 200 + actualMessage = responseAny.statusText || 'OK' + console.log('检测到直接响应格式') + } + // 适配数据格式 const adaptedCourse: Course = { - id: response.data.id, - title: response.data.name, - description: response.data.description, - content: response.data.outline, // 使用 outline 作为课程内容 - thumbnail: response.data.cover, - coverImage: response.data.cover, - price: parseFloat(response.data.price || '0'), - originalPrice: parseFloat(response.data.price || '0'), + id: actualData.id, + title: actualData.name, + description: actualData.description, + content: actualData.outline, // 使用 outline 作为课程内容 + thumbnail: actualData.cover, + coverImage: actualData.cover, + price: parseFloat(actualData.price || '0'), + originalPrice: parseFloat(actualData.price || '0'), currency: 'CNY', rating: 4.5, ratingCount: 0, studentsCount: 0, - duration: this.calculateDuration(response.data.startTime, response.data.endTime), + duration: this.calculateDuration(actualData.startTime, actualData.endTime), totalLessons: 0, level: 'beginner' as const, language: 'zh-CN', category: { - id: response.data.categoryId, + id: actualData.categoryId, name: '未分类', slug: 'uncategorized' }, tags: [], skills: [], - requirements: response.data.prerequisite ? [response.data.prerequisite] : [], - objectives: response.data.target ? [response.data.target] : [], + requirements: actualData.prerequisite ? [actualData.prerequisite] : [], + objectives: actualData.target ? [actualData.target] : [], instructor: { - id: response.data.teacherId || 0, - name: response.data.school || '未知讲师', + id: actualData.teacherId || 0, + name: actualData.school || '未知讲师', title: '讲师', - bio: response.data.position || '', + bio: actualData.position || '', avatar: '', rating: 4.5, studentsCount: 0, coursesCount: 0, - experience: response.data.arrangement || '', + experience: actualData.arrangement || '', education: [], certifications: [] }, status: 'published' as const, - createdAt: this.formatTimestamp(response.data.createdTime), - updatedAt: this.formatTimestamp(response.data.updatedTime), - publishedAt: response.data.startTime + createdAt: this.formatTimestamp(actualData.createdTime), + updatedAt: this.formatTimestamp(actualData.updatedTime), + publishedAt: actualData.startTime } return { - code: response.code, - message: response.message, + code: actualCode, + message: actualMessage, data: adaptedCourse } } catch (error) { @@ -300,28 +381,54 @@ export class CourseApi { const backendResponse = await ApiRequest.get('/lesson/section/list', { lesson_id: lessonId.toString() }) console.log('章节API响应:', backendResponse) - console.log('响应状态码:', backendResponse.code) - console.log('响应消息:', backendResponse.message) - console.log('原始章节数据:', backendResponse.data?.list) - console.log('章节数据数量:', backendResponse.data?.list?.length || 0) + + // 检查是否是axios响应格式还是我们的ApiResponse格式 + let actualData: any + let actualCode: number + let actualMessage: string + let actualTimestamp: string | undefined + + // 使用类型断言来处理不同的响应格式 + const responseAny = backendResponse as any + + if (responseAny.data && typeof responseAny.data === 'object' && 'data' in responseAny.data) { + // 这是我们期望的ApiResponse格式: { code, message, data: { list }, timestamp } + actualData = responseAny.data.data + actualCode = responseAny.data.code + actualMessage = responseAny.data.message + actualTimestamp = responseAny.data.timestamp?.toString() + console.log('检测到ApiResponse格式') + } else { + // 这可能是直接的axios响应格式: { list } + actualData = responseAny.data + actualCode = responseAny.status || 200 + actualMessage = responseAny.statusText || 'OK' + actualTimestamp = undefined + console.log('检测到直接响应格式') + } + + console.log('响应状态码:', actualCode) + console.log('响应消息:', actualMessage) + console.log('原始章节数据:', actualData?.list) + console.log('章节数据数量:', actualData?.list?.length || 0) // 检查数据是否存在 - if (!backendResponse.data || !backendResponse.data.list) { - console.warn('API返回的数据结构不正确:', backendResponse.data) + if (!actualData || !Array.isArray(actualData.list)) { + console.warn('API返回的数据结构不正确:', actualData) return { - code: backendResponse.code, - message: backendResponse.message, + code: actualCode, + message: actualMessage, data: { list: [], timestamp: Date.now(), - traceId: backendResponse.timestamp?.toString() || '' + traceId: actualTimestamp || '' }, - timestamp: backendResponse.timestamp?.toString() + timestamp: actualTimestamp } } // 适配数据格式 - const adaptedSections: CourseSection[] = backendResponse.data.list.map((section: BackendCourseSection) => ({ + const adaptedSections: CourseSection[] = actualData.list.map((section: BackendCourseSection) => ({ id: section.id, lessonId: section.lessonId, outline: section.videoUrl, // 将videoUrl映射到outline @@ -340,14 +447,14 @@ export class CourseApi { console.log('适配后的章节数据:', adaptedSections) const adaptedResponse: ApiResponse = { - code: backendResponse.code, - message: backendResponse.message, + code: actualCode, + message: actualMessage, data: { list: adaptedSections, timestamp: Date.now(), - traceId: backendResponse.timestamp?.toString() || '' + traceId: actualTimestamp || '' }, - timestamp: backendResponse.timestamp?.toString() + timestamp: actualTimestamp } return adaptedResponse diff --git a/src/views/Courses.vue b/src/views/Courses.vue index d2f0048..715c50b 100644 --- a/src/views/Courses.vue +++ b/src/views/Courses.vue @@ -355,16 +355,30 @@ const loadCourses = async () => { keyword: selectedMajor.value !== '全部' ? selectedMajor.value : undefined } + console.log('加载课程参数:', params) const response = await CourseApi.getCourses(params) + console.log('课程API响应:', response) - if (response.code === 0 || response.code === 200) { - courses.value = response.data.list - total.value = response.data.total + if (response && response.data) { + if (response.code === 0 || response.code === 200) { + courses.value = response.data.list || [] + total.value = response.data.total || 0 + console.log('课程加载成功:', courses.value.length, '条课程') + } else { + console.error('获取课程列表失败:', response.message) + courses.value = [] + total.value = 0 + } } else { - console.error('获取课程列表失败:', response.message) + console.error('API响应格式异常:', response) + courses.value = [] + total.value = 0 } } catch (error) { console.error('加载课程失败:', error) + courses.value = [] + total.value = 0 + // 可以在这里添加用户友好的错误提示 } finally { loading.value = false } diff --git a/src/views/Faculty.vue b/src/views/Faculty.vue index 147be40..901c9f0 100644 --- a/src/views/Faculty.vue +++ b/src/views/Faculty.vue @@ -106,14 +106,14 @@ const hasBannerImage = computed(() => bannerImageSrc.value.trim() !== '') // 筛选标签数据 const filterTabs = ref([ - { id: 'all', name: '全部讲师' }, - { id: 'main', name: '主讲' }, - { id: 'international', name: '注册国际讲师' }, - { id: 'consultant', name: '咨询师' }, - { id: 'expert', name: '专家顾问' }, - { id: 'senior', name: '资深讲师' }, - { id: 'featured', name: '金牌讲师' }, - { id: 'enterprise', name: '企业导师' } + { id: 'field', name: '课长领域' }, + { id: 'all', name: '全部' }, + { id: 'chinese-promotion', name: '汉语国际推广' }, + { id: 'chinese-language', name: '汉语言' }, + { id: 'chinese-education', name: '华文教育' }, + { id: 'e-commerce', name: '电子商务' }, + { id: 'new-energy', name: '新能源' }, + { id: 'smart-education', name: '智慧教育' } ]) const activeTab = ref('all') @@ -328,32 +328,35 @@ const goToPage = (page: number) => { display: flex; gap: 0; margin-bottom: 40px; - background: white; - border-radius: 8px; - padding: 8px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + background: transparent; + border-bottom: 1px solid #e5e5e5; + padding: 0; + box-shadow: none; } .filter-tab { - padding: 12px 24px; + padding: 12px 20px; border: none; background: transparent; color: #666; font-size: 14px; cursor: pointer; - border-radius: 6px; + border-radius: 0; transition: all 0.3s; white-space: nowrap; + position: relative; + border-bottom: 2px solid transparent; } .filter-tab:hover { - background: #f8f9fa; - color: #333; + color: #4A90E2; } .filter-tab.active { - background: #4A90E2; - color: white; + background: transparent; + color: #4A90E2; + border-bottom-color: #4A90E2; + font-weight: 500; } /* 师资卡片网格 */ @@ -546,12 +549,15 @@ const goToPage = (page: number) => { .filter-tabs { flex-wrap: wrap; - gap: 8px; + gap: 0; + overflow-x: auto; + -webkit-overflow-scrolling: touch; } .filter-tab { - padding: 8px 16px; + padding: 10px 16px; font-size: 13px; + flex-shrink: 0; } .placeholder-icon {