358 lines
17 KiB
HTML
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> |