feat+fix:mock数据后端返回不匹配类型报错,讲师章节树状对接

This commit is contained in:
小张 2025-08-15 18:12:31 +08:00
parent 0e1a73192f
commit f4a5f6f782
5 changed files with 246 additions and 189 deletions

View File

@ -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
}
}
}

View File

@ -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
}
// 前端章节列表响应格式

View File

@ -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;

View File

@ -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 []
}
//

View File

@ -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({