Files

358 lines
17 KiB
HTML

<div class="bg-gray-50">
<div class="max-w-3xl mx-auto p-4 md:p-6">
<h1 class="text-2xl md:text-3xl font-bold text-text-primary mb-6">AI-HR 审核对话</h1>
<div id="chat-container" class="border border-gray-200 rounded-lg h-[60vh] overflow-y-auto p-4 mb-4 bg-gray-50"></div>
<div class="flex border border-gray-200 rounded-lg overflow-hidden">
<textarea
id="message-input"
placeholder="请输入内容...(回车发送,Shift+回车换行)"
class="flex-1 p-3 focus:outline-none focus:ring-1 focus:ring-primary resize-none"
rows="3"
disabled
></textarea>
<button
id="send-btn"
class="bg-primary text-white px-6 py-3 hover:bg-primary/90 transition-colors disabled:bg-gray-300 disabled:cursor-not-allowed"
disabled
>
发送
</button>
</div>
<!-- 统计信息显示区域 -->
<div class="flex justify-between text-sm text-text-secondary mt-2">
<div id="word-count">当前输入:0 字(最多 2048 字)</div>
<div id="message-count">对话数量:0/50</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const chatContainer = document.getElementById('chat-container');
const messageInput = document.getElementById('message-input');
const sendBtn = document.getElementById('send-btn');
const wordCountEl = document.getElementById('word-count');
const messageCountEl = document.getElementById('message-count');
let isAiResponding = false;
let InviteID = '';
let currentMessageCount = 0;
let isConversationFinal = false; // 标记对话是否终态
// 生成12位邀请码
function generateInviteID() {
return Math.floor(Math.random() * 1000000000000).toString().padStart(12, '0');
}
// 初始化邀请码(从URL获取或生成)
function initInviteID() {
const urlParams = new URLSearchParams(window.location.search);
InviteID = urlParams.get('InviteID');
if (!InviteID || InviteID.length !== 12) {
InviteID = generateInviteID();
const newUrl = `${window.location.pathname}?InviteID=${InviteID}`;
window.history.replaceState({}, document.title, newUrl);
}
}
// 更新字数统计
function updateWordCount() {
const text = messageInput.value;
const count = text.length;
// 超过限制时标红
wordCountEl.className = count > 2048 ? 'text-red-500' : 'text-sm text-text-secondary';
wordCountEl.textContent = `当前输入:${count} 字(最多 2048 字)`;
// 控制发送按钮状态
if (count > 2048 || isAiResponding || isConversationFinal) {
sendBtn.disabled = true;
} else {
sendBtn.disabled = false;
}
}
// 更新对话数量统计
function updateMessageCount() {
// 超过限制时标红
messageCountEl.className = currentMessageCount >= 50 ? 'text-red-500' : 'text-sm text-text-secondary';
messageCountEl.textContent = `对话数量:${currentMessageCount}/50`;
// 达到最大对话数时禁用输入
if (currentMessageCount >= 50) {
messageInput.disabled = true;
sendBtn.disabled = true;
messageInput.placeholder = '对话已达到最大条数,无法继续输入';
}
}
// 渲染终态结果(高亮样式)
function renderFinalResult(status, result, reason = '', mood = '') {
const finalDiv = document.createElement('div');
finalDiv.className = 'mt-6 p-4 rounded-lg bg-gray-100 border border-gray-300';
// 终态标题
const title = document.createElement('h3');
title.className = `text-lg font-bold mb-2 ${status === 0 ? 'text-green-600' : 'text-red-600'}`;
title.textContent = status === 0
? '面试结果:通过 ✅'
: status === 1
? '面试结果:未通过 ❌'
: '无法回答该问题 🚫';
finalDiv.appendChild(title);
// 结果内容(确保不为空)
const resultEl = document.createElement('div');
resultEl.className = 'mb-2 text-gray-800';
resultEl.textContent = result.because;
finalDiv.appendChild(resultEl);
// 拒绝原因(仅拒绝回答时显示)
if (reason) {
const reasonEl = document.createElement('div');
reasonEl.className = 'text-sm text-gray-600 mb-1';
reasonEl.textContent = `原因:${reason}`;
finalDiv.appendChild(reasonEl);
}
// AI心情(仅拒绝回答时显示)
if (mood) {
const moodEl = document.createElement('div');
moodEl.className = 'text-sm text-gray-500 italic';
moodEl.textContent = `AI心情:${mood}`;
finalDiv.appendChild(moodEl);
}
// 终态提示
const tipEl = document.createElement('div');
tipEl.className = 'mt-3 text-xs text-gray-400';
tipEl.textContent = '对话已结束,无法继续发送消息';
finalDiv.appendChild(tipEl);
chatContainer.appendChild(finalDiv);
chatContainer.scrollTop = chatContainer.scrollHeight;
}
// 添加普通消息到聊天容器
function addMessage(sender, text) {
if (sender === 'ai') {
try {
const json = JSON.parse(text);
text = json.because || text;
} catch (e) {
}
}
const messageDiv = document.createElement('div');
messageDiv.className = `mb-4 ${sender === 'user' ? 'flex justify-end' : 'flex justify-start'}`;
const messageBubble = document.createElement('div');
messageBubble.className = `${
sender === 'user'
? 'bg-primary text-white rounded-lg rounded-tl-lg px-4 py-2 max-w-[80%]'
: 'bg-white border border-gray-200 rounded-lg rounded-tr-lg px-4 py-2 max-w-[80%] shadow-sm'
}`;
const senderLabel = document.createElement('div');
senderLabel.className = `text-xs mb-1 ${sender === 'user' ? 'text-white/80' : 'text-text-secondary'}`;
senderLabel.textContent = sender === 'user' ? '您' : 'AI-HR';
messageBubble.appendChild(senderLabel);
const messageContent = document.createElement('div');
messageContent.className = 'whitespace-pre-wrap';
messageContent.textContent = text;
messageBubble.appendChild(messageContent);
messageDiv.appendChild(messageBubble);
chatContainer.appendChild(messageDiv);
chatContainer.scrollTop = chatContainer.scrollHeight;
}
// 锁死对话(禁用输入和发送)
function lockConversation() {
messageInput.disabled = true;
sendBtn.disabled = true;
messageInput.placeholder = '对话已结束,无法继续输入';
}
// 启用输入和发送
function enableInput() {
if (!isConversationFinal && currentMessageCount < 50) {
messageInput.disabled = false;
sendBtn.disabled = false;
messageInput.focus();
}
}
// 发送消息
async function sendMessage() {
if (isConversationFinal) {
alert('对话已结束,无法继续发送消息');
return;
}
const text = messageInput.value.trim();
if (text.length > 2048) {
alert('消息长度不能超过2048字,请精简内容');
return;
}
if (currentMessageCount >= 50) {
alert('已达到最大对话数量(50条),无法继续发送');
return;
}
if (!text || !InviteID) return;
// 添加用户消息到界面
addMessage('user', text);
messageInput.value = '';
currentMessageCount++;
updateMessageCount();
updateWordCount();
// 禁用输入防止重复发送
disableInput();
isAiResponding = true;
try {
const response = await fetch(`/API/SendConversation?InviteID=${InviteID}`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: `Content=${encodeURIComponent(text)}`
});
const result = await response.json();
if (result.Success) {
// 显示AI回复
addMessage('ai', result.Data.AiMessage.Content);
currentMessageCount++;
updateMessageCount();
// 检查是否为终态,若是则渲染终态结果并锁死对话
if (result.Data.IsFinal) {
isConversationFinal = true;
renderFinalResult(
result.Data.FinalStatus,
result.Data.AiMessage.Content,
result.Data.FinalReason,
result.Data.FinalMood
);
lockConversation();
}
} else {
// 显示错误消息
addMessage('ai', `错误:${result.Message}`);
currentMessageCount++;
updateMessageCount();
// 若错误是终态导致的,直接锁死
if (result.IsFinal) {
isConversationFinal = true;
lockConversation();
}
// 处理对话过期/不存在的情况
if (result.Message.includes('已过期') || result.Message.includes('不存在')) {
InviteID = generateInviteID();
window.history.replaceState({}, document.title, `${window.location.pathname}?InviteID=${InviteID}`);
}
}
} catch (error) {
addMessage('ai', '发送消息失败,请重试');
currentMessageCount++;
updateMessageCount();
console.error('发送失败:', error);
} finally {
// 非终态且未达最大条数时,重新启用输入
if (!isConversationFinal && currentMessageCount < 50) {
enableInput();
}
isAiResponding = false;
}
}
// 禁用输入和发送
function disableInput() {
messageInput.disabled = true;
sendBtn.disabled = true;
}
// 初始化聊天(加载历史或创建新对话)
async function initChat() {
initInviteID();
try {
const response = await fetch(`/API/ConversationHistory?InviteID=${InviteID}`);
const result = await response.json();
if (result.Success) {
// 渲染历史消息
result.Data.Messages.forEach(msg => {
addMessage(msg.Role === 'User' ? 'user' : 'ai', msg.Content);
});
currentMessageCount = result.Data.MessageCount;
updateMessageCount();
// 【关键修复】只有 FinalStatus 存在且为有效数值(0/1)时,才判定为终态
// 避免因 FinalStatus 为 undefined 或 null 误触发终态渲染
if (result.Data.FinalStatus !== undefined && result.Data.FinalStatus !== null) {
isConversationFinal = true;
renderFinalResult(
result.Data.FinalStatus,
result.Data.FinalResult || result.Data.Messages[result.Data.Messages.length - 1]?.Content || '无结果信息',
result.Data.FinalReason,
result.Data.FinalMood
);
lockConversation();
} else {
// 非终态则启用输入
enableInput();
}
} else {
// 历史对话获取失败,创建新对话
const initResponse = await fetch(`/API/InitConversation?InviteID=${InviteID}`, {
method: 'POST'
});
const initResult = await initResponse.json();
if (initResult.Success) {
addMessage('ai', initResult.Data.AiMessage.Content);
currentMessageCount = 1;
updateMessageCount();
enableInput();
} else {
addMessage('ai', `初始化失败:${initResult.Message}`);
currentMessageCount = 1;
updateMessageCount();
}
}
updateWordCount(); // 初始化字数统计
} catch (error) {
addMessage('ai', '初始化对话失败,请刷新页面重试');
console.error('初始化失败:', error);
currentMessageCount = 1;
updateMessageCount();
}
}
// 绑定事件监听
messageInput.addEventListener('input', updateWordCount);
// 回车发送,Shift+回车换行
messageInput.addEventListener('keydown', (e) => {
if (isConversationFinal) return;
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
if (!isAiResponding) {
sendMessage();
}
}
});
// 发送按钮点击事件
sendBtn.addEventListener('click', () => {
if (!isConversationFinal && !isAiResponding) {
sendMessage();
}
});
// 启动聊天初始化
initChat();
});
</script>
</div>