修改课程播放视频代码

This commit is contained in:
username 2025-07-29 16:04:50 +08:00
parent c7453da854
commit ab31374e1a
4 changed files with 194 additions and 70 deletions

View File

@ -7,10 +7,7 @@
<title>在线学习平台</title> <title>在线学习平台</title>
<meta name="description" content="专业的在线学习平台,提供优质的编程和技术课程"> <meta name="description" content="专业的在线学习平台,提供优质的编程和技术课程">
<meta name="keywords" content="在线学习,编程课程,技术培训,Vue.js,React,Node.js"> <meta name="keywords" content="在线学习,编程课程,技术培训,Vue.js,React,Node.js">
<!-- CKPlayer CSS --> <!-- CKPlayer 将通过本地包引入移除CDN引用 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ckplayer@8.3.5/dist/ckplayer.css">
<!-- CKPlayer JS -->
<script src="https://cdn.jsdelivr.net/npm/ckplayer@8.3.5/dist/ckplayer.js"></script>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>

View File

@ -93,9 +93,55 @@ export class CourseApi {
// 调用后端API // 调用后端API
const response = await ApiRequest.get<BackendCourseListResponse>('/lesson/list', queryParams) const response = await ApiRequest.get<BackendCourseListResponse>('/lesson/list', queryParams)
console.log('课程列表API响应:', response) 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, id: backendCourse.id,
title: backendCourse.name, title: backendCourse.name,
description: backendCourse.description, description: backendCourse.description,
@ -140,20 +186,33 @@ export class CourseApi {
})) }))
const adaptedResponse: ApiResponse<PaginationResponse<Course>> = { const adaptedResponse: ApiResponse<PaginationResponse<Course>> = {
code: response.code, code: actualCode,
message: response.message, message: actualMessage,
data: { data: {
list: adaptedCourses, list: adaptedCourses,
total: response.data.total, total: actualData.total || 0,
page: params?.page || 1, page: params?.page || 1,
pageSize: params?.pageSize || 10, pageSize: params?.pageSize || 10,
totalPages: Math.ceil(response.data.total / (params?.pageSize || 10)) totalPages: Math.ceil((actualData.total || 0) / (params?.pageSize || 10))
} }
} }
return adaptedResponse return adaptedResponse
} catch (error) { } 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<BackendCourse>('/lesson/detail', { id }) const response = await ApiRequest.get<BackendCourse>('/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 = { const adaptedCourse: Course = {
id: response.data.id, id: actualData.id,
title: response.data.name, title: actualData.name,
description: response.data.description, description: actualData.description,
content: response.data.outline, // 使用 outline 作为课程内容 content: actualData.outline, // 使用 outline 作为课程内容
thumbnail: response.data.cover, thumbnail: actualData.cover,
coverImage: response.data.cover, coverImage: actualData.cover,
price: parseFloat(response.data.price || '0'), price: parseFloat(actualData.price || '0'),
originalPrice: parseFloat(response.data.price || '0'), originalPrice: parseFloat(actualData.price || '0'),
currency: 'CNY', currency: 'CNY',
rating: 4.5, rating: 4.5,
ratingCount: 0, ratingCount: 0,
studentsCount: 0, studentsCount: 0,
duration: this.calculateDuration(response.data.startTime, response.data.endTime), duration: this.calculateDuration(actualData.startTime, actualData.endTime),
totalLessons: 0, totalLessons: 0,
level: 'beginner' as const, level: 'beginner' as const,
language: 'zh-CN', language: 'zh-CN',
category: { category: {
id: response.data.categoryId, id: actualData.categoryId,
name: '未分类', name: '未分类',
slug: 'uncategorized' slug: 'uncategorized'
}, },
tags: [], tags: [],
skills: [], skills: [],
requirements: response.data.prerequisite ? [response.data.prerequisite] : [], requirements: actualData.prerequisite ? [actualData.prerequisite] : [],
objectives: response.data.target ? [response.data.target] : [], objectives: actualData.target ? [actualData.target] : [],
instructor: { instructor: {
id: response.data.teacherId || 0, id: actualData.teacherId || 0,
name: response.data.school || '未知讲师', name: actualData.school || '未知讲师',
title: '讲师', title: '讲师',
bio: response.data.position || '', bio: actualData.position || '',
avatar: '', avatar: '',
rating: 4.5, rating: 4.5,
studentsCount: 0, studentsCount: 0,
coursesCount: 0, coursesCount: 0,
experience: response.data.arrangement || '', experience: actualData.arrangement || '',
education: [], education: [],
certifications: [] certifications: []
}, },
status: 'published' as const, status: 'published' as const,
createdAt: this.formatTimestamp(response.data.createdTime), createdAt: this.formatTimestamp(actualData.createdTime),
updatedAt: this.formatTimestamp(response.data.updatedTime), updatedAt: this.formatTimestamp(actualData.updatedTime),
publishedAt: response.data.startTime publishedAt: actualData.startTime
} }
return { return {
code: response.code, code: actualCode,
message: response.message, message: actualMessage,
data: adaptedCourse data: adaptedCourse
} }
} catch (error) { } catch (error) {
@ -300,28 +381,54 @@ export class CourseApi {
const backendResponse = await ApiRequest.get<BackendCourseSectionListResponse>('/lesson/section/list', { lesson_id: lessonId.toString() }) const backendResponse = await ApiRequest.get<BackendCourseSectionListResponse>('/lesson/section/list', { lesson_id: lessonId.toString() })
console.log('章节API响应:', backendResponse) console.log('章节API响应:', backendResponse)
console.log('响应状态码:', backendResponse.code)
console.log('响应消息:', backendResponse.message) // 检查是否是axios响应格式还是我们的ApiResponse格式
console.log('原始章节数据:', backendResponse.data?.list) let actualData: any
console.log('章节数据数量:', backendResponse.data?.list?.length || 0) 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) { if (!actualData || !Array.isArray(actualData.list)) {
console.warn('API返回的数据结构不正确:', backendResponse.data) console.warn('API返回的数据结构不正确:', actualData)
return { return {
code: backendResponse.code, code: actualCode,
message: backendResponse.message, message: actualMessage,
data: { data: {
list: [], list: [],
timestamp: Date.now(), 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, id: section.id,
lessonId: section.lessonId, lessonId: section.lessonId,
outline: section.videoUrl, // 将videoUrl映射到outline outline: section.videoUrl, // 将videoUrl映射到outline
@ -340,14 +447,14 @@ export class CourseApi {
console.log('适配后的章节数据:', adaptedSections) console.log('适配后的章节数据:', adaptedSections)
const adaptedResponse: ApiResponse<CourseSectionListResponse> = { const adaptedResponse: ApiResponse<CourseSectionListResponse> = {
code: backendResponse.code, code: actualCode,
message: backendResponse.message, message: actualMessage,
data: { data: {
list: adaptedSections, list: adaptedSections,
timestamp: Date.now(), timestamp: Date.now(),
traceId: backendResponse.timestamp?.toString() || '' traceId: actualTimestamp || ''
}, },
timestamp: backendResponse.timestamp?.toString() timestamp: actualTimestamp
} }
return adaptedResponse return adaptedResponse

View File

@ -355,16 +355,30 @@ const loadCourses = async () => {
keyword: selectedMajor.value !== '全部' ? selectedMajor.value : undefined keyword: selectedMajor.value !== '全部' ? selectedMajor.value : undefined
} }
console.log('加载课程参数:', params)
const response = await CourseApi.getCourses(params) const response = await CourseApi.getCourses(params)
console.log('课程API响应:', response)
if (response && response.data) {
if (response.code === 0 || response.code === 200) { if (response.code === 0 || response.code === 200) {
courses.value = response.data.list courses.value = response.data.list || []
total.value = response.data.total total.value = response.data.total || 0
console.log('课程加载成功:', courses.value.length, '条课程')
} else { } else {
console.error('获取课程列表失败:', response.message) console.error('获取课程列表失败:', response.message)
courses.value = []
total.value = 0
}
} else {
console.error('API响应格式异常:', response)
courses.value = []
total.value = 0
} }
} catch (error) { } catch (error) {
console.error('加载课程失败:', error) console.error('加载课程失败:', error)
courses.value = []
total.value = 0
//
} finally { } finally {
loading.value = false loading.value = false
} }

View File

@ -106,14 +106,14 @@ const hasBannerImage = computed(() => bannerImageSrc.value.trim() !== '')
// //
const filterTabs = ref([ const filterTabs = ref([
{ id: 'all', name: '全部讲师' }, { id: 'field', name: '课长领域' },
{ id: 'main', name: '主讲' }, { id: 'all', name: '全部' },
{ id: 'international', name: '注册国际讲师' }, { id: 'chinese-promotion', name: '汉语国际推广' },
{ id: 'consultant', name: '咨询师' }, { id: 'chinese-language', name: '汉语言' },
{ id: 'expert', name: '专家顾问' }, { id: 'chinese-education', name: '华文教育' },
{ id: 'senior', name: '资深讲师' }, { id: 'e-commerce', name: '电子商务' },
{ id: 'featured', name: '金牌讲师' }, { id: 'new-energy', name: '新能源' },
{ id: 'enterprise', name: '企业导师' } { id: 'smart-education', name: '智慧教育' }
]) ])
const activeTab = ref('all') const activeTab = ref('all')
@ -328,32 +328,35 @@ const goToPage = (page: number) => {
display: flex; display: flex;
gap: 0; gap: 0;
margin-bottom: 40px; margin-bottom: 40px;
background: white; background: transparent;
border-radius: 8px; border-bottom: 1px solid #e5e5e5;
padding: 8px; padding: 0;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); box-shadow: none;
} }
.filter-tab { .filter-tab {
padding: 12px 24px; padding: 12px 20px;
border: none; border: none;
background: transparent; background: transparent;
color: #666; color: #666;
font-size: 14px; font-size: 14px;
cursor: pointer; cursor: pointer;
border-radius: 6px; border-radius: 0;
transition: all 0.3s; transition: all 0.3s;
white-space: nowrap; white-space: nowrap;
position: relative;
border-bottom: 2px solid transparent;
} }
.filter-tab:hover { .filter-tab:hover {
background: #f8f9fa; color: #4A90E2;
color: #333;
} }
.filter-tab.active { .filter-tab.active {
background: #4A90E2; background: transparent;
color: white; color: #4A90E2;
border-bottom-color: #4A90E2;
font-weight: 500;
} }
/* 师资卡片网格 */ /* 师资卡片网格 */
@ -546,12 +549,15 @@ const goToPage = (page: number) => {
.filter-tabs { .filter-tabs {
flex-wrap: wrap; flex-wrap: wrap;
gap: 8px; gap: 0;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
} }
.filter-tab { .filter-tab {
padding: 8px 16px; padding: 10px 16px;
font-size: 13px; font-size: 13px;
flex-shrink: 0;
} }
.placeholder-icon { .placeholder-icon {