feat+fix:mock数据后端返回不匹配类型报错,讲师章节树状对接
This commit is contained in:
parent
0e1a73192f
commit
f4a5f6f782
@ -15,7 +15,7 @@ import type {
|
||||
CourseSection,
|
||||
CourseSectionListResponse,
|
||||
BackendCourseSection,
|
||||
BackendCourseSectionListResponse,
|
||||
BackendInstructor,
|
||||
Quiz,
|
||||
LearningProgress,
|
||||
SearchRequest,
|
||||
@ -551,94 +551,65 @@ export class CourseApi {
|
||||
}
|
||||
|
||||
// 获取课程章节列表
|
||||
static async getCourseSections(lessonId: string): Promise<ApiResponse<CourseSectionListResponse>> {
|
||||
static async getCourseSections(courseId: string): Promise<ApiResponse<CourseSectionListResponse>> {
|
||||
try {
|
||||
console.log('尝试从API获取课程章节数据,课程ID:', lessonId)
|
||||
console.log('API请求URL: /lesson/section/list')
|
||||
console.log('API请求参数:', { lesson_id: lessonId })
|
||||
console.log('🔍 获取课程章节数据,课程ID:', courseId)
|
||||
console.log('🔍 API请求URL: /biz/course/' + courseId + '/section')
|
||||
|
||||
const backendResponse = await ApiRequest.get<BackendCourseSectionListResponse>('/lesson/section/list', { lesson_id: lessonId })
|
||||
console.log('章节API响应:', backendResponse)
|
||||
const response = await ApiRequest.get<any>(`/biz/course/${courseId}/section`)
|
||||
console.log('🔍 章节API响应:', response)
|
||||
|
||||
// 检查是否是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 (!actualData || !Array.isArray(actualData.list)) {
|
||||
console.warn('API返回的数据结构不正确:', actualData)
|
||||
return {
|
||||
code: actualCode,
|
||||
message: actualMessage,
|
||||
data: {
|
||||
list: [],
|
||||
timestamp: Date.now(),
|
||||
traceId: actualTimestamp || ''
|
||||
},
|
||||
timestamp: actualTimestamp
|
||||
}
|
||||
}
|
||||
// 处理后端响应格式
|
||||
if (response.data && response.data.success && response.data.result) {
|
||||
console.log('✅ 响应状态码:', response.data.code)
|
||||
console.log('✅ 响应消息:', response.data.message)
|
||||
console.log('✅ 原始章节数据:', response.data.result)
|
||||
console.log('✅ 章节数据数量:', response.data.result.length || 0)
|
||||
|
||||
// 适配数据格式
|
||||
const adaptedSections: CourseSection[] = actualData.list.map((section: BackendCourseSection) => ({
|
||||
const adaptedSections: CourseSection[] = response.data.result.map((section: BackendCourseSection) => ({
|
||||
id: section.id,
|
||||
lessonId: section.lessonId,
|
||||
outline: section.videoUrl, // 将videoUrl映射到outline
|
||||
lessonId: section.courseId, // 使用courseId作为lessonId
|
||||
outline: '', // 暂时为空,根据type可以设置不同的内容
|
||||
name: section.name,
|
||||
type: section.type,
|
||||
parentId: section.parentId,
|
||||
sort: section.sortOrder, // 将sortOrder映射到sort
|
||||
level: section.level === 0 ? 1 : 0, // 转换level逻辑:API中0=子级,1=父级;前端中0=父级,1=子级
|
||||
revision: section.revision,
|
||||
createdAt: section.createdTime ? new Date(section.createdTime).getTime() : null,
|
||||
updatedAt: section.updatedTime ? new Date(section.updatedTime).getTime() : null,
|
||||
sort: section.sortOrder,
|
||||
level: section.level,
|
||||
revision: 1, // 默认版本号
|
||||
createdAt: section.createTime ? new Date(section.createTime).getTime() : null,
|
||||
updatedAt: section.updateTime ? new Date(section.updateTime).getTime() : null,
|
||||
deletedAt: null,
|
||||
completed: false,
|
||||
duration: undefined
|
||||
}))
|
||||
|
||||
console.log('适配后的章节数据:', adaptedSections)
|
||||
console.log('✅ 适配后的章节数据:', adaptedSections)
|
||||
|
||||
const adaptedResponse: ApiResponse<CourseSectionListResponse> = {
|
||||
code: actualCode,
|
||||
message: actualMessage,
|
||||
return {
|
||||
code: response.data.code,
|
||||
message: response.data.message,
|
||||
data: {
|
||||
list: adaptedSections,
|
||||
timestamp: Date.now(),
|
||||
traceId: actualTimestamp || ''
|
||||
},
|
||||
timestamp: actualTimestamp
|
||||
traceId: response.data.timestamp?.toString() || ''
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.warn('⚠️ API返回的数据结构不正确:', response.data)
|
||||
return {
|
||||
code: 500,
|
||||
message: '数据格式错误',
|
||||
data: {
|
||||
list: [],
|
||||
timestamp: Date.now(),
|
||||
traceId: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return adaptedResponse
|
||||
} catch (error) {
|
||||
console.error('章节API调用失败:', error)
|
||||
console.error('错误详情:', {
|
||||
console.error('❌ 章节API调用失败:', error)
|
||||
console.error('❌ 错误详情:', {
|
||||
message: (error as Error).message,
|
||||
stack: (error as Error).stack,
|
||||
response: (error as any).response?.data,
|
||||
@ -791,6 +762,66 @@ export class CourseApi {
|
||||
return ApiRequest.get(`/courses/${courseId}/access`)
|
||||
}
|
||||
|
||||
// 获取课程讲师列表
|
||||
static async getCourseInstructors(courseId: string): Promise<ApiResponse<Instructor[]>> {
|
||||
try {
|
||||
console.log('🔍 获取课程讲师数据,课程ID:', courseId)
|
||||
console.log('🔍 API请求URL: /biz/course/' + courseId + '/teachers')
|
||||
|
||||
const response = await ApiRequest.get<any>(`/biz/course/${courseId}/teachers`)
|
||||
console.log('🔍 讲师API响应:', response)
|
||||
|
||||
// 处理后端响应格式
|
||||
if (response.data && response.data.success && response.data.result) {
|
||||
console.log('✅ 响应状态码:', response.data.code)
|
||||
console.log('✅ 响应消息:', response.data.message)
|
||||
console.log('✅ 原始讲师数据:', response.data.result)
|
||||
console.log('✅ 讲师数据数量:', response.data.result.length || 0)
|
||||
|
||||
// 适配数据格式
|
||||
const adaptedInstructors: Instructor[] = response.data.result.map((instructor: BackendInstructor) => ({
|
||||
id: parseInt(instructor.id) || 0, // 转换为数字ID
|
||||
name: instructor.name,
|
||||
title: instructor.title,
|
||||
bio: instructor.tag || '', // 使用tag作为bio
|
||||
avatar: instructor.avatar,
|
||||
rating: 4.8, // 默认评分
|
||||
studentsCount: 1000, // 默认学生数
|
||||
coursesCount: 10, // 默认课程数
|
||||
experience: '5年教学经验', // 默认经验
|
||||
education: ['计算机科学硕士'], // 默认教育背景
|
||||
certifications: ['高级讲师认证'] // 默认认证
|
||||
}))
|
||||
|
||||
console.log('✅ 适配后的讲师数据:', adaptedInstructors)
|
||||
|
||||
return {
|
||||
code: response.data.code,
|
||||
message: response.data.message,
|
||||
data: adaptedInstructors
|
||||
}
|
||||
} else {
|
||||
console.warn('⚠️ API返回的数据结构不正确:', response.data)
|
||||
return {
|
||||
code: 500,
|
||||
message: '数据格式错误',
|
||||
data: []
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ 讲师API调用失败:', error)
|
||||
console.error('❌ 错误详情:', {
|
||||
message: (error as Error).message,
|
||||
stack: (error as Error).stack,
|
||||
response: (error as any).response?.data,
|
||||
status: (error as any).response?.status,
|
||||
statusText: (error as any).response?.statusText
|
||||
})
|
||||
|
||||
// 重新抛出错误,不使用模拟数据
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -368,32 +368,32 @@ export interface LessonResource {
|
||||
|
||||
// 后端API返回的章节数据结构
|
||||
export interface BackendCourseSection {
|
||||
id: number
|
||||
lessonId: number
|
||||
videoUrl: string // 视频链接
|
||||
id: string
|
||||
courseId: string
|
||||
name: string // 章节名称
|
||||
type: number // 章节类型:0=视频、1=资料、2=考试、3=作业
|
||||
sortOrder: number // 排序
|
||||
parentId: number // 父章节ID
|
||||
level: number // 层级:0=子级(课时),1=父级(章节)
|
||||
revision: number // 版本号
|
||||
createdBy: number
|
||||
createdTime: string | null
|
||||
updatedBy: number
|
||||
updatedTime: string | null
|
||||
parentId: string // 父章节ID
|
||||
level: number // 章节层级:0=一级章节、1=二级章节
|
||||
createBy: string
|
||||
createTime: string
|
||||
updateBy: string
|
||||
updateTime: string
|
||||
}
|
||||
|
||||
// 前端使用的课程章节类型(适配后的数据结构)
|
||||
export interface CourseSection {
|
||||
id: string // 改为string类型,保持一致性
|
||||
lessonId: string // 改为string类型,与Course.id保持一致
|
||||
outline: string // 章节大纲/内容链接(从videoUrl适配)
|
||||
outline: string // 章节大纲/内容链接
|
||||
name: string // 章节名称
|
||||
parentId: number // 父章节ID
|
||||
type: number // 章节类型:0=视频、1=资料、2=考试、3=作业
|
||||
parentId: string // 父章节ID,改为string类型
|
||||
sort: number // 排序(从sortOrder适配)
|
||||
level: number // 层级:0=父级(章节),1=子级(课时)- 已从后端数据转换
|
||||
level: number // 层级:0=一级章节、1=二级章节
|
||||
revision: number // 版本号
|
||||
createdAt: number | null // 从createdTime适配
|
||||
updatedAt: number | null // 从updatedTime适配
|
||||
createdAt: number | null // 从createTime适配
|
||||
updatedAt: number | null // 从updateTime适配
|
||||
deletedAt: string | null
|
||||
completed?: boolean // 是否已完成(前端状态)
|
||||
duration?: string // 课时时长(前端计算)
|
||||
@ -401,8 +401,29 @@ export interface CourseSection {
|
||||
|
||||
// 后端章节列表响应格式
|
||||
export interface BackendCourseSectionListResponse {
|
||||
list: BackendCourseSection[]
|
||||
total: number
|
||||
success: boolean
|
||||
message: string
|
||||
code: number
|
||||
result: BackendCourseSection[]
|
||||
timestamp: number
|
||||
}
|
||||
|
||||
// 后端讲师数据结构
|
||||
export interface BackendInstructor {
|
||||
id: string
|
||||
name: string
|
||||
avatar: string
|
||||
title: string
|
||||
tag: string
|
||||
}
|
||||
|
||||
// 后端讲师列表响应格式
|
||||
export interface BackendInstructorListResponse {
|
||||
success: boolean
|
||||
message: string
|
||||
code: number
|
||||
result: BackendInstructor[]
|
||||
timestamp: number
|
||||
}
|
||||
|
||||
// 前端章节列表响应格式
|
||||
|
@ -140,7 +140,15 @@
|
||||
<!-- 讲师信息 -->
|
||||
<div class="instructors-section">
|
||||
<h3 class="section-title">讲师</h3>
|
||||
<div class="instructors-list">
|
||||
|
||||
<div v-if="instructorsLoading" class="instructors-loading">
|
||||
<p>正在加载讲师信息...</p>
|
||||
</div>
|
||||
<div v-else-if="instructorsError" class="instructors-error">
|
||||
<p>{{ instructorsError }}</p>
|
||||
<button @click="loadCourseInstructors" class="retry-btn">重试</button>
|
||||
</div>
|
||||
<div v-else class="instructors-list">
|
||||
<div class="instructor-item" v-for="instructor in instructors" :key="instructor.id">
|
||||
<div class="instructor-avatar">
|
||||
<SafeAvatar :src="instructor.avatar" :name="instructor.name" :size="50" />
|
||||
@ -480,6 +488,10 @@ const courseSections = ref<CourseSection[]>([])
|
||||
const sectionsLoading = ref(false)
|
||||
const sectionsError = ref('')
|
||||
|
||||
// 讲师数据
|
||||
const instructorsLoading = ref(false)
|
||||
const instructorsError = ref('')
|
||||
|
||||
// 报名状态管理
|
||||
const isEnrolled = ref(false) // 用户是否已报名该课程
|
||||
const enrollmentLoading = ref(false) // 报名加载状态
|
||||
@ -525,46 +537,12 @@ interface ChapterGroup {
|
||||
|
||||
const groupedSections = ref<ChapterGroup[]>([])
|
||||
|
||||
// 生成模拟章节数据(用于演示)
|
||||
const generateMockSections = (): CourseSection[] => {
|
||||
return [
|
||||
// 第一章 - 课前准备 (4个)
|
||||
{ id: "1", lessonId: courseId.value, name: '开课彩蛋:新开始新征程', outline: 'https://example.com/video1.m3u8', parentId: 0, sort: 1, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: true, duration: '01:03:56' },
|
||||
{ id: "2", lessonId: courseId.value, name: '课程定位与目标', outline: 'https://example.com/video2.m3u8', parentId: 0, sort: 2, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: true, duration: '00:44:05' },
|
||||
{ id: "3", lessonId: courseId.value, name: '教学安排及学习建议', outline: 'https://example.com/video3.m3u8', parentId: 0, sort: 3, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: true, duration: '00:52:22' },
|
||||
{ id: "4", lessonId: courseId.value, name: '课前准备PPT', outline: 'https://example.com/ppt1.ppt', parentId: 0, sort: 4, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: undefined },
|
||||
// 生成模拟章节数据(暂时禁用,等待API修复)
|
||||
// const generateMockSections = (): CourseSection[] => {
|
||||
// return []
|
||||
// }
|
||||
|
||||
// 第二章 - 程序设计基础知识 (5个)
|
||||
{ id: "5", lessonId: courseId.value, name: '第一课 程序设计入门', outline: 'https://example.com/video4.m3u8', parentId: 0, sort: 5, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: true, duration: '00:52:22' },
|
||||
{ id: "6", lessonId: courseId.value, name: '操作PPT', outline: 'https://example.com/ppt2.ppt', parentId: 0, sort: 6, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: undefined },
|
||||
{ id: "7", lessonId: courseId.value, name: '第二课 循环结构', outline: 'https://example.com/video5.m3u8', parentId: 0, sort: 7, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: true, duration: '01:03:56' },
|
||||
{ id: "8", lessonId: courseId.value, name: '函数&循环', outline: 'https://example.com/video5.m3u8', parentId: 0, sort: 8, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: undefined },
|
||||
{ id: "9", lessonId: courseId.value, name: '第三课 条件结构', outline: 'https://example.com/video6.m3u8', parentId: 0, sort: 9, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: '00:45:30' },
|
||||
|
||||
// 第三章 - 实战项目 (6个)
|
||||
{ id: "10", lessonId: courseId.value, name: '项目一:计算器开发', outline: 'https://example.com/video7.m3u8', parentId: 0, sort: 10, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: '01:20:15' },
|
||||
{ id: "11", lessonId: courseId.value, name: '项目源码下载', outline: 'https://example.com/source1.zip', parentId: 0, sort: 11, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: undefined },
|
||||
{ id: "12", lessonId: courseId.value, name: '项目二:数据管理系统', outline: 'https://example.com/video8.m3u8', parentId: 0, sort: 12, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: '01:45:20' },
|
||||
{ id: "13", lessonId: courseId.value, name: '作业:完成个人项目', outline: '', parentId: 0, sort: 13, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: undefined },
|
||||
{ id: "14", lessonId: courseId.value, name: '项目三:Web应用开发', outline: 'https://example.com/video9.m3u8', parentId: 0, sort: 14, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: '02:10:45' },
|
||||
{ id: "15", lessonId: courseId.value, name: '期末考试', outline: '', parentId: 0, sort: 15, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: undefined },
|
||||
|
||||
// 第四章 - 高级应用 (4个)
|
||||
{ id: "16", lessonId: courseId.value, name: '高级特性介绍', outline: 'https://example.com/video10.m3u8', parentId: 0, sort: 16, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: '00:55:30' },
|
||||
{ id: "17", lessonId: courseId.value, name: '性能优化技巧', outline: 'https://example.com/video11.m3u8', parentId: 0, sort: 17, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: '01:15:20' },
|
||||
{ id: "18", lessonId: courseId.value, name: '部署与发布', outline: 'https://example.com/video12.m3u8', parentId: 0, sort: 18, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: '00:40:15' },
|
||||
{ id: "19", lessonId: courseId.value, name: '课程总结', outline: 'https://example.com/video13.m3u8', parentId: 0, sort: 19, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: '00:30:10' },
|
||||
|
||||
// 第五章 - 拓展学习 (3个)
|
||||
{ id: "20", lessonId: courseId.value, name: '行业发展趋势', outline: 'https://example.com/video14.m3u8', parentId: 0, sort: 20, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: '00:35:45' },
|
||||
{ id: "21", lessonId: courseId.value, name: '学习资源推荐', outline: 'https://example.com/resources.pdf', parentId: 0, sort: 21, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: undefined },
|
||||
{ id: "22", lessonId: courseId.value, name: '结业证书申请', outline: '', parentId: 0, sort: 22, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: undefined },
|
||||
|
||||
// 第六章 - 答疑与交流 (2个)
|
||||
{ id: "23", lessonId: courseId.value, name: '常见问题解答', outline: 'https://example.com/video15.m3u8', parentId: 0, sort: 23, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: '00:25:30' },
|
||||
{ id: "24", lessonId: courseId.value, name: '在线答疑直播', outline: 'https://example.com/live1.m3u8', parentId: 0, sort: 24, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: '01:30:00' }
|
||||
]
|
||||
}
|
||||
|
||||
// 将章节按章分组
|
||||
const groupSectionsByChapter = (sections: CourseSection[]) => {
|
||||
@ -783,17 +761,17 @@ const loadCourseSections = async () => {
|
||||
console.log('章节API响应:', response)
|
||||
|
||||
if (response.code === 0 || response.code === 200) {
|
||||
if (response.data && Array.isArray(response.data)) {
|
||||
courseSections.value = response.data
|
||||
groupedSections.value = groupSectionsByChapter(response.data)
|
||||
console.log('章节数据设置成功:', courseSections.value)
|
||||
console.log('分组数据:', groupedSections.value)
|
||||
if (response.data && response.data.list && Array.isArray(response.data.list)) {
|
||||
courseSections.value = response.data.list
|
||||
groupedSections.value = groupSectionsByChapter(response.data.list)
|
||||
console.log('✅ 章节数据设置成功:', courseSections.value)
|
||||
console.log('✅ 分组数据:', groupedSections.value)
|
||||
} else {
|
||||
console.log('API返回的章节数据为空,使用模拟数据')
|
||||
console.log('⚠️ API返回的章节数据为空,使用模拟数据')
|
||||
loadMockData()
|
||||
}
|
||||
} else {
|
||||
console.log('API返回错误,使用模拟数据')
|
||||
console.log('⚠️ API返回错误,使用模拟数据')
|
||||
loadMockData()
|
||||
}
|
||||
} catch (err) {
|
||||
@ -807,15 +785,47 @@ const loadCourseSections = async () => {
|
||||
|
||||
// 加载模拟数据
|
||||
const loadMockData = () => {
|
||||
console.log('加载模拟章节数据')
|
||||
const mockSections = generateMockSections()
|
||||
courseSections.value = mockSections
|
||||
groupedSections.value = groupSectionsByChapter(mockSections)
|
||||
console.log('⚠️ API调用失败,暂不使用模拟数据')
|
||||
// 暂时不加载模拟数据,等待API修复
|
||||
courseSections.value = []
|
||||
groupedSections.value = []
|
||||
}
|
||||
|
||||
// 计算学习进度
|
||||
// const completed = mockSections.filter(section => section.completed).length
|
||||
// completedLessons.value = completed
|
||||
// progress.value = Math.round((completed / mockSections.length) * 100)
|
||||
// 加载课程讲师列表
|
||||
const loadCourseInstructors = async () => {
|
||||
if (!courseId.value || courseId.value.trim() === '') {
|
||||
instructorsError.value = '课程ID无效'
|
||||
console.error('课程ID无效:', courseId.value)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
instructorsLoading.value = true
|
||||
instructorsError.value = ''
|
||||
|
||||
console.log('调用API获取课程讲师...')
|
||||
const response = await CourseApi.getCourseInstructors(courseId.value)
|
||||
console.log('讲师API响应:', response)
|
||||
|
||||
if (response.code === 0 || response.code === 200) {
|
||||
if (response.data && Array.isArray(response.data)) {
|
||||
instructors.value = response.data
|
||||
console.log('✅ 讲师数据设置成功:', instructors.value)
|
||||
} else {
|
||||
console.log('⚠️ API返回的讲师数据为空,使用默认数据')
|
||||
// 保持默认的mock数据
|
||||
}
|
||||
} else {
|
||||
console.log('⚠️ API返回错误,使用默认数据')
|
||||
instructorsError.value = response.message || '获取讲师信息失败'
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('加载课程讲师失败:', err)
|
||||
instructorsError.value = '获取讲师信息失败'
|
||||
// 保持默认的mock数据
|
||||
} finally {
|
||||
instructorsLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 切换章节展开/折叠
|
||||
@ -1131,7 +1141,8 @@ onMounted(() => {
|
||||
console.log('课程详情页加载完成,课程ID:', courseId.value)
|
||||
initializeMockState() // 初始化模拟状态
|
||||
loadCourseDetail()
|
||||
// loadCourseSections() // 暂时禁用章节接口调用,因为接口不存在
|
||||
loadCourseSections() // 启用章节接口调用
|
||||
loadCourseInstructors() // 启用讲师接口调用
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -1821,6 +1832,34 @@ onMounted(() => {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 讲师加载状态 */
|
||||
.instructors-loading {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.instructors-error {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.instructors-error .retry-btn {
|
||||
margin-top: 10px;
|
||||
padding: 6px 12px;
|
||||
background: #1890ff;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.instructors-error .retry-btn:hover {
|
||||
background: #40a9ff;
|
||||
}
|
||||
|
||||
/* 分隔线样式 */
|
||||
.course-info-divider {
|
||||
height: 1px;
|
||||
|
@ -534,45 +534,10 @@ const displayComments = ref([
|
||||
}
|
||||
])
|
||||
|
||||
// 生成模拟章节数据(用于演示)
|
||||
// 生成模拟章节数据(暂时禁用)
|
||||
const generateMockSections = (): CourseSection[] => {
|
||||
return [
|
||||
// 第一章 - 课前准备 (4个)
|
||||
{ id: "1", lessonId: courseId.value, name: '开课彩蛋:新开始新征程', outline: 'https://example.com/video1.m3u8', parentId: 0, sort: 1, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: true, duration: '01:03:56' },
|
||||
{ id: "2", lessonId: courseId.value, name: '课程定位与目标', outline: 'https://example.com/video2.m3u8', parentId: 0, sort: 2, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: true, duration: '00:44:05' },
|
||||
{ id: "3", lessonId: courseId.value, name: '教学安排及学习建议', outline: 'https://example.com/video3.m3u8', parentId: 0, sort: 3, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: true, duration: '00:52:22' },
|
||||
{ id: "4", lessonId: courseId.value, name: '课前准备PPT', outline: 'https://example.com/ppt1.ppt', parentId: 0, sort: 4, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: undefined },
|
||||
|
||||
// 第二章 - 程序设计基础知识 (5个)
|
||||
{ id: "5", lessonId: courseId.value, name: '第一课 程序设计入门', outline: 'https://example.com/video4.m3u8', parentId: 0, sort: 5, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: true, duration: '00:52:22' },
|
||||
{ id: "6", lessonId: courseId.value, name: '操作PPT', outline: 'https://example.com/ppt2.ppt', parentId: 0, sort: 6, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: undefined },
|
||||
{ id: "7", lessonId: courseId.value, name: '第二课 循环结构', outline: 'https://example.com/video5.m3u8', parentId: 0, sort: 7, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: true, duration: '01:03:56' },
|
||||
{ id: "8", lessonId: courseId.value, name: '函数&循环', outline: 'https://example.com/video5.m3u8', parentId: 0, sort: 8, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: undefined },
|
||||
{ id: "9", lessonId: courseId.value, name: '第三课 条件结构', outline: 'https://example.com/video6.m3u8', parentId: 0, sort: 9, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: '00:45:30' },
|
||||
|
||||
// 第三章 - 实战项目 (6个)
|
||||
{ id: "10", lessonId: courseId.value, name: '项目一:计算器开发', outline: 'https://example.com/video7.m3u8', parentId: 0, sort: 10, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: '01:20:15' },
|
||||
{ id: "11", lessonId: courseId.value, name: '项目源码下载', outline: 'https://example.com/source1.zip', parentId: 0, sort: 11, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: undefined },
|
||||
{ id: "12", lessonId: courseId.value, name: '项目二:数据管理系统', outline: 'https://example.com/video8.m3u8', parentId: 0, sort: 12, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: '01:45:20' },
|
||||
{ id: "13", lessonId: courseId.value, name: '作业:完成个人项目', outline: '', parentId: 0, sort: 13, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: undefined },
|
||||
{ id: "14", lessonId: courseId.value, name: '项目三:Web应用开发', outline: 'https://example.com/video9.m3u8', parentId: 0, sort: 14, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: '02:10:45' },
|
||||
{ id: "15", lessonId: courseId.value, name: '期末考试', outline: '', parentId: 0, sort: 15, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: undefined },
|
||||
|
||||
// 第四章 - 高级应用 (4个)
|
||||
{ id: "16", lessonId: courseId.value, name: '高级特性介绍', outline: 'https://example.com/video10.m3u8', parentId: 0, sort: 16, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: '00:55:30' },
|
||||
{ id: "17", lessonId: courseId.value, name: '性能优化技巧', outline: 'https://example.com/video11.m3u8', parentId: 0, sort: 17, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: '01:15:20' },
|
||||
{ id: "18", lessonId: courseId.value, name: '部署与发布', outline: 'https://example.com/video12.m3u8', parentId: 0, sort: 18, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: '00:40:15' },
|
||||
{ id: "19", lessonId: courseId.value, name: '课程总结', outline: 'https://example.com/video13.m3u8', parentId: 0, sort: 19, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: '00:30:10' },
|
||||
|
||||
// 第五章 - 拓展学习 (3个)
|
||||
{ id: "20", lessonId: courseId.value, name: '行业发展趋势', outline: 'https://example.com/video14.m3u8', parentId: 0, sort: 20, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: '00:35:45' },
|
||||
{ id: "21", lessonId: courseId.value, name: '学习资源推荐', outline: 'https://example.com/resources.pdf', parentId: 0, sort: 21, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: undefined },
|
||||
{ id: "22", lessonId: courseId.value, name: '结业证书申请', outline: '', parentId: 0, sort: 22, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: undefined },
|
||||
|
||||
// 第六章 - 答疑与交流 (2个)
|
||||
{ id: "23", lessonId: courseId.value, name: '常见问题解答', outline: 'https://example.com/video15.m3u8', parentId: 0, sort: 23, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: '00:25:30' },
|
||||
{ id: "24", lessonId: courseId.value, name: '在线答疑直播', outline: 'https://example.com/live1.m3u8', parentId: 0, sort: 24, level: 1, revision: 1, createdAt: Date.now(), updatedAt: Date.now(), deletedAt: null, completed: false, duration: '01:30:00' }
|
||||
]
|
||||
// 暂时返回空数组,等待API修复
|
||||
return []
|
||||
}
|
||||
|
||||
// 将章节按章分组
|
||||
|
@ -541,14 +541,15 @@ const generateChapterGroups = () => {
|
||||
lessonId: courseId.value,
|
||||
outline: currentVideoUrl.value,
|
||||
name: currentVideoTitle.value || '开课彩蛋:新开始新征程',
|
||||
parentId: 1,
|
||||
parentId: "1",
|
||||
sort: 0,
|
||||
level: 1,
|
||||
revision: 0,
|
||||
createdAt: Date.now(),
|
||||
updatedAt: null,
|
||||
deletedAt: null,
|
||||
completed: false
|
||||
completed: false,
|
||||
type: 0
|
||||
}
|
||||
],
|
||||
expanded: true
|
||||
@ -556,7 +557,7 @@ const generateChapterGroups = () => {
|
||||
} else {
|
||||
parentSections.forEach((parentSection, index) => {
|
||||
const childSections = courseSections.value.filter(section =>
|
||||
section.level === 1 && section.parentId === parseInt(parentSection.id)
|
||||
section.level === 1 && section.parentId === parentSection.id
|
||||
)
|
||||
|
||||
groups.push({
|
||||
|
Loading…
x
Reference in New Issue
Block a user