|
@@ -11,7 +11,7 @@
|
|
|
</view>
|
|
|
|
|
|
<!-- 主要内容区域 - 修改为流式布局 -->
|
|
|
- <scroll-view class="content-scroll" scroll-y="true" :scroll-into-view="currentScrollId" :scroll-with-animation="true">
|
|
|
+ <scroll-view class="content-scroll" scroll-y="true" :scroll-into-view="currentScrollId" :scroll-with-animation="true" :scroll-top="scrollTop">
|
|
|
<view class="content">
|
|
|
<!-- 测试内容区域 - 修改为流式布局,每个问题都显示 -->
|
|
|
<view v-for="(question, qIndex) in questions" :key="question.id" :id="'question-'+qIndex" class="interview-content">
|
|
@@ -44,7 +44,7 @@
|
|
|
'option-selected': isOptionSelected(question, qIndex, index),
|
|
|
'option-disabled': qIndex !== currentQuestionIndex
|
|
|
}"
|
|
|
- @click="qIndex === currentQuestionIndex && selectOption(index)">
|
|
|
+ @click="handleOptionClick(qIndex, index)">
|
|
|
<text class="option-text">{{ option.option_text || (typeof option === 'string' ? option : JSON.stringify(option)) }}</text>
|
|
|
</view>
|
|
|
</view>
|
|
@@ -58,7 +58,13 @@
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
-
|
|
|
+ <view class="continue-button-container" v-if="showContinueButton" id="continue-button">
|
|
|
+ <button class="continue-button" @click="handleContinue">
|
|
|
+ {{ currentGroupIndex < questionGroups.length - 1 ? '继续下一部分' : '完成测试' }}
|
|
|
+ </button>
|
|
|
+ </view>
|
|
|
+ <!-- 添加底部锚点元素 -->
|
|
|
+ <view id="bottom-anchor" style="height: 1px;"></view>
|
|
|
<!-- 添加底部空白区域,确保内容不被固定按钮遮挡 -->
|
|
|
<view class="bottom-space"></view>
|
|
|
</view>
|
|
@@ -144,6 +150,11 @@
|
|
|
questionStartTime: null, // 存储问题开始时间
|
|
|
mode: 'normal', // 相机模式
|
|
|
currentScrollId: 'question-0', // 当前滚动到的问题ID
|
|
|
+ questionGroups: [], // 存储分组后的题目
|
|
|
+ currentGroupIndex: 0, // 当前显示的组索引
|
|
|
+ showContinueButton: false, // 是否显示继续按钮
|
|
|
+ scrollToBottom: false, // 新增:是否需要滚动到底部
|
|
|
+ scrollTop: 0, // 控制 scroll-view 的滚动位置
|
|
|
}
|
|
|
},
|
|
|
computed: {
|
|
@@ -204,9 +215,8 @@
|
|
|
|
|
|
// 如果返回的是问题列表
|
|
|
if (data && Array.isArray(data)) {
|
|
|
- // 处理多个问题,并过滤掉 question_form 为 0 的问题
|
|
|
- this.questions = data
|
|
|
- .filter(q => q.question_form !== 0) // 过滤掉开放问题
|
|
|
+ const formattedQuestions = data
|
|
|
+ .filter(q => q.question_form !== 0)
|
|
|
.map((q, index) => ({
|
|
|
id: q.id || index + 1,
|
|
|
text: q.question || '未知问题',
|
|
@@ -220,8 +230,12 @@
|
|
|
correctAnswers: q.correct_answers || [],
|
|
|
difficulty: q.difficulty || 1,
|
|
|
difficultyName: q.difficulty_name || '初级',
|
|
|
- imageUrl: q.question_image_url || '' // 添加图片URL字段
|
|
|
+ imageUrl: q.question_image_url || '', // 添加图片URL字段
|
|
|
+ category: q.category || 0, // 添加 category 字段
|
|
|
}));
|
|
|
+
|
|
|
+ // 处理题目分组
|
|
|
+ this.processQuestionGroups(formattedQuestions);
|
|
|
} else {
|
|
|
// 处理单个问题
|
|
|
this.processInterviewData(res);
|
|
@@ -319,7 +333,7 @@
|
|
|
url: '/pages/interview-question/interview-question'
|
|
|
});
|
|
|
}
|
|
|
- }, 1500); // 1.5秒后自动进入下一题
|
|
|
+ }, 1500);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -336,10 +350,22 @@
|
|
|
},
|
|
|
|
|
|
selectOption(index) {
|
|
|
- if (this.showResult) return; // 已显示结果时不能再选择
|
|
|
- console.log('selectOption',index);
|
|
|
+ // 如果是最后一题且已经显示继续按钮,不允许重复作答
|
|
|
+ if (this.currentQuestionIndex === this.questions.length - 1 && this.showContinueButton) {
|
|
|
+ uni.showToast({
|
|
|
+ title: '该题目已完成,请继续下一部分',
|
|
|
+ icon: 'none',
|
|
|
+ duration: 2000
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果已经显示结果,不允许再次选择
|
|
|
+ if (this.showResult) return;
|
|
|
+
|
|
|
+ console.log('selectOption', index);
|
|
|
// 判断当前题目类型
|
|
|
- if (this.currentQuestion.questionType === 2) { // 多选题
|
|
|
+ if (this.currentQuestion.questionType === 2) {
|
|
|
// 如果已经选中,则取消选中
|
|
|
const optionIndex = this.selectedOptions.indexOf(index);
|
|
|
if (optionIndex > -1) {
|
|
@@ -348,46 +374,59 @@
|
|
|
// 否则添加到已选中数组
|
|
|
this.selectedOptions.push(index);
|
|
|
}
|
|
|
- // 多选题不自动提交,需要用户手动点击"进入下一题"按钮
|
|
|
- } else if (this.currentQuestion.questionType === 1 || this.currentQuestion.questionType === 3 || this.currentQuestion.questionType === 4) { // 单选题、看图答题或类型4题目
|
|
|
+ // 多选题不自动提交,需要用户手动点击"提交答案"按钮
|
|
|
+ } else if (this.currentQuestion.questionType === 1 || this.currentQuestion.questionType === 3 || this.currentQuestion.questionType === 4) {
|
|
|
this.selectedOption = index;
|
|
|
+ this.playAiSpeaking();
|
|
|
+
|
|
|
+ setTimeout(() => {
|
|
|
+ this.checkAnswer();
|
|
|
+ this.saveAnswer();
|
|
|
+
|
|
|
+ this.submitCurrentAnswer().then(() => {
|
|
|
+ setTimeout(() => {
|
|
|
+ this.goToNextQuestion();
|
|
|
+ }, 800);
|
|
|
+ }).catch(error => {
|
|
|
+ console.error('提交答案失败:', error);
|
|
|
+ setTimeout(() => {
|
|
|
+ this.goToNextQuestion();
|
|
|
+ }, 1000);
|
|
|
+ });
|
|
|
+ }, 500);
|
|
|
+ } else {
|
|
|
+ // 单选题逻辑
|
|
|
+ this.selectedOption = index;
|
|
|
+ // 保存答案到 answers 数组
|
|
|
+ const answer = {
|
|
|
+ questionId: this.currentQuestion.id,
|
|
|
+ questionType: this.currentQuestion.questionType,
|
|
|
+ answer: index
|
|
|
+ };
|
|
|
+ const existingIndex = this.answers.findIndex(a => a.questionId === answer.questionId);
|
|
|
+ if (existingIndex > -1) {
|
|
|
+ this.answers[existingIndex] = answer;
|
|
|
+ } else {
|
|
|
+ this.answers.push(answer);
|
|
|
+ }
|
|
|
|
|
|
- // 自动提交答案并进入下一题
|
|
|
this.playAiSpeaking();
|
|
|
|
|
|
- // 短暂延迟后提交答案,让用户看到选中效果
|
|
|
setTimeout(() => {
|
|
|
this.checkAnswer();
|
|
|
this.saveAnswer();
|
|
|
|
|
|
- // 提交答案
|
|
|
this.submitCurrentAnswer().then(() => {
|
|
|
- // 提交成功后,短暂延迟后自动进入下一题
|
|
|
setTimeout(() => {
|
|
|
- // 如果是最后一题,跳转到面试页面
|
|
|
- if (this.currentQuestionIndex >= this.questions.length - 1) {
|
|
|
- uni.navigateTo({
|
|
|
- url: '/pages/interview-question/interview-question'
|
|
|
- });
|
|
|
- } else {
|
|
|
- // 否则进入下一题
|
|
|
- this.goToNextQuestion();
|
|
|
- }
|
|
|
- }, 800); // 0.8秒后自动进入下一题
|
|
|
+ this.goToNextQuestion();
|
|
|
+ }, 800);
|
|
|
}).catch(error => {
|
|
|
console.error('提交答案失败:', error);
|
|
|
- // 即使提交失败也继续下一题
|
|
|
setTimeout(() => {
|
|
|
- if (this.currentQuestionIndex >= this.questions.length - 1) {
|
|
|
- uni.navigateTo({
|
|
|
- url: '/pages/interview-question/interview-question'
|
|
|
- });
|
|
|
- } else {
|
|
|
- this.goToNextQuestion();
|
|
|
- }
|
|
|
+ this.goToNextQuestion();
|
|
|
}, 1000);
|
|
|
});
|
|
|
- }, 500); // 0.5秒后提交答案
|
|
|
+ }, 500);
|
|
|
}
|
|
|
},
|
|
|
|
|
@@ -404,10 +443,9 @@
|
|
|
}
|
|
|
|
|
|
// 根据题目类型检查答案是否正确
|
|
|
- if (this.currentQuestion.questionType === 0) { // 开放问题
|
|
|
- // 开放问题没有标准答案,可以根据需要设置为正确或待评估
|
|
|
- this.isAnswerCorrect = true; // 或者设置为 null 表示待评估
|
|
|
- } else if (this.currentQuestion.questionType === 2) { // 多选题
|
|
|
+ if (this.currentQuestion.questionType === 0) {
|
|
|
+ this.isAnswerCorrect = true;
|
|
|
+ } else if (this.currentQuestion.questionType === 2) {
|
|
|
const sortedSelected = [...this.selectedOptions].sort();
|
|
|
const sortedCorrect = [...this.currentQuestion.correctAnswers].sort();
|
|
|
|
|
@@ -416,7 +454,7 @@
|
|
|
} else {
|
|
|
this.isAnswerCorrect = sortedSelected.every((value, index) => value === sortedCorrect[index]);
|
|
|
}
|
|
|
- } else { // 单选题或看图答题 (questionType === 1 或 3)
|
|
|
+ } else {
|
|
|
this.isAnswerCorrect = this.selectedOption === this.currentQuestion.correctAnswer;
|
|
|
}
|
|
|
|
|
@@ -425,6 +463,14 @@
|
|
|
|
|
|
// 保存当前题目的答案
|
|
|
this.saveAnswer();
|
|
|
+
|
|
|
+ // 如果是最后一题,确保状态被保存
|
|
|
+ if (this.currentQuestionIndex === this.questions.length - 1) {
|
|
|
+ this.$nextTick(() => {
|
|
|
+ // 强制更新视图
|
|
|
+ this.$forceUpdate();
|
|
|
+ });
|
|
|
+ }
|
|
|
},
|
|
|
|
|
|
// 修改 nextQuestion 方法,只处理多选题
|
|
@@ -592,27 +638,45 @@
|
|
|
}
|
|
|
},
|
|
|
|
|
|
- // 修改 goToNextQuestion 方法,在切换到下一题时重置当前选项状态
|
|
|
+ // 修改 goToNextQuestion 方法
|
|
|
async goToNextQuestion() {
|
|
|
- // 重置状态
|
|
|
- this.showResult = false;
|
|
|
- // 重置当前选择状态,但不影响已保存的答案
|
|
|
- this.selectedOption = null;
|
|
|
- this.selectedOptions = [];
|
|
|
- this.openQuestionAnswer = ''; // 重置开放问题答案
|
|
|
+ // 检查是否完成当前组的所有题目
|
|
|
+ if (this.currentQuestionIndex >= this.questions.length - 1) {
|
|
|
+ // 当前组的所有题目都已完成,显示继续按钮
|
|
|
+ this.showContinueButton = true;
|
|
|
+
|
|
|
+ // 使用延时确保按钮渲染完成后再滚动
|
|
|
+ setTimeout(() => {
|
|
|
+ // 先重置 scrollTop,然后再设置新值,强制触发更新
|
|
|
+ this.scrollTop = 0;
|
|
|
+
|
|
|
+ // 使用 nextTick 确保视图已更新
|
|
|
+ this.$nextTick(() => {
|
|
|
+ // 延迟设置新的滚动位置
|
|
|
+ setTimeout(() => {
|
|
|
+ this.scrollTop = 10000; // 一个足够大的值
|
|
|
+ }, 100);
|
|
|
+ });
|
|
|
+ }, 200);
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- // 前往下一题
|
|
|
+ // 如果还有下一题,继续下一题
|
|
|
this.currentQuestionIndex++;
|
|
|
-
|
|
|
- // 更新滚动ID,滚动到下一题
|
|
|
this.currentScrollId = 'question-' + this.currentQuestionIndex;
|
|
|
|
|
|
- // 如果已经有下一题的详情,直接使用
|
|
|
+ // 重置状态(只在非最后一题时重置)
|
|
|
+ this.showResult = false;
|
|
|
+ this.selectedOption = null;
|
|
|
+ this.selectedOptions = [];
|
|
|
+ this.openQuestionAnswer = '';
|
|
|
+
|
|
|
+ // 如果有下一题的详情
|
|
|
if (this.questions[this.currentQuestionIndex]) {
|
|
|
// 更新进度
|
|
|
this.progressWidth = (this.currentQuestionIndex + 1) / this.questions.length * 100;
|
|
|
|
|
|
- // 重置计时器但不显示
|
|
|
+ // 重置计时器
|
|
|
this.resetTimer();
|
|
|
|
|
|
// 播放AI介绍下一题
|
|
@@ -621,63 +685,6 @@
|
|
|
setTimeout(() => {
|
|
|
this.pauseAiSpeaking();
|
|
|
}, 2000);
|
|
|
-
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // 否则请求下一题详情
|
|
|
- try {
|
|
|
- this.loading = true;
|
|
|
-
|
|
|
- // 假设您有一个获取单个题目详情的API
|
|
|
- // const res = await getQuestionDetail({
|
|
|
- // interviewId: this.interviewId,
|
|
|
- // questionIndex: this.currentQuestionIndex
|
|
|
- // });
|
|
|
-
|
|
|
- // 模拟API调用
|
|
|
- await new Promise(resolve => setTimeout(resolve, 1000));
|
|
|
- const res = { /* 模拟的题目数据 */ };
|
|
|
-
|
|
|
- // 处理题目数据
|
|
|
- if (res) {
|
|
|
- const formattedQuestion = {
|
|
|
- id: res.id || this.currentQuestionIndex + 1,
|
|
|
- text: res.question || '未知问题',
|
|
|
- options: res.options || [],
|
|
|
- correctAnswer: res.correctAnswer || 0,
|
|
|
- isImportant: res.is_system || false,
|
|
|
- explanation: res.explanation || '',
|
|
|
- questionType: res.question_form || 1,
|
|
|
- questionTypeName: res.question_form_name || '单选题',
|
|
|
- is_required_correct: res.is_required_correct || false,
|
|
|
- correctAnswers: res.correct_answers || [],
|
|
|
- difficulty: res.difficulty || 1,
|
|
|
- difficultyName: res.difficulty_name || '初级'
|
|
|
- };
|
|
|
-
|
|
|
- // 添加到问题列表
|
|
|
- this.questions.push(formattedQuestion);
|
|
|
- }
|
|
|
-
|
|
|
- } catch (error) {
|
|
|
- console.error('获取题目详情失败:', error);
|
|
|
- uni.showToast({
|
|
|
- title: '获取题目失败,请重试',
|
|
|
- icon: 'none'
|
|
|
- });
|
|
|
-
|
|
|
- // 出错时回到上一题
|
|
|
- this.currentQuestionIndex--;
|
|
|
-
|
|
|
- } finally {
|
|
|
- this.loading = false;
|
|
|
-
|
|
|
- // 更新进度
|
|
|
- this.progressWidth = (this.currentQuestionIndex + 1) / this.questions.length * 100;
|
|
|
-
|
|
|
- // 重置计时器
|
|
|
- this.resetTimer();
|
|
|
}
|
|
|
},
|
|
|
|
|
@@ -831,30 +838,121 @@
|
|
|
});
|
|
|
},
|
|
|
|
|
|
- // 修改 isOptionSelected 方法,更精确地判断选项是否被选中
|
|
|
+ // 修改 isOptionSelected 方法
|
|
|
isOptionSelected(question, qIndex, optionIndex) {
|
|
|
// 获取该题的答案
|
|
|
const answer = this.answers.find(a => a.questionId === question.id);
|
|
|
|
|
|
// 如果是当前正在回答的题目
|
|
|
if (qIndex === this.currentQuestionIndex) {
|
|
|
- if (question.questionType === 1 || question.questionType === 3 || question.questionType === 4) { // 单选题
|
|
|
+ if (question.questionType === 1 || question.questionType === 3 || question.questionType === 4) {
|
|
|
return this.selectedOption === optionIndex;
|
|
|
- } else if (question.questionType === 2) { // 多选题
|
|
|
+ } else if (question.questionType === 2) {
|
|
|
return this.selectedOptions.includes(optionIndex);
|
|
|
}
|
|
|
}
|
|
|
// 如果是已回答过的题目,使用保存的答案
|
|
|
else if (answer) {
|
|
|
- if (question.questionType === 1 || question.questionType === 3 || question.questionType === 4) { // 单选题
|
|
|
+ if (question.questionType === 1 || question.questionType === 3 || question.questionType === 4) {
|
|
|
return answer.answer === optionIndex;
|
|
|
- } else if (question.questionType === 2 && Array.isArray(answer.answer)) { // 多选题
|
|
|
+ } else if (question.questionType === 2 && Array.isArray(answer.answer)) {
|
|
|
return answer.answer.includes(optionIndex);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
},
|
|
|
+
|
|
|
+ // 修改 handleOptionClick 方法
|
|
|
+ handleOptionClick(qIndex, index) {
|
|
|
+ // 如果点击的是已完成的题目
|
|
|
+ if (qIndex < this.currentQuestionIndex) {
|
|
|
+ uni.showToast({
|
|
|
+ title: '该题目已完成,无法修改',
|
|
|
+ icon: 'none',
|
|
|
+ duration: 2000
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果是最后一题且已经显示继续按钮,不允许重复作答
|
|
|
+ if (qIndex === this.questions.length - 1 && this.showContinueButton) {
|
|
|
+ uni.showToast({
|
|
|
+ title: '该题目已完成,请继续下一部分',
|
|
|
+ icon: 'none',
|
|
|
+ duration: 2000
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果点击的是还未到的题目
|
|
|
+ if (qIndex > this.currentQuestionIndex) {
|
|
|
+ // 查找第一个未完成的题目索引
|
|
|
+ const firstUnansweredIndex = this.currentQuestionIndex;
|
|
|
+ uni.showToast({
|
|
|
+ title: `请先完成第 ${firstUnansweredIndex + 1} 题`,
|
|
|
+ icon: 'none',
|
|
|
+ duration: 2000
|
|
|
+ });
|
|
|
+
|
|
|
+ // 滚动到当前应该做的题目
|
|
|
+ this.currentScrollId = 'question-' + firstUnansweredIndex;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果是当前题目,调用原有的选择逻辑
|
|
|
+ this.selectOption(index);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 处理题目分组
|
|
|
+ processQuestionGroups(questions) {
|
|
|
+ // 创建三个分组
|
|
|
+ const group1 = []; // category 为 10 或 7 的题目
|
|
|
+ const group2 = []; // category 为 5 的题目
|
|
|
+ const group3 = []; // 其他题目
|
|
|
+
|
|
|
+ // 对题目进行分类
|
|
|
+ questions.forEach(q => {
|
|
|
+ if (q.category === 10 || q.category === 7) {
|
|
|
+ group1.push(q);
|
|
|
+ } else if (q.category === 5) {
|
|
|
+ group2.push(q);
|
|
|
+ } else {
|
|
|
+ group3.push(q);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 按照要求的顺序组合题目组
|
|
|
+ this.questionGroups = [group3, group1, group2].filter(group => group.length > 0);
|
|
|
+
|
|
|
+ // 设置当前显示的题目
|
|
|
+ this.questions = this.questionGroups[0] || [];
|
|
|
+ },
|
|
|
+
|
|
|
+ // 处理继续按钮点击
|
|
|
+ handleContinue() {
|
|
|
+ if (this.currentGroupIndex < this.questionGroups.length - 1) {
|
|
|
+ // 还有下一组题目
|
|
|
+ this.currentGroupIndex++;
|
|
|
+ this.questions = this.questionGroups[this.currentGroupIndex];
|
|
|
+ this.currentQuestionIndex = 0;
|
|
|
+ this.showContinueButton = false;
|
|
|
+ this.currentScrollId = 'question-0';
|
|
|
+
|
|
|
+ // 重置滚动位置
|
|
|
+ this.scrollTop = 0;
|
|
|
+
|
|
|
+ // 重置相关状态
|
|
|
+ this.selectedOption = null;
|
|
|
+ this.selectedOptions = [];
|
|
|
+ this.showResult = false;
|
|
|
+ } else {
|
|
|
+ // 所有组都完成了,跳转到下一个页面
|
|
|
+ uni.navigateTo({
|
|
|
+ url: '/pages/interview-question/interview-question'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
},
|
|
|
// 添加生命周期钩子,确保在组件销毁时清除计时器
|
|
|
beforeDestroy() {
|
|
@@ -1466,7 +1564,7 @@
|
|
|
|
|
|
.option-disabled {
|
|
|
opacity: 0.7;
|
|
|
- pointer-events: none;
|
|
|
+ background-color: #f0f0f0;
|
|
|
}
|
|
|
|
|
|
.question-button-container {
|
|
@@ -1498,4 +1596,23 @@
|
|
|
border-radius: 10rpx;
|
|
|
overflow: hidden;
|
|
|
}
|
|
|
+
|
|
|
+ /* 添加继续按钮样式 */
|
|
|
+ .continue-button-container {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ padding: 20rpx;
|
|
|
+ margin-top: 20rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .continue-button {
|
|
|
+ background-color: #0039b3;
|
|
|
+ color: #ffffff;
|
|
|
+ border-radius: 10rpx;
|
|
|
+ font-size: 30rpx;
|
|
|
+ height: 80rpx;
|
|
|
+ line-height: 80rpx;
|
|
|
+ width: 90%;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
</style>
|