修改课程播放视频代码

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>
<meta name="description" content="专业的在线学习平台,提供优质的编程和技术课程">
<meta name="keywords" content="在线学习,编程课程,技术培训,Vue.js,React,Node.js">
<!-- CKPlayer CSS -->
<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>
<!-- CKPlayer 将通过本地包引入移除CDN引用 -->
</head>
<body>
<div id="app"></div>

View File

@ -93,9 +93,55 @@ export class CourseApi {
// 调用后端API
const response = await ApiRequest.get<BackendCourseListResponse>('/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<PaginationResponse<Course>> = {
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<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 = {
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<BackendCourseSectionListResponse>('/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<CourseSectionListResponse> = {
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

View File

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

View File

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