feat:练习讨论对接接口
This commit is contained in:
parent
7aa5257cbd
commit
ce54a41f4a
@ -176,6 +176,7 @@ export class AIApi {
|
|||||||
|
|
||||||
const decoder = new TextDecoder()
|
const decoder = new TextDecoder()
|
||||||
let buffer = ''
|
let buffer = ''
|
||||||
|
// @ts-ignore
|
||||||
let messageEndReceived = false
|
let messageEndReceived = false
|
||||||
let endTimeout: NodeJS.Timeout | null = null
|
let endTimeout: NodeJS.Timeout | null = null
|
||||||
|
|
||||||
@ -330,20 +331,21 @@ export class AIApi {
|
|||||||
* @returns Promise<void>
|
* @returns Promise<void>
|
||||||
*/
|
*/
|
||||||
static async sendChatMessageWithEventSource(
|
static async sendChatMessageWithEventSource(
|
||||||
content: string,
|
// @ts-ignore
|
||||||
|
content: string, // TODO: 需要在实际实现中使用此参数
|
||||||
onMessage: (chunk: string) => void,
|
onMessage: (chunk: string) => void,
|
||||||
onComplete?: () => void,
|
onComplete?: () => void,
|
||||||
onError?: (error: any) => void
|
onError?: (error: any) => void
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const request: AIChatRequest = {
|
// const request: AIChatRequest = {
|
||||||
appId: "1970031066993217537",
|
// appId: "1970031066993217537",
|
||||||
content: content,
|
// content: content,
|
||||||
responseMode: "streaming"
|
// responseMode: "streaming"
|
||||||
}
|
// } // 暂时未使用
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 获取token
|
// 获取token
|
||||||
const token = localStorage.getItem('token')
|
// const token = localStorage.getItem('token') // 暂时未使用
|
||||||
|
|
||||||
// 构建URL参数
|
// 构建URL参数
|
||||||
const url = new URL(`${import.meta.env.VITE_API_BASE_URL}/airag/chat/send`)
|
const url = new URL(`${import.meta.env.VITE_API_BASE_URL}/airag/chat/send`)
|
||||||
|
@ -916,6 +916,40 @@ export class CourseApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取考试题目列表
|
||||||
|
static async getExamQuestions(examId: string, studentId: string): Promise<ApiResponse<any>> {
|
||||||
|
try {
|
||||||
|
console.log('🚀 调用获取考试题目API,考试ID:', examId, '学生ID:', studentId)
|
||||||
|
|
||||||
|
// 直接在URL中添加studentId参数
|
||||||
|
const url = `/aiol/aiolExam/getExamQuestions/${examId}?studentId=${studentId}`
|
||||||
|
console.log('🔍 请求URL:', url)
|
||||||
|
|
||||||
|
const response = await ApiRequest.get<any>(url)
|
||||||
|
console.log('🔍 考试题目API响应:', response)
|
||||||
|
|
||||||
|
return response
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 获取考试题目失败:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取题目详情
|
||||||
|
static async getQuestionDetail(questionId: string): Promise<ApiResponse<any>> {
|
||||||
|
try {
|
||||||
|
console.log('🚀 调用获取题目详情API,题目ID:', questionId)
|
||||||
|
|
||||||
|
const response = await ApiRequest.get<any>(`/aiol/aiolRepo/repoList/${questionId}`)
|
||||||
|
console.log('🔍 题目详情API响应:', response)
|
||||||
|
|
||||||
|
return response
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 获取题目详情失败:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 获取章节讨论
|
// 获取章节讨论
|
||||||
static async getSectionDiscussion(courseId: string, sectionId: string): Promise<ApiResponse<any>> {
|
static async getSectionDiscussion(courseId: string, sectionId: string): Promise<ApiResponse<any>> {
|
||||||
try {
|
try {
|
||||||
|
@ -220,7 +220,7 @@ const categoryList = ref<CourseCategory[]>([]);
|
|||||||
const repoOptions = computed(() => [
|
const repoOptions = computed(() => [
|
||||||
{ label: '全部题库', value: '' },
|
{ label: '全部题库', value: '' },
|
||||||
...repoList.value.map((repo: Repo) => ({
|
...repoList.value.map((repo: Repo) => ({
|
||||||
label: `${repo.title} (${repo.questionCount || 0}题)`,
|
label: `${repo.title} (${repo.question_count || 0}题)`,
|
||||||
value: repo.id
|
value: repo.id
|
||||||
}))
|
}))
|
||||||
]);
|
]);
|
||||||
|
@ -664,6 +664,9 @@ const createManualQualityButton = () => {
|
|||||||
option.addEventListener('mouseleave', () => {
|
option.addEventListener('mouseleave', () => {
|
||||||
if (quality.value !== props.currentQuality) {
|
if (quality.value !== props.currentQuality) {
|
||||||
option.style.backgroundColor = 'transparent'
|
option.style.backgroundColor = 'transparent'
|
||||||
|
} else {
|
||||||
|
// 如果是当前选中的清晰度,保持蓝色背景
|
||||||
|
option.style.backgroundColor = '#007bff'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -698,6 +701,11 @@ const createManualQualityButton = () => {
|
|||||||
// 标记按钮已创建
|
// 标记按钮已创建
|
||||||
qualityButtonCreated.value = true
|
qualityButtonCreated.value = true
|
||||||
|
|
||||||
|
// 初始化菜单选项样式
|
||||||
|
setTimeout(() => {
|
||||||
|
updateQualityMenuStyles(props.currentQuality)
|
||||||
|
}, 100)
|
||||||
|
|
||||||
console.log('✅ 手动清晰度按钮创建完成')
|
console.log('✅ 手动清晰度按钮创建完成')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -707,6 +715,68 @@ const getCurrentQualityLabel = () => {
|
|||||||
return current ? current.label : '360p'
|
return current ? current.label : '360p'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新清晰度菜单选项的样式
|
||||||
|
const updateQualityMenuStyles = (currentQualityValue: string) => {
|
||||||
|
const qualityMenu = dplayerContainer.value?.querySelector('.manual-quality .dplayer-quality-list')
|
||||||
|
if (qualityMenu) {
|
||||||
|
const qualityItems = qualityMenu.querySelectorAll('.dplayer-quality-item')
|
||||||
|
qualityItems.forEach((item, index) => {
|
||||||
|
const quality = props.videoQualities[index]
|
||||||
|
if (quality) {
|
||||||
|
const isActive = quality.value === currentQualityValue
|
||||||
|
const itemElement = item as HTMLElement
|
||||||
|
|
||||||
|
// 设置基础样式
|
||||||
|
itemElement.style.cssText = `
|
||||||
|
padding: 8px 12px;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 13px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
text-align: center;
|
||||||
|
${isActive ? 'background: #007bff;' : 'background: transparent;'}
|
||||||
|
`
|
||||||
|
|
||||||
|
// 重新绑定鼠标事件,确保当前选中项的样式正确
|
||||||
|
const newItemElement = itemElement.cloneNode(true) as HTMLElement
|
||||||
|
itemElement.parentNode?.replaceChild(newItemElement, itemElement)
|
||||||
|
|
||||||
|
// 重新绑定事件
|
||||||
|
newItemElement.addEventListener('mouseenter', () => {
|
||||||
|
if (quality.value !== currentQualityValue) {
|
||||||
|
newItemElement.style.backgroundColor = 'rgba(255, 255, 255, 0.1)'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
newItemElement.addEventListener('mouseleave', () => {
|
||||||
|
if (quality.value !== currentQualityValue) {
|
||||||
|
newItemElement.style.backgroundColor = 'transparent'
|
||||||
|
} else {
|
||||||
|
// 如果是当前选中的清晰度,保持蓝色背景
|
||||||
|
newItemElement.style.backgroundColor = '#007bff'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
newItemElement.addEventListener('click', () => {
|
||||||
|
console.log('🔄 手动切换清晰度到:', quality.label)
|
||||||
|
switchQuality(quality)
|
||||||
|
;(qualityMenu as HTMLElement).style.display = 'none'
|
||||||
|
|
||||||
|
// 更新按钮文字
|
||||||
|
const qualityButton = dplayerContainer.value?.querySelector('.manual-quality .dplayer-quality-button')
|
||||||
|
if (qualityButton) {
|
||||||
|
qualityButton.textContent = quality.label
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不需要在这里再次调用updateQualityMenuStyles,因为switchQuality会处理
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(`🎨 更新清晰度选项样式: ${quality.label} - ${isActive ? '激活' : '未激活'}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const switchQuality = (quality: any) => {
|
const switchQuality = (quality: any) => {
|
||||||
console.log('🔄 开始切换清晰度:', {
|
console.log('🔄 开始切换清晰度:', {
|
||||||
quality: quality,
|
quality: quality,
|
||||||
@ -814,6 +884,9 @@ const switchQuality = (quality: any) => {
|
|||||||
existingButton.textContent = quality.label
|
existingButton.textContent = quality.label
|
||||||
console.log('✅ 清晰度按钮文字已立即更新为:', quality.label)
|
console.log('✅ 清晰度按钮文字已立即更新为:', quality.label)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新清晰度菜单选项的样式
|
||||||
|
updateQualityMenuStyles(quality.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -345,13 +345,13 @@
|
|||||||
<div class="discussion-container">
|
<div class="discussion-container">
|
||||||
<!-- 讨论标题行 -->
|
<!-- 讨论标题行 -->
|
||||||
<div class="discussion-title-row">
|
<div class="discussion-title-row">
|
||||||
<h2 class="discussion-title">讨论</h2>
|
<h2 class="discussion-title">{{ discussionTitle }}</h2>
|
||||||
<span class="participation-status">未参与</span>
|
<span class="participation-status">未参与</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 讨论描述 -->
|
<!-- 讨论描述 -->
|
||||||
<div class="discussion-description">
|
<div class="discussion-description">
|
||||||
如何理解学风与科研诚信?如何理解优良学风与科研诚信之间的关系?各位同学大家好,欢迎加入本学期的课程学习,在学习过程有任何问题困惑和不同见解,都欢迎同学在讨论区积极交流,踊跃回复老师留在讨论区的问题。
|
{{ discussionDescription }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 评论统计 -->
|
<!-- 评论统计 -->
|
||||||
@ -1347,6 +1347,7 @@ import { useMessage } from 'naive-ui'
|
|||||||
import { CourseApi } from '@/api/modules/course'
|
import { CourseApi } from '@/api/modules/course'
|
||||||
import { CommentApi } from '@/api/modules/comment'
|
import { CommentApi } from '@/api/modules/comment'
|
||||||
import { AIApi } from '@/api/modules/ai'
|
import { AIApi } from '@/api/modules/ai'
|
||||||
|
import { AuthApi } from '@/api/modules/auth'
|
||||||
import type { Course, CourseSection, CourseComment } from '@/api/types'
|
import type { Course, CourseSection, CourseComment } from '@/api/types'
|
||||||
import QuillEditor from '@/components/common/QuillEditor.vue'
|
import QuillEditor from '@/components/common/QuillEditor.vue'
|
||||||
import DPlayerVideo from '@/components/course/DPlayerVideo.vue'
|
import DPlayerVideo from '@/components/course/DPlayerVideo.vue'
|
||||||
@ -1580,6 +1581,8 @@ const practiceFinished = ref(false)
|
|||||||
const discussionMode = ref(false)
|
const discussionMode = ref(false)
|
||||||
const currentDiscussionSection = ref<CourseSection | null>(null)
|
const currentDiscussionSection = ref<CourseSection | null>(null)
|
||||||
const discussionList = ref<any[]>([])
|
const discussionList = ref<any[]>([])
|
||||||
|
const discussionTitle = ref('讨论')
|
||||||
|
const discussionDescription = ref('')
|
||||||
const newComment = ref('')
|
const newComment = ref('')
|
||||||
const replyingTo = ref<any>(null)
|
const replyingTo = ref<any>(null)
|
||||||
|
|
||||||
@ -2124,71 +2127,13 @@ const loadCourseSections = async () => {
|
|||||||
console.log('✅ API返回的原始章节数据:', response.data.list)
|
console.log('✅ API返回的原始章节数据:', response.data.list)
|
||||||
console.log('✅ 章节数据数量:', response.data.list.length)
|
console.log('✅ 章节数据数量:', response.data.list.length)
|
||||||
|
|
||||||
// 添加模拟练习章节
|
// 直接使用API返回的章节数据
|
||||||
const sectionsWithPractice = [...response.data.list]
|
courseSections.value = response.data.list
|
||||||
|
groupedSections.value = groupSectionsByChapter(response.data.list)
|
||||||
// 添加一个练习章节到第一章
|
|
||||||
const practiceSection: CourseSection = {
|
|
||||||
id: '999999', // 使用一个特殊的ID
|
|
||||||
lessonId: '999999',
|
|
||||||
name: 'JavaScript基础练习',
|
|
||||||
type: 5, // 练习类型
|
|
||||||
level: 2, // 二级章节
|
|
||||||
parentId: sectionsWithPractice.find(s => s.level === 1)?.id || '1', // 找到第一个父章节
|
|
||||||
duration: '30分钟',
|
|
||||||
completed: false,
|
|
||||||
outline: '',
|
|
||||||
sort: 999,
|
|
||||||
revision: 1,
|
|
||||||
createdAt: Date.now(),
|
|
||||||
updatedAt: Date.now(),
|
|
||||||
deletedAt: null
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将练习章节插入到合适的位置(第一章的最后)
|
|
||||||
const firstChapterSections = sectionsWithPractice.filter(s => s.level === 2 && s.parentId === practiceSection.parentId)
|
|
||||||
if (firstChapterSections.length > 0) {
|
|
||||||
// 插入到第一章的最后一个章节后面
|
|
||||||
const insertIndex = sectionsWithPractice.findIndex(s => s.id === firstChapterSections[firstChapterSections.length - 1].id) + 1
|
|
||||||
sectionsWithPractice.splice(insertIndex, 0, practiceSection)
|
|
||||||
} else {
|
|
||||||
// 如果没有找到合适位置,就添加到最后
|
|
||||||
sectionsWithPractice.push(practiceSection)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加一个讨论章节
|
|
||||||
const discussionSection: CourseSection = {
|
|
||||||
id: '999998', // 使用另一个特殊的ID
|
|
||||||
lessonId: '999998',
|
|
||||||
name: '机器学习与科研流程讨论',
|
|
||||||
type: 6, // 讨论类型(自定义)
|
|
||||||
level: 2, // 二级章节
|
|
||||||
parentId: sectionsWithPractice.find(s => s.level === 1)?.id || '1', // 找到第一个父章节
|
|
||||||
duration: '讨论',
|
|
||||||
completed: false,
|
|
||||||
outline: '',
|
|
||||||
sort: 998,
|
|
||||||
revision: 1,
|
|
||||||
createdAt: Date.now(),
|
|
||||||
updatedAt: Date.now(),
|
|
||||||
deletedAt: null
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将讨论章节插入到练习章节后面
|
|
||||||
const practiceIndex = sectionsWithPractice.findIndex(s => s.id === practiceSection.id)
|
|
||||||
if (practiceIndex !== -1) {
|
|
||||||
sectionsWithPractice.splice(practiceIndex + 1, 0, discussionSection)
|
|
||||||
} else {
|
|
||||||
sectionsWithPractice.push(discussionSection)
|
|
||||||
}
|
|
||||||
|
|
||||||
courseSections.value = sectionsWithPractice
|
|
||||||
groupedSections.value = groupSectionsByChapter(sectionsWithPractice)
|
|
||||||
|
|
||||||
console.log('✅ 设置后的courseSections:', courseSections.value)
|
console.log('✅ 设置后的courseSections:', courseSections.value)
|
||||||
console.log('✅ 设置后的groupedSections:', groupedSections.value)
|
console.log('✅ 设置后的groupedSections:', groupedSections.value)
|
||||||
console.log('✅ groupedSections长度:', groupedSections.value.length)
|
console.log('✅ groupedSections长度:', groupedSections.value.length)
|
||||||
console.log('✅ 已添加练习章节:', practiceSection)
|
|
||||||
} else {
|
} else {
|
||||||
console.log('❌ API返回的章节数据为空或格式错误')
|
console.log('❌ API返回的章节数据为空或格式错误')
|
||||||
console.log('❌ response.data:', response.data)
|
console.log('❌ response.data:', response.data)
|
||||||
@ -2499,7 +2444,20 @@ const handlePractice = async (section: CourseSection) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 调用章节练习API
|
// 第一步:获取用户信息
|
||||||
|
console.log('👤 获取用户信息...')
|
||||||
|
const userInfoResponse = await AuthApi.getUserInfo()
|
||||||
|
|
||||||
|
if (!userInfoResponse.success || !userInfoResponse.result?.baseInfo?.id) {
|
||||||
|
console.error('❌ 获取用户信息失败:', userInfoResponse)
|
||||||
|
message.error('获取用户信息失败,请重新登录')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const studentId = userInfoResponse.result.baseInfo.id
|
||||||
|
console.log('✅ 获取用户信息成功,学生ID:', studentId)
|
||||||
|
|
||||||
|
// 第二步:调用章节练习API获取考试信息
|
||||||
const response = await CourseApi.getSectionExercise(courseId.value, section.id.toString())
|
const response = await CourseApi.getSectionExercise(courseId.value, section.id.toString())
|
||||||
|
|
||||||
if (response.data && (response.data.code === 200 || response.data.code === 0)) {
|
if (response.data && (response.data.code === 200 || response.data.code === 0)) {
|
||||||
@ -2507,21 +2465,120 @@ const handlePractice = async (section: CourseSection) => {
|
|||||||
|
|
||||||
// 处理练习数据
|
// 处理练习数据
|
||||||
const exerciseData = response.data.result
|
const exerciseData = response.data.result
|
||||||
if (exerciseData && Array.isArray(exerciseData)) {
|
if (exerciseData && Array.isArray(exerciseData) && exerciseData.length > 0) {
|
||||||
// 设置练习数据
|
// 获取第一个考试的ID
|
||||||
practiceQuestions.value = exerciseData
|
const firstExam = exerciseData[0]
|
||||||
currentPracticeSection.value = section
|
const examId = firstExam.id
|
||||||
practiceMode.value = true
|
|
||||||
practiceStarted.value = true // 直接开始练习,不需要点击开始按钮
|
|
||||||
practiceFinished.value = false
|
|
||||||
currentQuestionIndex.value = 0
|
|
||||||
|
|
||||||
// 初始化答案数组
|
console.log('🔍 获取到考试信息:', firstExam)
|
||||||
practiceAnswers.value = new Array(exerciseData.length).fill(null).map(() => [])
|
console.log('📋 开始获取考试题目,考试ID:', examId, '学生ID:', studentId)
|
||||||
fillAnswers.value = new Array(exerciseData.length).fill(null).map(() => [])
|
|
||||||
essayAnswers.value = new Array(exerciseData.length).fill('')
|
|
||||||
|
|
||||||
console.log('✅ 练习模式已启动,题目数量:', practiceQuestions.value.length)
|
// 第三步:根据考试ID和学生ID获取题目列表
|
||||||
|
const questionsResponse = await CourseApi.getExamQuestions(examId, studentId)
|
||||||
|
|
||||||
|
if (questionsResponse.data && (questionsResponse.data.code === 200 || questionsResponse.data.code === 0)) {
|
||||||
|
console.log('✅ 获取考试题目成功:', questionsResponse.data)
|
||||||
|
|
||||||
|
const questionsList = questionsResponse.data.result
|
||||||
|
if (questionsList && Array.isArray(questionsList) && questionsList.length > 0) {
|
||||||
|
console.log('📝 题目列表:', questionsList)
|
||||||
|
|
||||||
|
// 第四步:根据每个题目ID获取详细信息
|
||||||
|
const detailedQuestions = []
|
||||||
|
|
||||||
|
for (const questionItem of questionsList) {
|
||||||
|
try {
|
||||||
|
console.log('🔍 获取题目详情,题目ID:', questionItem.id)
|
||||||
|
const detailResponse = await CourseApi.getQuestionDetail(questionItem.id)
|
||||||
|
|
||||||
|
if (detailResponse.data && (detailResponse.data.code === 200 || detailResponse.data.code === 0)) {
|
||||||
|
const questionDetail = detailResponse.data.result
|
||||||
|
console.log('✅ 获取题目详情成功:', questionDetail)
|
||||||
|
detailedQuestions.push(questionDetail)
|
||||||
|
} else {
|
||||||
|
console.warn('⚠️ 获取题目详情失败:', questionItem.id, detailResponse.data?.message)
|
||||||
|
}
|
||||||
|
} catch (detailError) {
|
||||||
|
console.error('❌ 获取题目详情异常:', questionItem.id, detailError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (detailedQuestions.length > 0) {
|
||||||
|
// 处理题目数据格式,确保符合前端显示要求
|
||||||
|
const processedQuestions = detailedQuestions.map((questionData, index) => {
|
||||||
|
console.log(`🔍 处理题目 ${index + 1}:`, questionData)
|
||||||
|
|
||||||
|
// 解析API返回的数据结构
|
||||||
|
const question = questionData.question
|
||||||
|
const answers = questionData.answer || []
|
||||||
|
|
||||||
|
// 题目类型映射:0=单选题,1=多选题,2=判断题,3=填空题,4=简答题
|
||||||
|
const typeMap: { [key: number]: string } = {
|
||||||
|
0: '单选题',
|
||||||
|
1: '多选题',
|
||||||
|
2: '判断题',
|
||||||
|
3: '填空题',
|
||||||
|
4: '简答题'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取选项内容
|
||||||
|
const options = answers
|
||||||
|
.sort((a: any, b: any) => a.orderNo - b.orderNo) // 按orderNo排序
|
||||||
|
.map((answer: any) => answer.content)
|
||||||
|
|
||||||
|
// 找出正确答案的索引
|
||||||
|
const correctAnswers = answers
|
||||||
|
.filter((answer: any) => answer.izCorrent === 1)
|
||||||
|
.map((answer: any) => answer.orderNo - 1) // orderNo从1开始,转换为从0开始的索引
|
||||||
|
|
||||||
|
console.log(`📝 题目选项:`, options)
|
||||||
|
console.log(`✅ 正确答案索引:`, correctAnswers)
|
||||||
|
|
||||||
|
// 根据API返回的数据结构适配前端需要的格式
|
||||||
|
const processedQuestion = {
|
||||||
|
id: question.id,
|
||||||
|
title: question.content || `题目 ${index + 1}`,
|
||||||
|
type: typeMap[question.type] || '单选题',
|
||||||
|
score: question.score || 5,
|
||||||
|
options: options,
|
||||||
|
correctAnswer: correctAnswers.length === 1 ? correctAnswers[0] : correctAnswers,
|
||||||
|
analysis: question.analysis || '',
|
||||||
|
difficulty: question.difficulty || 0,
|
||||||
|
// 保留原始数据以备后用
|
||||||
|
originalData: questionData
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`✅ 处理后的题目 ${index + 1}:`, processedQuestion)
|
||||||
|
return processedQuestion
|
||||||
|
})
|
||||||
|
|
||||||
|
// 设置练习数据
|
||||||
|
practiceQuestions.value = processedQuestions
|
||||||
|
currentPracticeSection.value = section
|
||||||
|
practiceMode.value = true
|
||||||
|
practiceStarted.value = true // 直接开始练习,不需要点击开始按钮
|
||||||
|
practiceFinished.value = false
|
||||||
|
currentQuestionIndex.value = 0
|
||||||
|
|
||||||
|
// 初始化答案数组
|
||||||
|
practiceAnswers.value = new Array(processedQuestions.length).fill(null).map(() => [])
|
||||||
|
fillAnswers.value = new Array(processedQuestions.length).fill(null).map(() => [])
|
||||||
|
essayAnswers.value = new Array(processedQuestions.length).fill('')
|
||||||
|
|
||||||
|
console.log('✅ 练习模式已启动,题目数量:', practiceQuestions.value.length)
|
||||||
|
console.log('✅ 处理后的题目列表:', practiceQuestions.value)
|
||||||
|
} else {
|
||||||
|
console.warn('⚠️ 没有获取到有效的题目详情')
|
||||||
|
message.warning('没有获取到有效的题目详情')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn('⚠️ 考试题目列表为空:', questionsList)
|
||||||
|
message.warning('考试题目列表为空')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('❌ 获取考试题目失败:', questionsResponse.data?.message || questionsResponse.message)
|
||||||
|
message.error(questionsResponse.data?.message || questionsResponse.message || '获取考试题目失败')
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.warn('⚠️ 练习数据格式异常:', exerciseData)
|
console.warn('⚠️ 练习数据格式异常:', exerciseData)
|
||||||
message.warning('练习数据格式异常')
|
message.warning('练习数据格式异常')
|
||||||
@ -2791,17 +2848,40 @@ const loadDiscussionData = async (section: CourseSection) => {
|
|||||||
|
|
||||||
// 处理讨论数据
|
// 处理讨论数据
|
||||||
const discussionData = response.data.result
|
const discussionData = response.data.result
|
||||||
if (discussionData && Array.isArray(discussionData)) {
|
if (discussionData && Array.isArray(discussionData) && discussionData.length > 0) {
|
||||||
// 为每个评论添加点赞状态字段
|
// 获取第一个讨论项的标题和描述
|
||||||
discussionList.value = discussionData.map(comment => ({
|
const firstDiscussion = discussionData[0]
|
||||||
...comment,
|
console.log('🔍 讨论数据详情:', firstDiscussion)
|
||||||
isLiked: false, // 默认未点赞
|
|
||||||
likes: comment.likes || 0 // 确保有点赞数字段
|
if (firstDiscussion.title) {
|
||||||
}))
|
discussionTitle.value = firstDiscussion.title
|
||||||
console.log('✅ 讨论数据加载完成,讨论数量:', discussionList.value.length)
|
console.log('✅ 设置讨论标题:', firstDiscussion.title)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstDiscussion.description) {
|
||||||
|
// 去掉HTML标签,但保留文本内容
|
||||||
|
const originalDescription = firstDiscussion.description
|
||||||
|
const strippedDescription = stripHtmlTags(originalDescription)
|
||||||
|
discussionDescription.value = strippedDescription
|
||||||
|
console.log('✅ 原始描述:', originalDescription)
|
||||||
|
console.log('✅ 处理后描述:', strippedDescription)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 这个API返回的是讨论主题信息,不是评论列表
|
||||||
|
// 评论列表可能需要另外的API调用
|
||||||
|
// 暂时初始化为空的评论列表
|
||||||
|
discussionList.value = []
|
||||||
|
console.log('✅ 讨论主题数据加载完成')
|
||||||
|
|
||||||
|
// TODO: 这里可能需要调用另一个API来获取评论列表
|
||||||
|
// 例如: loadDiscussionComments(firstDiscussion.id)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
console.warn('⚠️ 讨论数据格式异常:', discussionData)
|
console.warn('⚠️ 讨论数据格式异常:', discussionData)
|
||||||
discussionList.value = []
|
discussionList.value = []
|
||||||
|
// 重置标题和描述
|
||||||
|
discussionTitle.value = '讨论'
|
||||||
|
discussionDescription.value = ''
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.error('❌ 获取章节讨论失败:', response.data?.message || response.message)
|
console.error('❌ 获取章节讨论失败:', response.data?.message || response.message)
|
||||||
@ -2815,11 +2895,25 @@ const loadDiscussionData = async (section: CourseSection) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 工具函数:去掉HTML标签,保留文本内容
|
||||||
|
const stripHtmlTags = (html: string): string => {
|
||||||
|
if (!html) return ''
|
||||||
|
|
||||||
|
// 创建一个临时的DOM元素来解析HTML
|
||||||
|
const tempDiv = document.createElement('div')
|
||||||
|
tempDiv.innerHTML = html
|
||||||
|
|
||||||
|
// 获取纯文本内容
|
||||||
|
return tempDiv.textContent || tempDiv.innerText || ''
|
||||||
|
}
|
||||||
|
|
||||||
const exitDiscussion = () => {
|
const exitDiscussion = () => {
|
||||||
console.log('🚪 正在退出讨论模式...')
|
console.log('🚪 正在退出讨论模式...')
|
||||||
discussionMode.value = false
|
discussionMode.value = false
|
||||||
currentDiscussionSection.value = null
|
currentDiscussionSection.value = null
|
||||||
discussionList.value = []
|
discussionList.value = []
|
||||||
|
discussionTitle.value = '讨论'
|
||||||
|
discussionDescription.value = ''
|
||||||
newComment.value = ''
|
newComment.value = ''
|
||||||
replyingTo.value = null
|
replyingTo.value = null
|
||||||
console.log('✅ 已退出讨论模式,discussionMode:', discussionMode.value)
|
console.log('✅ 已退出讨论模式,discussionMode:', discussionMode.value)
|
||||||
|
@ -133,9 +133,9 @@ const examName = ref(route.query.examName as string || '考试')
|
|||||||
// const viewCount = ref(1024) // 暂时注释,后续需要时再启用
|
// const viewCount = ref(1024) // 暂时注释,后续需要时再启用
|
||||||
|
|
||||||
// 返回上级
|
// 返回上级
|
||||||
const goBack = () => {
|
// const goBack = () => {
|
||||||
router.push(`/course/${courseId.value}`)
|
// router.push(`/course/${courseId.value}`)
|
||||||
}
|
// } // 暂时未使用
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user