LPC/static/js/main.js

434 lines
13 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

let currentFeature = null;
let interviewId = localStorage.getItem('interviewId') || null;
let conversationKey = 'default';
function showFeature(feature) {
document.querySelectorAll('.tool-section').forEach(section => {
section.style.display = 'none';
});
document.querySelectorAll('.feature-card').forEach(card => {
card.style.transform = '';
card.style.boxShadow = '';
});
if (feature === null) {
currentFeature = null;
return;
}
const featureCard = document.querySelector(`.feature-card[onclick="showFeature('${feature}')"]`);
if (featureCard) {
featureCard.style.transform = 'translateY(-5px)';
featureCard.style.boxShadow = '0 10px 30px rgba(102, 126, 234, 0.3)';
}
currentFeature = feature;
const sectionMap = {
'resume': 'resume-section',
'interview': 'interview-section',
'feedback': 'interview-section'
};
const targetSection = sectionMap[feature];
if (targetSection) {
document.getElementById(targetSection).style.display = 'block';
if (feature === 'resume') {
document.getElementById('resume-result').style.display = 'none';
}
}
}
function showLoading(show) {
document.getElementById('loading-overlay').style.display = show ? 'flex' : 'none';
}
async function optimizeResume() {
const targetPosition = document.getElementById('target-position').value.trim();
const resumeContent = document.getElementById('resume-content').value.trim();
if (!resumeContent) {
alert('请输入简历内容');
return;
}
showLoading(true);
try {
const response = await fetch('/api/resume/optimize', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
resume_content: resumeContent,
target_position: targetPosition
})
});
const data = await response.json();
if (data.error) {
alert(data.error);
return;
}
const suggestionsDiv = document.getElementById('resume-suggestions');
suggestionsDiv.innerHTML = formatSuggestions(data.suggestions);
document.getElementById('resume-result').style.display = 'block';
} catch (error) {
alert('优化失败:' + error.message);
} finally {
showLoading(false);
}
}
function formatSuggestions(text) {
if (!text) return '<p>未能生成优化建议,请重试。</p>';
let html = text
.replace(/\n\n/g, '</p><p>')
.replace(/\n/g, '<br>');
return '<p>' + html + '</p>';
}
async function startInterview() {
const jobPosition = document.getElementById('job-position').value.trim();
if (!jobPosition) {
alert('请输入目标岗位');
return;
}
const difficulty = document.querySelector('input[name="difficulty"]:checked').value;
showLoading(true);
try {
const response = await fetch('/api/interview/start', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
job_position: jobPosition,
difficulty: difficulty
})
});
const data = await response.json();
if (data.error) {
alert(data.error);
return;
}
interviewId = data.interview_id;
localStorage.setItem('interviewId', interviewId);
document.getElementById('interview-setup').style.display = 'none';
document.getElementById('interview-active').style.display = 'flex';
document.getElementById('current-position').textContent = '目标岗位:' + data.job_position;
updatePhaseBadge(data.phase);
const messagesContainer = document.getElementById('interview-messages');
messagesContainer.innerHTML = '';
addInterviewMessage(data.question, 'interviewer');
} catch (error) {
alert('开始面试失败:' + error.message);
} finally {
showLoading(false);
}
}
function updatePhaseBadge(phase) {
const phaseMap = {
'intro': '自我介绍',
'professional': '专业能力',
'scenario': '情景假设',
'career': '职业规划',
'closing': '面试结束'
};
document.getElementById('current-phase').textContent = phaseMap[phase] || phase;
}
function addInterviewMessage(content, type) {
if (!content) return;
const messagesContainer = document.getElementById('interview-messages');
const messageDiv = document.createElement('div');
messageDiv.className = 'message ' + type;
messageDiv.textContent = content;
messagesContainer.appendChild(messageDiv);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}
async function submitAnswer() {
const answerInput = document.getElementById('answer-input');
const answer = answerInput.value.trim();
if (!answer) {
alert('请输入你的回答');
return;
}
addInterviewMessage(answer, 'candidate');
answerInput.value = '';
showLoading(true);
try {
const response = await fetch('/api/interview/answer', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
interview_id: interviewId,
answer: answer,
request_feedback: false
})
});
const data = await response.json();
if (data.error) {
alert(data.error);
return;
}
if (data.ended) {
// 面试结束调用endInterview获取生成的面试反馈
endInterview();
} else if (data.question) {
addInterviewMessage(data.question, 'interviewer');
updatePhaseBadge(data.phase);
}
} catch (error) {
alert('提交回答失败:' + error.message);
} finally {
showLoading(false);
}
}
async function requestFeedback() {
const answerInput = document.getElementById('answer-input');
const answer = answerInput.value.trim();
if (!answer) {
alert('请先输入你的回答');
return;
}
addInterviewMessage(answer, 'candidate');
answerInput.value = '';
showLoading(true);
try {
const response = await fetch('/api/interview/answer', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
interview_id: interviewId,
answer: answer,
request_feedback: true
})
});
const data = await response.json();
if (data.error) {
alert(data.error);
return;
}
if (data.feedback) {
addInterviewMessage(data.feedback, 'feedback');
// 添加返回面试的按钮
const messagesContainer = document.getElementById('interview-messages');
const returnButton = document.createElement('button');
returnButton.className = 'return-interview-btn';
returnButton.textContent = '返回面试';
returnButton.onclick = function() {
// 移除按钮
this.remove();
// 可以在这里添加额外的逻辑,比如重新启用输入框等
};
const buttonContainer = document.createElement('div');
buttonContainer.className = 'return-button-container';
buttonContainer.appendChild(returnButton);
messagesContainer.appendChild(buttonContainer);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}
} catch (error) {
alert('获取反馈失败:' + error.message);
} finally {
showLoading(false);
}
}
async function endInterview() {
const messages = [];
document.querySelectorAll('.interview-messages .message').forEach(msg => {
messages.push({
role: msg.classList.contains('interviewer') ? 'assistant' : 'user',
content: msg.textContent
});
});
showLoading(true);
try {
const response = await fetch('/api/interview/feedback', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
interview_id: interviewId,
conversation_history: messages
})
});
const data = await response.json();
if (data.error) {
alert(data.error);
return;
}
showFinalFeedback(data.feedback);
} catch (error) {
console.error('获取反馈失败:', error);
alert('获取反馈失败,请稍后重试');
} finally {
showLoading(false);
}
}
function showFinalFeedback(feedbackData) {
document.getElementById('interview-active').style.display = 'none';
document.getElementById('interview-feedback').style.display = 'block';
const feedbackContent = document.getElementById('feedback-content');
if (typeof feedbackData === 'string') {
// 将Markdown风格的列表和标题转换为HTML
let formattedFeedback = feedbackData
// 转换标题
.replace(/^###\s+(.*)$/gm, '<h4>$1</h4>')
.replace(/^##\s+(.*)$/gm, '<h3>$1</h3>')
// 转换列表
.replace(/^\*\s+(.*)$/gm, '<li>$1</li>')
.replace(/(?:<li>.*<\/li>\n?)+/gm, '<ul>$&</ul>')
// 转换换行
.replace(/\n\n/g, '</p><p>')
.replace(/\n/g, '<br>');
feedbackContent.innerHTML = `<div class="feedback-container">${formattedFeedback}</div>`;
} else {
let html = '<h4>面试综合评估</h4>';
feedbackData.forEach(msg => {
html += `<p><strong>${msg.role === 'assistant' ? '面试官' : '你'}</strong>${msg.content}</p>`;
});
feedbackContent.innerHTML = html;
}
}
function resetInterview() {
interviewId = null;
localStorage.removeItem('interviewId');
document.getElementById('interview-setup').style.display = 'block';
document.getElementById('interview-active').style.display = 'none';
document.getElementById('interview-feedback').style.display = 'none';
document.getElementById('interview-messages').innerHTML = '';
document.getElementById('answer-input').value = '';
document.getElementById('job-position').value = '';
}
async function sendChatMessage() {
const chatInput = document.getElementById('chat-input');
const message = chatInput.value.trim();
if (!message) {
return;
}
const messagesContainer = document.getElementById('chat-messages');
const userMessageDiv = document.createElement('div');
userMessageDiv.className = 'chat-message user';
userMessageDiv.textContent = message;
messagesContainer.appendChild(userMessageDiv);
chatInput.value = '';
messagesContainer.scrollTop = messagesContainer.scrollHeight;
showLoading(true);
try {
const response = await fetch('/api/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
message: message,
system_type: 'general_assistant',
conversation_key: conversationKey
})
});
const data = await response.json();
if (data.error) {
alert(data.error);
return;
}
const assistantMessageDiv = document.createElement('div');
assistantMessageDiv.className = 'chat-message assistant';
assistantMessageDiv.textContent = data.response;
messagesContainer.appendChild(assistantMessageDiv);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
} catch (error) {
alert('发送消息失败:' + error.message);
} finally {
showLoading(false);
}
}
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('chat-input').addEventListener('keypress', function(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendChatMessage();
}
});
document.getElementById('answer-input').addEventListener('keypress', function(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
submitAnswer();
}
});
});