|
@@ -1,19 +1,24 @@
|
|
|
<template>
|
|
|
<view class="camera-container">
|
|
|
<!-- 顶部标题栏 -->
|
|
|
- <view class="header">
|
|
|
+ <!-- <view class="header">
|
|
|
<view class="back-button" @click="back">
|
|
|
- <!-- <text class="iconfont icon-back"></text> -->
|
|
|
+ <text class="iconfont icon-back"></text>
|
|
|
</view>
|
|
|
- <view class="title">智能AI面试</view>
|
|
|
+ <view class="title">中国传统文化测试</view>
|
|
|
<view class="controls">
|
|
|
- <!-- <text class="iconfont icon-mic"></text>
|
|
|
- <text class="iconfont icon-settings" @click="toggleSettings"></text> -->
|
|
|
+ <text class="iconfont icon-mic"></text>
|
|
|
+ <text class="iconfont icon-settings" @click="toggleSettings"></text>
|
|
|
</view>
|
|
|
- </view>
|
|
|
+ </view> -->
|
|
|
|
|
|
<!-- 主要内容区域 -->
|
|
|
<view class="content">
|
|
|
+ <!-- 数字人头像 -->
|
|
|
+ <view class="digital-avatar">
|
|
|
+ <image src="/static/avatar.png" mode="aspectFill" class="avatar-image"></image>
|
|
|
+ </view>
|
|
|
+
|
|
|
<!-- 进度指示器 -->
|
|
|
<!-- <view class="progress-container">
|
|
|
<view class="progress-step" v-for="(step, index) in 3" :key="index">
|
|
@@ -27,55 +32,35 @@
|
|
|
|
|
|
<!-- 面试内容区域 -->
|
|
|
<view class="interview-content">
|
|
|
- <!-- 面试官和用户视频区域 -->
|
|
|
- <view class="video-area">
|
|
|
- <!-- AI面试官视频/图像 -->
|
|
|
- <view class="progress-container">
|
|
|
- <view class="progress-step" v-for="(step, index) in 3" :key="index">
|
|
|
- <view class="step-circle" :class="{'active': currentStep >= index+1}">{{index+1}}</view>
|
|
|
- <view class="step-text">{{stepTexts[index]}}</view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- <view class="video-camera">
|
|
|
- <view class="interviewer">
|
|
|
- <!-- 替换为AI数字人视频或动态图像 -->
|
|
|
- <video id="aiInterviewer" src="/static/ai-interviewer.mp4" autoplay loop muted
|
|
|
- object-fit="contain" class="interviewer-video" v-if="useVideo"></video>
|
|
|
- <image src="/static/interviewer.png" mode="aspectFit" class="interviewer-avatar" v-else>
|
|
|
- </image>
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- 用户摄像头预览 -->
|
|
|
- <!-- <view class="user-camera-container">
|
|
|
- <camera device-position="front" flash="off" @error="error"
|
|
|
- style="width: 100%; height: 100%;"></camera>
|
|
|
- </view> -->
|
|
|
- </view>
|
|
|
-
|
|
|
- </view>
|
|
|
-
|
|
|
- <view class="progress-bar">
|
|
|
- <view class="progress-fill" :style="{width: progressWidth + '%'}"></view>
|
|
|
+ <!-- 问题编号和进度 -->
|
|
|
+ <view class="question-number">
|
|
|
+ <text>{{currentQuestionIndex + 1}}-{{currentQuestion.id}}/{{questions.length}}</text>
|
|
|
</view>
|
|
|
+
|
|
|
<!-- 问题和选项区域 -->
|
|
|
<view class="question-area">
|
|
|
- <view class="question">
|
|
|
- <text>{{currentQuestion.text}}</text>
|
|
|
+ <view class="question-importance">
|
|
|
+ <text v-if="currentQuestion.isImportant">重点题</text> {{currentQuestion.text}}
|
|
|
</view>
|
|
|
|
|
|
<view class="options">
|
|
|
<view v-for="(option, index) in currentQuestion.options" :key="index" class="option-item"
|
|
|
- :class="{'option-selected': selectedOption === index}" @click="selectOption(index)">
|
|
|
- <text class="option-label">{{optionLabels[index]}}.</text>
|
|
|
+ :class="{
|
|
|
+ 'option-selected': selectedOption === index,
|
|
|
+ 'option-correct': showResult && index === currentQuestion.correctAnswer,
|
|
|
+ 'option-wrong': showResult && selectedOption === index && index !== currentQuestion.correctAnswer
|
|
|
+ }"
|
|
|
+ @click="selectOption(index)">
|
|
|
<text class="option-text">{{option}}</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
- <button class="next-button" @click="nextQuestion">
|
|
|
- <view class="button-content">
|
|
|
- <text class="timer-text">本题剩余 {{remainingTime}}</text>
|
|
|
- <text class="next-text">进入下一题</text>
|
|
|
- </view>
|
|
|
+ <view class="timer-container">
|
|
|
+ <text class="timer-text">本题剩余时间 {{remainingTime}}</text>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <button class="next-button" @click="nextQuestion" :disabled="selectedOption === null">
|
|
|
+ 下一题
|
|
|
</button>
|
|
|
</view>
|
|
|
</view>
|
|
@@ -90,9 +75,13 @@
|
|
|
<!-- 面试结束弹窗 -->
|
|
|
<view class="interview-end-modal" v-if="showEndModal">
|
|
|
<view class="modal-content">
|
|
|
- <view class="modal-title">面试已结束</view>
|
|
|
- <view class="modal-message">感谢您参加本次面试,我们将尽快与您联系。</view>
|
|
|
- <button type="primary" @click="back">返回首页</button>
|
|
|
+ <view class="modal-title">测试已完成</view>
|
|
|
+ <view class="score-display">{{score}}/{{totalQuestions}}</view>
|
|
|
+ <view class="modal-message">您在本次中国传统文化测试中答对了{{score}}道题目,共{{totalQuestions}}道题目。</view>
|
|
|
+ <view class="modal-buttons">
|
|
|
+ <button type="default" class="modal-button" @click="restartTest">重新测试</button>
|
|
|
+ <button type="primary" class="modal-button" @click="back">返回首页</button>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
@@ -106,44 +95,58 @@
|
|
|
devicePosition: 'front',
|
|
|
interviewStarted: true,
|
|
|
showEndModal: false,
|
|
|
- interviewerMessage: '你好,欢迎来到智能面试间',
|
|
|
currentQuestionIndex: 0,
|
|
|
currentStep: 1,
|
|
|
- stepTexts: ['面试准备', '回答问题', '面试结束'],
|
|
|
+ stepTexts: ['测试准备', '回答问题', '测试结束'],
|
|
|
progressWidth: 50,
|
|
|
- remainingTime: '01:00',
|
|
|
- optionLabels: ['A', 'B', 'C', 'D'],
|
|
|
+ remainingTime: '00:27',
|
|
|
selectedOption: null,
|
|
|
+ showResult: false,
|
|
|
+ isAnswerCorrect: false,
|
|
|
questions: [{
|
|
|
- text: '当我们在工作压力下,有时候我们会感到焦虑和精神紧张。',
|
|
|
+ id: 6,
|
|
|
+ text: '以下不属于中国传统节日的是( )。',
|
|
|
options: [
|
|
|
- '十分不同意',
|
|
|
- '不太同意',
|
|
|
- '不能确定',
|
|
|
- '比较同意'
|
|
|
- ]
|
|
|
+ 'A. 春节',
|
|
|
+ 'B. 端午节',
|
|
|
+ 'C. 重阳节',
|
|
|
+ 'D. 元旦'
|
|
|
+ ],
|
|
|
+ correctAnswer: 3,
|
|
|
+ isImportant: true,
|
|
|
+ explanation: '元旦是公历新年,属于现代节日,而春节、端午节和重阳节都是中国传统节日。'
|
|
|
},
|
|
|
{
|
|
|
- text: '您为什么对我们公司的这个职位感兴趣?',
|
|
|
+ id: 7,
|
|
|
+ text: '下列哪个是中国四大名著之一( )。',
|
|
|
options: [
|
|
|
- '薪资待遇好',
|
|
|
- '公司文化吸引我',
|
|
|
- '职业发展空间大',
|
|
|
- '工作内容有挑战性'
|
|
|
- ]
|
|
|
+ 'A. 聊斋志异',
|
|
|
+ 'B. 西游记',
|
|
|
+ 'C. 世说新语',
|
|
|
+ 'D. 聊斋志异'
|
|
|
+ ],
|
|
|
+ correctAnswer: 1,
|
|
|
+ isImportant: false,
|
|
|
+ explanation: '中国四大名著是《红楼梦》、《西游记》、《水浒传》和《三国演义》。'
|
|
|
},
|
|
|
{
|
|
|
- text: '您认为您的哪些技能和经验最适合这个职位?',
|
|
|
+ id: 8,
|
|
|
+ text: '中国传统文化中"仁义礼智信"五常不包括( )。',
|
|
|
options: [
|
|
|
- '专业技能',
|
|
|
- '沟通能力',
|
|
|
- '团队协作',
|
|
|
- '解决问题的能力'
|
|
|
- ]
|
|
|
+ 'A. 忠',
|
|
|
+ 'B. 孝',
|
|
|
+ 'C. 礼',
|
|
|
+ 'D. 信'
|
|
|
+ ],
|
|
|
+ correctAnswer: 1,
|
|
|
+ isImportant: true,
|
|
|
+ explanation: '儒家的五常是"仁、义、礼、智、信",不包括"孝"。'
|
|
|
}
|
|
|
],
|
|
|
- useVideo: true,
|
|
|
- aiVideoContext: null
|
|
|
+ useVideo: false,
|
|
|
+ timerInterval: null,
|
|
|
+ score: 0,
|
|
|
+ totalQuestions: 0
|
|
|
}
|
|
|
},
|
|
|
computed: {
|
|
@@ -160,16 +163,21 @@
|
|
|
}
|
|
|
// 启动倒计时
|
|
|
this.startTimer();
|
|
|
+ // 设置总题目数
|
|
|
+ this.totalQuestions = this.questions.length;
|
|
|
},
|
|
|
methods: {
|
|
|
startTimer() {
|
|
|
// 模拟倒计时
|
|
|
- let seconds = 60;
|
|
|
+ let seconds = 30; // 每题30秒
|
|
|
this.timerInterval = setInterval(() => {
|
|
|
seconds--;
|
|
|
if (seconds <= 0) {
|
|
|
clearInterval(this.timerInterval);
|
|
|
- this.nextQuestion();
|
|
|
+ // 时间到,自动提交答案
|
|
|
+ if (!this.showResult) {
|
|
|
+ this.checkAnswer();
|
|
|
+ }
|
|
|
}
|
|
|
const min = Math.floor(seconds / 60).toString().padStart(2, '0');
|
|
|
const sec = (seconds % 60).toString().padStart(2, '0');
|
|
@@ -183,23 +191,64 @@
|
|
|
},
|
|
|
|
|
|
selectOption(index) {
|
|
|
+ if (this.showResult) return; // 已显示结果时不能再选择
|
|
|
+
|
|
|
this.selectedOption = index;
|
|
|
this.playAiSpeaking();
|
|
|
-
|
|
|
- setTimeout(() => {
|
|
|
- this.pauseAiSpeaking();
|
|
|
- }, 3000);
|
|
|
+ },
|
|
|
+
|
|
|
+ checkAnswer() {
|
|
|
+ clearInterval(this.timerInterval); // 停止计时
|
|
|
+
|
|
|
+ // 确保当前问题存在
|
|
|
+ if (!this.currentQuestion) {
|
|
|
+ console.error('当前问题不存在');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查答案是否正确
|
|
|
+ this.isAnswerCorrect = this.selectedOption === this.currentQuestion.correctAnswer;
|
|
|
+
|
|
|
+ // 更新分数
|
|
|
+ if (this.isAnswerCorrect) {
|
|
|
+ this.score++;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 直接进入下一题,不显示结果
|
|
|
+ this.goToNextQuestion();
|
|
|
},
|
|
|
|
|
|
nextQuestion() {
|
|
|
+ // 如果还没有显示结果,先检查答案
|
|
|
+ if (this.selectedOption !== null) {
|
|
|
+ this.checkAnswer();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 新增方法,处理进入下一题的逻辑
|
|
|
+ goToNextQuestion() {
|
|
|
+ // 重置状态
|
|
|
+ this.showResult = false;
|
|
|
+ this.selectedOption = null;
|
|
|
+
|
|
|
+ // 前往下一题
|
|
|
this.currentQuestionIndex++;
|
|
|
+
|
|
|
+ // 检查是否已完成所有题目
|
|
|
if (this.currentQuestionIndex >= this.questions.length) {
|
|
|
- this.currentQuestionIndex = 0; // 循环演示
|
|
|
+ // 显示测试结束弹窗
|
|
|
+ this.showEndModal = true;
|
|
|
+ return;
|
|
|
}
|
|
|
- this.selectedOption = null;
|
|
|
+
|
|
|
+ // 重置计时器
|
|
|
this.resetTimer();
|
|
|
+
|
|
|
+ // 更新进度
|
|
|
this.progressWidth = (this.currentQuestionIndex + 1) / this.questions.length * 100;
|
|
|
|
|
|
+ // 播放AI介绍下一题
|
|
|
this.playAiSpeaking();
|
|
|
|
|
|
setTimeout(() => {
|
|
@@ -217,6 +266,7 @@
|
|
|
|
|
|
back() {
|
|
|
// 返回上一页
|
|
|
+ clearInterval(this.timerInterval); // 确保清除计时器
|
|
|
uni.navigateBack();
|
|
|
},
|
|
|
|
|
@@ -239,6 +289,22 @@
|
|
|
if (this.useVideo && this.aiVideoContext) {
|
|
|
this.aiVideoContext.pause();
|
|
|
}
|
|
|
+ },
|
|
|
+
|
|
|
+ // 重新开始测试
|
|
|
+ restartTest() {
|
|
|
+ this.currentQuestionIndex = 0;
|
|
|
+ this.score = 0;
|
|
|
+ this.showEndModal = false;
|
|
|
+ this.showResult = false;
|
|
|
+ this.selectedOption = null;
|
|
|
+ this.resetTimer();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 添加生命周期钩子,确保在组件销毁时清除计时器
|
|
|
+ beforeDestroy() {
|
|
|
+ if (this.timerInterval) {
|
|
|
+ clearInterval(this.timerInterval);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -260,7 +326,7 @@
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
padding: 0 30rpx;
|
|
|
- color: #ffffff;
|
|
|
+ /* color: #ffffff; */
|
|
|
}
|
|
|
|
|
|
.back-button {
|
|
@@ -292,7 +358,7 @@
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
position: relative;
|
|
|
- padding: 90rpx 20rpx 0;
|
|
|
+ padding: 50rpx 20rpx 0;
|
|
|
}
|
|
|
|
|
|
.progress-container {
|
|
@@ -403,7 +469,13 @@
|
|
|
flex-direction: column;
|
|
|
}
|
|
|
|
|
|
- .question {
|
|
|
+ .question-number {
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #666;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .question-importance {
|
|
|
background-color: #f9f9f9;
|
|
|
padding: 20rpx;
|
|
|
border-radius: 10rpx;
|
|
@@ -411,6 +483,11 @@
|
|
|
font-size: 28rpx;
|
|
|
color: #333;
|
|
|
}
|
|
|
+
|
|
|
+ .question-importance text {
|
|
|
+ color: #ff0000;
|
|
|
+ margin-right: 10rpx;
|
|
|
+ }
|
|
|
|
|
|
.options {
|
|
|
display: flex;
|
|
@@ -420,62 +497,58 @@
|
|
|
}
|
|
|
|
|
|
.option-item {
|
|
|
- display: flex;
|
|
|
padding: 20rpx;
|
|
|
background-color: #f5f5f5;
|
|
|
border-radius: 10rpx;
|
|
|
font-size: 28rpx;
|
|
|
transition: all 0.3s;
|
|
|
- border: 1px solid transparent;
|
|
|
+ border: 1px solid #e0e0e0;
|
|
|
}
|
|
|
|
|
|
.option-selected {
|
|
|
background-color: #e6f7ff;
|
|
|
border: 1px solid #1890ff;
|
|
|
}
|
|
|
-
|
|
|
- .option-label {
|
|
|
- margin-right: 10rpx;
|
|
|
- font-weight: bold;
|
|
|
- color: #666;
|
|
|
+
|
|
|
+ .option-correct {
|
|
|
+ background-color: #d4f7d4;
|
|
|
+ border: 1px solid #52c41a;
|
|
|
+ }
|
|
|
+
|
|
|
+ .option-wrong {
|
|
|
+ background-color: #fff1f0;
|
|
|
+ border: 1px solid #ff4d4f;
|
|
|
}
|
|
|
|
|
|
.option-text {
|
|
|
color: #333;
|
|
|
}
|
|
|
+
|
|
|
+ .timer-container {
|
|
|
+ text-align: center;
|
|
|
+ margin: 20rpx 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .timer-text {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #666;
|
|
|
+ }
|
|
|
|
|
|
.next-button {
|
|
|
- background-color: #333333;
|
|
|
+ background-color: #6c5ce7;
|
|
|
color: #ffffff;
|
|
|
- border-radius: 50rpx;
|
|
|
+ border-radius: 10rpx;
|
|
|
font-size: 30rpx;
|
|
|
- margin: 200rpx auto;
|
|
|
+ margin: 20rpx auto;
|
|
|
height: 80rpx;
|
|
|
line-height: 80rpx;
|
|
|
width: 90%;
|
|
|
- padding: 0 30rpx;
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
- box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.2);
|
|
|
- }
|
|
|
-
|
|
|
- .button-content {
|
|
|
- padding: 0 20px;
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- width: 100%;
|
|
|
- align-items: center;
|
|
|
- }
|
|
|
-
|
|
|
- .timer-text {
|
|
|
- color: #4cd964;
|
|
|
- font-size: 28rpx;
|
|
|
+ text-align: center;
|
|
|
}
|
|
|
-
|
|
|
- .next-text {
|
|
|
- color: #ffffff;
|
|
|
- font-size: 28rpx;
|
|
|
+
|
|
|
+ .next-button[disabled] {
|
|
|
+ background-color: #cccccc;
|
|
|
+ color: #999999;
|
|
|
}
|
|
|
|
|
|
.footer {
|
|
@@ -525,12 +598,30 @@
|
|
|
margin-bottom: 30rpx;
|
|
|
}
|
|
|
|
|
|
+ .score-display {
|
|
|
+ font-size: 48rpx;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #6c5ce7;
|
|
|
+ margin: 20rpx 0;
|
|
|
+ }
|
|
|
+
|
|
|
.modal-message {
|
|
|
font-size: 28rpx;
|
|
|
color: #666;
|
|
|
text-align: center;
|
|
|
margin-bottom: 40rpx;
|
|
|
}
|
|
|
+
|
|
|
+ .modal-buttons {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ width: 100%;
|
|
|
+ margin-top: 20rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .modal-button {
|
|
|
+ width: 45%;
|
|
|
+ }
|
|
|
|
|
|
@keyframes speaking {
|
|
|
0% {
|
|
@@ -549,4 +640,20 @@
|
|
|
.speaking {
|
|
|
animation: speaking 1.5s infinite;
|
|
|
}
|
|
|
+
|
|
|
+ .digital-avatar {
|
|
|
+ /* position: absolute;
|
|
|
+ top: 20rpx;
|
|
|
+ right: 20rpx; */
|
|
|
+ width: 120rpx;
|
|
|
+ height: 120rpx;
|
|
|
+ z-index: 10;
|
|
|
+ }
|
|
|
+
|
|
|
+ .avatar-image {
|
|
|
+ width: 120rpx;
|
|
|
+ height: 120rpx;
|
|
|
+ border-radius: 20rpx;
|
|
|
+ border: 2rpx solid #e0e0e0;
|
|
|
+ }
|
|
|
</style>
|