450 lines
14 KiB
JavaScript
450 lines
14 KiB
JavaScript
// AI帮你面试 - 核心JavaScript功能
|
||
|
||
// 全局变量
|
||
let currentQuestionId = 1;
|
||
const totalQuestions = 4;
|
||
|
||
// API配置
|
||
const API_CONFIG = {
|
||
// 开发环境API密钥(实际生产环境应使用更安全的方式管理)
|
||
apiKey: 'sk-0944c102849e45d9ab3f24d5169de289',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'X-API-Key': 'sk-0944c102849e45d9ab3f24d5169de289'
|
||
}
|
||
};
|
||
|
||
// 面试流程数据(与后端保持一致)
|
||
const interviewProcess = [
|
||
{"id": 1, "title": "自我介绍", "description": "请简要介绍您自己,包括教育背景和工作经验", "duration": "3分钟"},
|
||
{"id": 2, "title": "技术能力评估", "description": "回答技术相关问题,展示专业知识", "duration": "15分钟"},
|
||
{"id": 3, "title": "项目经验分享", "description": "分享您参与的重要项目和成果", "duration": "10分钟"},
|
||
{"id": 4, "title": "问题与解答", "description": "您可以向面试官提问", "duration": "7分钟"}
|
||
];
|
||
|
||
// 页面加载完成后初始化
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// 初始化面试流程界面
|
||
initInterviewProcess();
|
||
|
||
// 绑定事件监听器
|
||
bindEventListeners();
|
||
});
|
||
|
||
// 初始化面试流程界面
|
||
function initInterviewProcess() {
|
||
// 高亮第一个步骤
|
||
updateStepHighlight(currentQuestionId);
|
||
|
||
// 更新当前问题显示
|
||
updateCurrentQuestion(currentQuestionId);
|
||
}
|
||
|
||
// 绑定事件监听器
|
||
function bindEventListeners() {
|
||
// 分析按钮
|
||
const analyzeBtn = document.getElementById('analyze-btn');
|
||
if (analyzeBtn) {
|
||
analyzeBtn.addEventListener('click', handleAnalyzeAnswer);
|
||
}
|
||
|
||
// 下一题按钮
|
||
const nextBtn = document.getElementById('next-question-btn');
|
||
if (nextBtn) {
|
||
nextBtn.addEventListener('click', handleNextQuestion);
|
||
}
|
||
|
||
// 返回修改按钮
|
||
const backBtn = document.getElementById('back-to-question-btn');
|
||
if (backBtn) {
|
||
backBtn.addEventListener('click', handleBackToQuestion);
|
||
}
|
||
|
||
// 完成面试按钮
|
||
const finishBtn = document.getElementById('finish-interview-btn');
|
||
if (finishBtn) {
|
||
finishBtn.addEventListener('click', handleFinishInterview);
|
||
}
|
||
|
||
// 关闭结果弹窗按钮
|
||
const closeModalBtn = document.querySelector('.close');
|
||
if (closeModalBtn) {
|
||
closeModalBtn.addEventListener('click', closeResultsModal);
|
||
}
|
||
|
||
// 点击弹窗外部关闭
|
||
const modal = document.getElementById('results-modal');
|
||
if (modal) {
|
||
modal.addEventListener('click', function(event) {
|
||
if (event.target === modal) {
|
||
closeResultsModal();
|
||
}
|
||
});
|
||
}
|
||
|
||
// 点击流程步骤切换问题
|
||
const processSteps = document.querySelectorAll('.process-step');
|
||
processSteps.forEach(step => {
|
||
step.addEventListener('click', function() {
|
||
const stepId = parseInt(this.dataset.stepId);
|
||
if (stepId <= currentQuestionId) {
|
||
switchToQuestion(stepId);
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
// 处理AI分析回答
|
||
function handleAnalyzeAnswer() {
|
||
const answerText = document.getElementById('answer-text').value;
|
||
|
||
if (!answerText.trim()) {
|
||
alert('请先输入您的回答!');
|
||
return;
|
||
}
|
||
|
||
// 显示加载状态
|
||
const analyzeBtn = document.getElementById('analyze-btn');
|
||
const originalText = analyzeBtn.textContent;
|
||
analyzeBtn.textContent = '分析中...';
|
||
analyzeBtn.disabled = true;
|
||
|
||
// 模拟AI分析过程(实际项目中这里会调用后端API)
|
||
setTimeout(() => {
|
||
// 调用AI分析API
|
||
analyzeAnswerAPI(currentQuestionId, answerText)
|
||
.then(data => {
|
||
// 显示分析结果
|
||
displayAnalysisResults(data);
|
||
|
||
// 更新UI
|
||
document.getElementById('question-stage').classList.add('hidden');
|
||
document.getElementById('analysis-stage').classList.remove('hidden');
|
||
})
|
||
.catch(error => {
|
||
console.error('分析失败:', error);
|
||
alert('分析失败,请稍后重试!');
|
||
})
|
||
.finally(() => {
|
||
// 恢复按钮状态
|
||
analyzeBtn.textContent = originalText;
|
||
analyzeBtn.disabled = false;
|
||
});
|
||
}, 1500);
|
||
}
|
||
|
||
// 调用AI分析API
|
||
async function analyzeAnswerAPI(questionId, answer) {
|
||
try {
|
||
const response = await fetch('/analyze_answer', {
|
||
method: 'POST',
|
||
headers: API_CONFIG.headers,
|
||
body: JSON.stringify({
|
||
question_id: questionId,
|
||
answer: answer
|
||
})
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error('分析请求失败');
|
||
}
|
||
|
||
return await response.json();
|
||
} catch (error) {
|
||
console.error('API调用错误:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 显示AI分析结果
|
||
function displayAnalysisResults(data) {
|
||
// 更新综合评分
|
||
document.getElementById('overall-score').textContent = data.overall_score;
|
||
|
||
// 更新内容分析进度条
|
||
updateProgressBar('completeness-bar', 'completeness-value', data.content_analysis.completeness);
|
||
updateProgressBar('relevance-bar', 'relevance-value', data.content_analysis.relevance);
|
||
updateProgressBar('depth-bar', 'depth-value', data.content_analysis.depth);
|
||
|
||
// 更新建议列表
|
||
const suggestionsList = document.getElementById('suggestions-list');
|
||
suggestionsList.innerHTML = '';
|
||
|
||
data.suggestions.forEach(suggestion => {
|
||
const li = document.createElement('li');
|
||
li.textContent = suggestion;
|
||
suggestionsList.appendChild(li);
|
||
});
|
||
}
|
||
|
||
// 更新进度条
|
||
function updateProgressBar(barId, valueId, percentage) {
|
||
const bar = document.getElementById(barId);
|
||
const value = document.getElementById(valueId);
|
||
|
||
bar.style.width = `${percentage}%`;
|
||
value.textContent = `${percentage}%`;
|
||
}
|
||
|
||
// 处理下一题按钮点击
|
||
function handleNextQuestion() {
|
||
if (currentQuestionId < totalQuestions) {
|
||
currentQuestionId++;
|
||
switchToQuestion(currentQuestionId);
|
||
} else {
|
||
alert('这是最后一个问题!');
|
||
}
|
||
}
|
||
|
||
// 切换到指定问题
|
||
function switchToQuestion(questionId) {
|
||
currentQuestionId = questionId;
|
||
|
||
// 更新当前问题显示
|
||
updateCurrentQuestion(questionId);
|
||
|
||
// 更新步骤高亮
|
||
updateStepHighlight(questionId);
|
||
|
||
// 切换到问题阶段
|
||
document.getElementById('question-stage').classList.remove('hidden');
|
||
document.getElementById('analysis-stage').classList.add('hidden');
|
||
|
||
// 清空回答输入
|
||
document.getElementById('answer-text').value = '';
|
||
}
|
||
|
||
// 更新当前问题显示
|
||
function updateCurrentQuestion(questionId) {
|
||
const question = interviewProcess.find(q => q.id === questionId);
|
||
if (question) {
|
||
document.getElementById('current-question-title').textContent = question.title;
|
||
document.getElementById('current-question-description').textContent = question.description;
|
||
}
|
||
}
|
||
|
||
// 更新步骤高亮
|
||
function updateStepHighlight(questionId) {
|
||
const steps = document.querySelectorAll('.process-step');
|
||
steps.forEach(step => {
|
||
const stepId = parseInt(step.dataset.stepId);
|
||
if (stepId === questionId) {
|
||
step.classList.add('active');
|
||
} else if (stepId < questionId) {
|
||
step.classList.add('completed');
|
||
step.classList.remove('active');
|
||
} else {
|
||
step.classList.remove('active', 'completed');
|
||
}
|
||
});
|
||
}
|
||
|
||
// 处理返回修改按钮
|
||
function handleBackToQuestion() {
|
||
document.getElementById('analysis-stage').classList.add('hidden');
|
||
document.getElementById('question-stage').classList.remove('hidden');
|
||
}
|
||
|
||
// 处理完成面试按钮
|
||
function handleFinishInterview() {
|
||
// 显示确认对话框
|
||
if (confirm('确定要完成面试吗?')) {
|
||
// 调用获取面试结果API
|
||
getInterviewResults()
|
||
.then(results => {
|
||
displayInterviewResults(results);
|
||
openResultsModal();
|
||
})
|
||
.catch(error => {
|
||
console.error('获取结果失败:', error);
|
||
alert('获取面试结果失败,请稍后重试!');
|
||
});
|
||
}
|
||
}
|
||
|
||
// 获取面试结果
|
||
async function getInterviewResults() {
|
||
try {
|
||
const response = await fetch('/get_results', {
|
||
headers: API_CONFIG.headers
|
||
});
|
||
if (!response.ok) {
|
||
throw new Error('获取结果请求失败');
|
||
}
|
||
return await response.json();
|
||
} catch (error) {
|
||
console.error('API调用错误:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 显示面试结果
|
||
function displayInterviewResults(results) {
|
||
const container = document.getElementById('results-container');
|
||
|
||
container.innerHTML = `
|
||
<div class="score-card">
|
||
<h3>最终评分</h3>
|
||
<div class="score-value">${results.overall_score}</div>
|
||
</div>
|
||
|
||
<div class="results-details">
|
||
<h3>分类评分</h3>
|
||
<div class="category-scores">
|
||
<div class="score-item">
|
||
<label>专业知识</label>
|
||
<div class="progress">
|
||
<div class="progress-fill" style="width: ${results.category_scores.professional_knowledge}%"></div>
|
||
</div>
|
||
<span>${results.category_scores.professional_knowledge}%</span>
|
||
</div>
|
||
|
||
<div class="score-item">
|
||
<label>沟通能力</label>
|
||
<div class="progress">
|
||
<div class="progress-fill" style="width: ${results.category_scores.communication_skills}%"></div>
|
||
</div>
|
||
<span>${results.category_scores.communication_skills}%</span>
|
||
</div>
|
||
|
||
<div class="score-item">
|
||
<label>解决问题</label>
|
||
<div class="progress">
|
||
<div class="progress-fill" style="width: ${results.category_scores.problem_solving}%"></div>
|
||
</div>
|
||
<span>${results.category_scores.problem_solving}%</span>
|
||
</div>
|
||
|
||
<div class="score-item">
|
||
<label>经验相关性</label>
|
||
<div class="progress">
|
||
<div class="progress-fill" style="width: ${results.category_scores.experience_relevance}%"></div>
|
||
</div>
|
||
<span>${results.category_scores.experience_relevance}%</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="strengths-improvements">
|
||
<div class="strengths">
|
||
<h3>👍 优点</h3>
|
||
<ul>
|
||
${results.strengths.map(strength => `<li>${strength}</li>`).join('')}
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="improvements">
|
||
<h3>📈 需要改进的地方</h3>
|
||
<ul>
|
||
${results.improvement_areas.map(area => `<li>${area}</li>`).join('')}
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="final-recommendation">
|
||
<h3>💡 最终建议</h3>
|
||
<p class="recommendation-text">${results.final_recommendation}</p>
|
||
</div>
|
||
`;
|
||
|
||
// 添加结果页面样式
|
||
addResultsStyles();
|
||
}
|
||
|
||
// 添加结果页面样式
|
||
function addResultsStyles() {
|
||
// 动态添加结果页面需要的CSS样式
|
||
const style = document.createElement('style');
|
||
style.textContent = `
|
||
.results-details {
|
||
margin: 30px 0;
|
||
}
|
||
|
||
.category-scores {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||
gap: 20px;
|
||
}
|
||
|
||
.score-item {
|
||
background: white;
|
||
padding: 15px;
|
||
border-radius: 10px;
|
||
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
|
||
}
|
||
|
||
.score-item label {
|
||
display: block;
|
||
margin-bottom: 10px;
|
||
font-weight: bold;
|
||
color: #555;
|
||
}
|
||
|
||
.strengths-improvements {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||
gap: 30px;
|
||
margin: 30px 0;
|
||
}
|
||
|
||
.strengths, .improvements {
|
||
background: white;
|
||
padding: 20px;
|
||
border-radius: 10px;
|
||
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
|
||
}
|
||
|
||
.strengths h3, .improvements h3 {
|
||
margin-bottom: 15px;
|
||
color: #333;
|
||
border-bottom: 2px solid #eee;
|
||
padding-bottom: 10px;
|
||
}
|
||
|
||
.strengths ul, .improvements ul {
|
||
list-style: none;
|
||
}
|
||
|
||
.strengths li, .improvements li {
|
||
padding: 10px 0;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
color: #555;
|
||
}
|
||
|
||
.strengths li:last-child, .improvements li:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.final-recommendation {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
padding: 30px;
|
||
border-radius: 10px;
|
||
text-align: center;
|
||
}
|
||
|
||
.final-recommendation h3 {
|
||
margin-bottom: 20px;
|
||
font-size: 1.5rem;
|
||
}
|
||
|
||
.recommendation-text {
|
||
font-size: 1.2rem;
|
||
font-weight: bold;
|
||
line-height: 1.6;
|
||
}
|
||
`;
|
||
document.head.appendChild(style);
|
||
}
|
||
|
||
// 打开结果弹窗
|
||
function openResultsModal() {
|
||
document.getElementById('results-modal').classList.remove('hidden');
|
||
}
|
||
|
||
// 关闭结果弹窗
|
||
function closeResultsModal() {
|
||
document.getElementById('results-modal').classList.add('hidden');
|
||
}
|
||
|