// AI 免费对话 - 前端逻辑 (function () { const chatEl = document.getElementById("chat"); const inputEl = document.getElementById("input"); const sendBtn = document.getElementById("send"); // 保存对话历史(用于多轮上下文) const history = []; let sending = false; /** 获取当前选择的 provider */ function getProvider() { const el = document.querySelector('input[name="provider"]:checked'); return el ? el.value : "deepseek"; } /** 追加一条消息到界面 */ function appendMessage(role, content) { const wrapper = document.createElement("div"); wrapper.className = `message ${role}`; const bubble = document.createElement("div"); bubble.className = "bubble"; bubble.textContent = content; wrapper.appendChild(bubble); chatEl.appendChild(wrapper); chatEl.scrollTop = chatEl.scrollHeight; return bubble; } /** 发送消息并接收 SSE 流 */ async function send() { if (sending) return; const text = inputEl.value.trim(); if (!text) return; sending = true; sendBtn.disabled = true; inputEl.value = ""; // 展示用户消息 appendMessage("user", text); history.push({ role: "user", content: text }); // 展示助手占位 const bubble = appendMessage("assistant", ""); bubble.classList.add("cursor"); let assistantText = ""; try { const resp = await fetch("api/chat", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ provider: getProvider(), messages: history, }), }); if (!resp.ok || !resp.body) { throw new Error("网络请求失败"); } const reader = resp.body.getReader(); const decoder = new TextDecoder("utf-8"); let buffer = ""; while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); // 按 SSE 事件分隔(\n\n) const parts = buffer.split("\n\n"); buffer = parts.pop(); // 保留最后不完整片段 for (const part of parts) { const line = part.trim(); if (!line.startsWith("data:")) continue; const data = line.slice(5).trim(); if (data === "[DONE]") continue; try { const obj = JSON.parse(data); if (obj.error) { assistantText += `\n[错误] ${obj.error}`; bubble.textContent = assistantText; } else if (obj.content) { assistantText += obj.content; bubble.textContent = assistantText; chatEl.scrollTop = chatEl.scrollHeight; } } catch (e) { // 忽略解析错误 } } } } catch (err) { assistantText += `\n[错误] ${err.message}`; bubble.textContent = assistantText; } finally { bubble.classList.remove("cursor"); if (assistantText) { history.push({ role: "assistant", content: assistantText }); } sending = false; sendBtn.disabled = false; inputEl.focus(); } } // 事件绑定 sendBtn.addEventListener("click", send); inputEl.addEventListener("keydown", function (e) { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); send(); } }); })();