From c35f54fdd9482bfa2a87541fe80ef2639e35029e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E5=BC=A0?= <2091066548@qq.com> Date: Sat, 13 Sep 2025 15:52:55 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/modules/exam.ts | 67 +++- src/components/course/DPlayerVideo.vue | 229 +++++------- src/views/teacher/ExamPages/AddQuestion.vue | 353 ++++++++++++++++-- .../ExamPages/QuestionBankManagement.vue | 282 +++++++++++++- 4 files changed, 749 insertions(+), 182 deletions(-) diff --git a/src/api/modules/exam.ts b/src/api/modules/exam.ts index 6732202..9f7a132 100644 --- a/src/api/modules/exam.ts +++ b/src/api/modules/exam.ts @@ -100,6 +100,65 @@ export class ExamApi { return response } + /** + * 导出题库 + */ + static async exportRepo(repoId: string): Promise { + console.log('🚀 导出题库:', { repoId }) + + try { + const response = await fetch(`${import.meta.env.VITE_API_BASE_URL}/aiol/aiolRepo/exportXls?repoId=${repoId}`, { + method: 'GET', + headers: { + 'X-Access-Token': localStorage.getItem('token') || '', + 'Content-Type': 'application/json' + } + }) + + if (!response.ok) { + throw new Error(`导出失败: ${response.status} ${response.statusText}`) + } + + const blob = await response.blob() + console.log('✅ 题库导出成功:', blob) + return blob + } catch (error) { + console.error('❌ 导出题库失败:', error) + throw error + } + } + + /** + * 导入题库 + */ + static async importRepo(repoId: string, file: File): Promise> { + console.log('🚀 导入题库:', { repoId, fileName: file.name }) + + try { + const formData = new FormData() + formData.append('file', file) + + const response = await fetch(`${import.meta.env.VITE_API_BASE_URL}/aiol/aiolRepo/importXls?repoId=${repoId}`, { + method: 'POST', + headers: { + 'X-Access-Token': localStorage.getItem('token') || '' + }, + body: formData + }) + + if (!response.ok) { + throw new Error(`导入失败: ${response.status} ${response.statusText}`) + } + + const result = await response.json() + console.log('✅ 题库导入成功:', result) + return result + } catch (error) { + console.error('❌ 导入题库失败:', error) + throw error + } + } + // ========== 题目管理 ========== /** @@ -184,7 +243,7 @@ export class ExamApi { */ static async updateQuestionOption(data: UpdateQuestionOptionRequest): Promise> { console.log('🚀 编辑题目选项:', data) - const response = await ApiRequest.put('/gen/questionoption/questionOption/edit', data) + const response = await ApiRequest.put('/aiol/aiolQuestionOption/edit', data) console.log('✅ 编辑题目选项成功:', response) return response } @@ -204,11 +263,11 @@ export class ExamApi { // ========== 题目答案管理 ========== /** - * 添加题目答案 + * 添加题目答案 - 使用新的API接口 */ static async createQuestionAnswer(data: CreateQuestionAnswerRequest): Promise> { console.log('🚀 添加题目答案:', data) - const response = await ApiRequest.post('/gen/questionanswer/questionAnswer/add', data) + const response = await ApiRequest.post('/aiol/aiolQuestionAnswer/add', data) console.log('✅ 添加题目答案成功:', response) return response } @@ -218,7 +277,7 @@ export class ExamApi { */ static async updateQuestionAnswer(data: UpdateQuestionAnswerRequest): Promise> { console.log('🚀 编辑题目答案:', data) - const response = await ApiRequest.put('/gen/questionanswer/questionAnswer/edit', data) + const response = await ApiRequest.put('/aiol/aiolQuestionAnswer/edit', data) console.log('✅ 编辑题目答案成功:', response) return response } diff --git a/src/components/course/DPlayerVideo.vue b/src/components/course/DPlayerVideo.vue index bf97d14..9ebfddb 100644 --- a/src/components/course/DPlayerVideo.vue +++ b/src/components/course/DPlayerVideo.vue @@ -16,54 +16,32 @@ - -
- -
- -
- -
-
- {{ quality.label }} -
+
+ + +
+
+ 清晰度: +
+ +
+
+ {{ quality.label }}
- - - - - - - - -
- -
+ +
@@ -102,7 +80,6 @@ const emit = defineEmits<{ error: [error: any] qualityChange: [quality: string] screenshot: [dataUrl: string] - danmakuSend: [text: string] }>() const dplayerContainer = ref() @@ -110,9 +87,7 @@ let player: any = null const playerInitialized = ref(false) const isPlaying = ref(false) -// 新增功能状态 -const danmakuEnabled = ref(true) -const danmakuText = ref('') +// 功能状态 const isPictureInPicture = ref(false) const showQualityMenu = ref(false) @@ -224,17 +199,6 @@ const initializePlayer = async (videoUrl?: string) => { playbackSpeed: [0.5, 0.75, 1, 1.25, 1.5, 2], loop: false, screenshot: true, // 启用截屏功能 - danmaku: { - id: 'course-video-' + Date.now(), - api: '/api/danmaku/', // 弹幕API地址 - token: 'demo-token', - maximum: 1000, - // 移除有问题的addition API - // addition: ['https://api.prprpr.me/dplayer/'], - user: 'student', - bottom: '15%', - unlimited: true - }, contextmenu: [ { text: '截屏', @@ -420,32 +384,7 @@ const togglePictureInPicture = async () => { } } -// 弹幕功能 -const toggleDanmaku = () => { - danmakuEnabled.value = !danmakuEnabled.value - if (player && player.danmaku) { - if (danmakuEnabled.value) { - player.danmaku.show() - } else { - player.danmaku.hide() - } - } -} -const sendDanmaku = () => { - if (danmakuText.value.trim() && player && player.danmaku) { - const danmaku = { - text: danmakuText.value.trim(), - color: '#ffffff', - type: 'right' - } - - player.danmaku.send(danmaku) - emit('danmakuSend', danmakuText.value.trim()) - danmakuText.value = '' - console.log('发送弹幕:', danmaku.text) - } -} // 清晰度切换相关函数 const getCurrentQualityLabel = () => { @@ -540,8 +479,6 @@ defineExpose({ initializePlayer, takeScreenshot, togglePictureInPicture, - toggleDanmaku, - sendDanmaku, switchQuality }) @@ -742,64 +679,98 @@ onUnmounted(() => { color: #fff; } -/* 弹幕输入框 */ -.danmaku-input-container { - position: absolute; - bottom: 60px; - left: 16px; - right: 16px; - z-index: 15; -} - -.danmaku-input-wrapper { - display: flex; - gap: 8px; - background: rgba(0, 0, 0, 0.8); - padding: 8px; +/* 播放器下方的清晰度选择器 */ +.video-controls-bottom { + margin-top: 12px; + padding: 12px 16px; + background: #f8f9fa; border-radius: 8px; - backdrop-filter: blur(4px); + border: 1px solid #e9ecef; } -.danmaku-input { - flex: 1; - padding: 8px 12px; - background: rgba(255, 255, 255, 0.1); - color: white; - border: 1px solid rgba(255, 255, 255, 0.2); - border-radius: 4px; +.quality-selector-bottom { + display: flex; + align-items: center; + gap: 12px; +} + +.quality-label { font-size: 14px; - outline: none; - transition: all 0.2s; + color: #495057; + font-weight: 500; } -.danmaku-input::placeholder { - color: rgba(255, 255, 255, 0.6); +.quality-dropdown-bottom { + position: relative; } -.danmaku-input:focus { - background: rgba(255, 255, 255, 0.15); - border-color: #007bff; -} - -.danmaku-send-btn { - padding: 8px 16px; - background: #007bff; - color: white; - border: none; - border-radius: 4px; +.quality-btn-bottom { + display: flex; + align-items: center; + gap: 6px; + padding: 8px 12px; + min-width: 80px; + background: #fff; + color: #495057; + border: 1px solid #ced4da; + border-radius: 6px; font-size: 14px; cursor: pointer; - transition: background-color 0.2s; - white-space: nowrap; + transition: all 0.2s ease; + justify-content: space-between; } -.danmaku-send-btn:hover:not(:disabled) { - background: #0056b3; +.quality-btn-bottom:hover { + border-color: #007bff; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } -.danmaku-send-btn:disabled { - background: rgba(255, 255, 255, 0.2); - cursor: not-allowed; +.quality-dropdown-icon { + transition: transform 0.2s ease; +} + +.quality-dropdown-icon.rotated { + transform: rotate(180deg); +} + +.quality-menu-bottom { + position: absolute; + top: 100%; + left: 0; + right: 0; + margin-top: 4px; + background: #fff; + border: 1px solid #ced4da; + border-radius: 6px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + z-index: 1000; + overflow: hidden; +} + +.quality-option-bottom { + padding: 10px 12px; + font-size: 14px; + color: #495057; + cursor: pointer; + transition: background-color 0.2s ease; + border-bottom: 1px solid #f8f9fa; +} + +.quality-option-bottom:last-child { + border-bottom: none; +} + +.quality-option-bottom:hover { + background-color: #f8f9fa; +} + +.quality-option-bottom.active { + background-color: #007bff; + color: #fff; +} + +.quality-option-bottom.active:hover { + background-color: #0056b3; } /* 响应式设计 */ diff --git a/src/views/teacher/ExamPages/AddQuestion.vue b/src/views/teacher/ExamPages/AddQuestion.vue index 5de21e8..5d06e2a 100644 --- a/src/views/teacher/ExamPages/AddQuestion.vue +++ b/src/views/teacher/ExamPages/AddQuestion.vue @@ -328,6 +328,10 @@ const questionForm = reactive({ explanation: '' // 解析 }); +// 存储现有的选项和答案ID(用于编辑模式) +const existingOptionsIds = ref([]); +const existingAnswersIds = ref([]); + // 表单验证规则 const formRules = { type: { @@ -471,9 +475,8 @@ const saveQuestion = async () => { // 根据编辑模式调用不同接口 if (isEditMode.value && questionId) { - // TODO: 实现编辑模式的接口调用 - message.info('编辑模式暂未实现'); - return; + // 编辑模式 - 更新现有题目 + await updateExistingQuestion(questionId); } else { // 新增模式 - 按照接口调用顺序执行 await createNewQuestion(bankId); @@ -550,10 +553,14 @@ const createNewQuestion = async (bankId: string) => { console.log('✅ 题目创建成功,题目ID:', questionId); - // 如果是选择题或判断题,需要创建选项 + // 根据题目类型创建选项或答案 const questionType = getQuestionTypeNumber(questionForm.type); - if (questionType === 0 || questionType === 1 || questionType === 2) { // 单选、多选、判断题 + if (questionType === 0 || questionType === 1 || questionType === 2) { + // 单选、多选、判断题:创建选项 await createQuestionOptions(questionId, questionType); + } else if (questionType === 3 || questionType === 4) { + // 填空题、简答题:创建答案 + await createQuestionAnswers(questionId, questionType); } message.success('题目创建成功!'); @@ -582,9 +589,15 @@ const createQuestionOptions = async (questionId: string, questionType: number) = const options = questionForm.options || []; const correctAnswer = questionForm.correctAnswer; + console.log('🔍 单选题选项创建逻辑:', { + options, + correctAnswer, + correctAnswerType: typeof correctAnswer + }); + optionsToCreate = options.map((option: any, index: number) => ({ content: option.content || option.text || '', - izCorrent: option.letter === correctAnswer ? 1 : 0, + izCorrent: index === correctAnswer ? 1 : 0, orderNo: index })); @@ -592,16 +605,29 @@ const createQuestionOptions = async (questionId: string, questionType: number) = const options = questionForm.options || []; const correctAnswers = questionForm.correctAnswers || []; + console.log('🔍 多选题选项创建逻辑:', { + options, + correctAnswers, + correctAnswersType: typeof correctAnswers + }); + optionsToCreate = options.map((option: any, index: number) => ({ content: option.content || option.text || '', - izCorrent: correctAnswers.includes(option.letter) ? 1 : 0, + izCorrent: correctAnswers.includes(index) ? 1 : 0, orderNo: index })); } else if (questionType === 2) { // 判断题 // 判断题固定两个选项:正确和错误 - const correctAnswer = questionForm.correctAnswer; - const isCorrectTrue = String(correctAnswer) === 'true' || correctAnswer === 1; + const trueFalseAnswer = questionForm.trueFalseAnswer; + const isCorrectTrue = trueFalseAnswer === true; + + console.log('🔍 判断题选项创建逻辑:', { + trueFalseAnswer, + isCorrectTrue, + trueFalseAnswerType: typeof trueFalseAnswer + }); + optionsToCreate = [ { content: '正确', @@ -640,6 +666,231 @@ const createQuestionOptions = async (questionId: string, questionType: number) = } }; +// 创建题目答案(填空题和简答题) +const createQuestionAnswers = async (questionId: string, questionType: number) => { + try { + console.log('🚀 开始创建题目答案,题目ID:', questionId, '题目类型:', questionType); + + let answersToCreate: Array<{ + answerText: string; + orderNo: number; + }> = []; + + if (questionType === 3) { // 填空题 + const fillBlankAnswers = questionForm.fillBlankAnswers || []; + answersToCreate = fillBlankAnswers.map((answer: any, index: number) => ({ + answerText: answer.content || '', + orderNo: index + })); + + } else if (questionType === 4) { // 简答题 + const shortAnswer = questionForm.shortAnswer || ''; + if (shortAnswer.trim()) { + answersToCreate = [{ + answerText: shortAnswer, + orderNo: 0 + }]; + } + } + + console.log('📊 准备创建的答案:', answersToCreate); + + // 批量创建答案 + for (const answer of answersToCreate) { + const answerData = { + questionId: questionId, + answerText: answer.answerText, + orderNo: answer.orderNo + }; + + console.log('🚀 创建答案:', answerData); + const answerResponse = await ExamApi.createQuestionAnswer(answerData); + console.log('✅ 答案创建成功:', answerResponse); + } + + console.log('✅ 所有答案创建完成'); + + } catch (error: any) { + console.error('❌ 创建题目答案失败:', error); + throw new Error('创建题目答案失败:' + (error.message || '未知错误')); + } +}; + +// 更新现有题目的完整流程 +const updateExistingQuestion = async (questionId: string) => { + try { + console.log('🚀 开始更新题目,题目ID:', questionId); + + // 1. 更新题目基本信息 + const questionData = { + id: questionId, + parentId: undefined, + type: getQuestionTypeNumber(questionForm.type), + content: questionForm.title, + analysis: questionForm.explanation || '', + difficulty: getDifficultyNumber(questionForm.difficulty), + score: questionForm.score + }; + + console.log('🚀 更新题目基本信息:', questionData); + const questionResponse = await ExamApi.updateQuestion(questionData); + console.log('✅ 题目基本信息更新成功:', questionResponse); + + // 2. 根据题目类型更新选项或答案 + const questionType = getQuestionTypeNumber(questionForm.type); + if (questionType === 0 || questionType === 1 || questionType === 2) { + // 单选、多选、判断题:更新选项 + await updateQuestionOptions(questionId, questionType); + } else if (questionType === 3 || questionType === 4) { + // 填空题、简答题:更新答案 + await updateQuestionAnswers(questionId, questionType); + } + + message.success('题目更新成功!'); + router.back(); + + } catch (error: any) { + console.error('❌ 更新题目失败:', error); + message.error(error.message || '更新题目失败'); + throw error; + } +}; + +// 更新题目答案(填空题和简答题) +const updateQuestionAnswers = async (questionId: string, questionType: number) => { + try { + console.log('🚀 开始更新题目答案,题目ID:', questionId, '题目类型:', questionType); + + let answersToUpdate: Array<{ + id: string; + answerText: string; + orderNo: number; + }> = []; + + if (questionType === 3) { // 填空题 + const fillBlankAnswers = questionForm.fillBlankAnswers || []; + answersToUpdate = fillBlankAnswers.map((answer: any, index: number) => ({ + id: existingAnswersIds.value[index] || '', + answerText: answer.content || '', + orderNo: index + })).filter(answer => answer.id); // 只更新有ID的答案 + + } else if (questionType === 4) { // 简答题 + const shortAnswer = questionForm.shortAnswer || ''; + if (shortAnswer.trim() && existingAnswersIds.value.length > 0) { + answersToUpdate = [{ + id: existingAnswersIds.value[0], + answerText: shortAnswer, + orderNo: 0 + }]; + } + } + + console.log('📊 准备更新的答案:', answersToUpdate); + + // 批量更新答案 + for (const answer of answersToUpdate) { + const answerData = { + id: answer.id, + questionId: questionId, + answerText: answer.answerText, + orderNo: answer.orderNo + }; + + console.log('🚀 更新答案:', answerData); + const answerResponse = await ExamApi.updateQuestionAnswer(answerData); + console.log('✅ 答案更新成功:', answerResponse); + } + + console.log('✅ 所有答案更新完成'); + + } catch (error: any) { + console.error('❌ 更新题目答案失败:', error); + throw new Error('更新题目答案失败:' + (error.message || '未知错误')); + } +}; + +// 更新题目选项(单选、多选、判断题) +const updateQuestionOptions = async (questionId: string, questionType: number) => { + try { + console.log('🚀 开始更新题目选项,题目ID:', questionId, '题目类型:', questionType); + + let optionsToUpdate: Array<{ + id: string; + content: string; + izCorrent: number; + orderNo: number; + }> = []; + + if (questionType === 0) { // 单选题 + const options = questionForm.options || []; + const correctAnswer = questionForm.correctAnswer; + + optionsToUpdate = options.map((option: any, index: number) => ({ + id: existingOptionsIds.value[index] || '', + content: option.content || '', + izCorrent: index === correctAnswer ? 1 : 0, + orderNo: index + })).filter(option => option.id); // 只更新有ID的选项 + + } else if (questionType === 1) { // 多选题 + const options = questionForm.options || []; + const correctAnswers = questionForm.correctAnswers || []; + + optionsToUpdate = options.map((option: any, index: number) => ({ + id: existingOptionsIds.value[index] || '', + content: option.content || '', + izCorrent: correctAnswers.includes(index) ? 1 : 0, + orderNo: index + })).filter(option => option.id); // 只更新有ID的选项 + + } else if (questionType === 2) { // 判断题 + const correctAnswer = questionForm.trueFalseAnswer; + const isCorrectTrue = correctAnswer === true; + + if (existingOptionsIds.value.length >= 2) { + optionsToUpdate = [ + { + id: existingOptionsIds.value[0], + content: '正确', + izCorrent: isCorrectTrue ? 1 : 0, + orderNo: 0 + }, + { + id: existingOptionsIds.value[1], + content: '错误', + izCorrent: isCorrectTrue ? 0 : 1, + orderNo: 1 + } + ]; + } + } + + console.log('📊 准备更新的选项:', optionsToUpdate); + + // 批量更新选项 + for (const option of optionsToUpdate) { + const optionData = { + id: option.id, + questionId: questionId, + content: option.content, + izCorrent: option.izCorrent, + orderNo: option.orderNo + }; + + console.log('🚀 更新选项:', optionData); + const optionResponse = await ExamApi.updateQuestionOption(optionData); + console.log('✅ 选项更新成功:', optionResponse); + } + + console.log('✅ 所有选项更新完成'); + + } catch (error: any) { + console.error('❌ 更新题目选项失败:', error); + throw new Error('更新题目选项失败:' + (error.message || '未知错误')); + } +}; + // 验证答案设置 @@ -911,69 +1162,81 @@ const renderQuestionData = (questionData: any) => { }; // 渲染单选题数据 -const renderSingleChoiceData = (answers: any[]) => { - console.log('🔘 渲染单选题数据:', answers); +const renderSingleChoiceData = (options: any[]) => { + console.log('🔘 渲染单选题数据:', options); - if (answers && answers.length > 0) { + if (options && options.length > 0) { // 按orderNo排序 - const sortedAnswers = answers.sort((a, b) => a.orderNo - b.orderNo); + const sortedOptions = options.sort((a, b) => a.orderNo - b.orderNo); + + // 保存现有选项ID + existingOptionsIds.value = sortedOptions.map(option => option.id); // 设置选项 - questionForm.options = sortedAnswers.map(answer => ({ - content: answer.content || '' + questionForm.options = sortedOptions.map(option => ({ + content: option.content || '' })); - // 设置正确答案(orderNo从1开始,数组索引从0开始) - const correctAnswer = sortedAnswers.find(answer => answer.izCorrent === 1); - if (correctAnswer) { - questionForm.correctAnswer = correctAnswer.orderNo - 1; + // 设置正确答案 + const correctOption = sortedOptions.find(option => option.izCorrent === 1); + if (correctOption) { + questionForm.correctAnswer = correctOption.orderNo; } console.log('✅ 单选题渲染完成:', { options: questionForm.options, - correctAnswer: questionForm.correctAnswer + correctAnswer: questionForm.correctAnswer, + existingOptionsIds: existingOptionsIds.value }); } }; // 渲染多选题数据 -const renderMultipleChoiceData = (answers: any[]) => { - console.log('☑️ 渲染多选题数据:', answers); +const renderMultipleChoiceData = (options: any[]) => { + console.log('☑️ 渲染多选题数据:', options); - if (answers && answers.length > 0) { + if (options && options.length > 0) { // 按orderNo排序 - const sortedAnswers = answers.sort((a, b) => a.orderNo - b.orderNo); + const sortedOptions = options.sort((a, b) => a.orderNo - b.orderNo); + + // 保存现有选项ID + existingOptionsIds.value = sortedOptions.map(option => option.id); // 设置选项 - questionForm.options = sortedAnswers.map(answer => ({ - content: answer.content || '' + questionForm.options = sortedOptions.map(option => ({ + content: option.content || '' })); // 设置正确答案(可能有多个) - questionForm.correctAnswers = sortedAnswers - .filter(answer => answer.izCorrent === 1) - .map(answer => answer.orderNo - 1); + questionForm.correctAnswers = sortedOptions + .filter(option => option.izCorrent === 1) + .map(option => option.orderNo); console.log('✅ 多选题渲染完成:', { options: questionForm.options, - correctAnswers: questionForm.correctAnswers + correctAnswers: questionForm.correctAnswers, + existingOptionsIds: existingOptionsIds.value }); } }; // 渲染判断题数据 -const renderTrueFalseData = (answers: any[]) => { - console.log('✔️ 渲染判断题数据:', answers); +const renderTrueFalseData = (options: any[]) => { + console.log('✔️ 渲染判断题数据:', options); - if (answers && answers.length > 0) { - const correctAnswer = answers.find(answer => answer.izCorrent === 1); - if (correctAnswer) { + if (options && options.length > 0) { + // 保存现有选项ID + existingOptionsIds.value = options.map(option => option.id); + + const correctOption = options.find(option => option.izCorrent === 1); + if (correctOption) { // 假设判断题的正确答案content为"正确"或"错误" - questionForm.trueFalseAnswer = correctAnswer.content === '正确' || correctAnswer.content === 'true'; + questionForm.trueFalseAnswer = correctOption.content === '正确' || correctOption.content === 'true'; } console.log('✅ 判断题渲染完成:', { - trueFalseAnswer: questionForm.trueFalseAnswer + trueFalseAnswer: questionForm.trueFalseAnswer, + existingOptionsIds: existingOptionsIds.value }); } }; @@ -983,14 +1246,18 @@ const renderFillBlankData = (answers: any[]) => { console.log('📝 渲染填空题数据:', answers); if (answers && answers.length > 0) { + // 保存现有答案ID + existingAnswersIds.value = answers.map(answer => answer.id); + questionForm.fillBlankAnswers = answers.map(answer => ({ - content: answer.content || '', + content: answer.answerText || answer.content || '', score: 1, caseSensitive: false })); console.log('✅ 填空题渲染完成:', { - fillBlankAnswers: questionForm.fillBlankAnswers + fillBlankAnswers: questionForm.fillBlankAnswers, + existingAnswersIds: existingAnswersIds.value }); } }; @@ -1000,10 +1267,14 @@ const renderShortAnswerData = (answers: any[]) => { console.log('📄 渲染简答题数据:', answers); if (answers && answers.length > 0) { - questionForm.shortAnswer = answers[0]?.content || ''; + // 保存现有答案ID + existingAnswersIds.value = answers.map(answer => answer.id); + + questionForm.shortAnswer = answers[0]?.answerText || answers[0]?.content || ''; console.log('✅ 简答题渲染完成:', { - shortAnswer: questionForm.shortAnswer + shortAnswer: questionForm.shortAnswer, + existingAnswersIds: existingAnswersIds.value }); } }; diff --git a/src/views/teacher/ExamPages/QuestionBankManagement.vue b/src/views/teacher/ExamPages/QuestionBankManagement.vue index 7cfd208..83d6482 100644 --- a/src/views/teacher/ExamPages/QuestionBankManagement.vue +++ b/src/views/teacher/ExamPages/QuestionBankManagement.vue @@ -46,18 +46,74 @@ - + +
+
+

将题目导入到题库:{{ getSelectedBankName() }}

+

请选择要导入的Excel文件(支持.xlsx, .xls格式)

+
+ + + +
+ + + +
+ + 点击或者拖动文件到该区域来上传 + + + 请上传Excel格式的题库文件 + +
+
+ +
+ +
+

成功导入:{{ importResult.details.success }} 条

+

失败:{{ importResult.details.failed }} 条

+
+
+
+
+ + +