diff --git a/index.html b/index.html index b07c5d0..469153b 100755 --- a/index.html +++ b/index.html @@ -1046,6 +1046,11 @@ align-items: center; height: 100vh; } + .no-after::after { + content: none !important; + display: none !important; +} + .popbgc { background-color: rgba(0, 0, 0, 0.4); @@ -1189,7 +1194,7 @@ .thinking-loading::after { content: ''; - animation: dots 1.5s infinite; + animation: dots 0.8s infinite; } @keyframes dots { @@ -1701,14 +1706,14 @@ document.querySelectorAll('.logout1').forEach(el => { el.style.display = 'none'; }); - console.log(3) + document.querySelectorAll('.username').forEach((e) => { e.textContent = params.get("nickname") || getcookie("nickname"); }) document.querySelector(".userinfo-wrapper img").src = getcookie("headimgurl") || params.get("headingimg"); userId = decodeURIComponent(Number(escape(atob(encodedUserId)))); - console.log(4) + } else { userId = decodeURIComponent(Number(escape(atob(encodedUserId)))); @@ -1929,7 +1934,7 @@ } isPre = true;//s是否预设题目 - console.log(isUpload, 88) + if (!sendParam.imageBase64 && !fileInput.files.length) { // if (!isUpload[activeChatId]) { if (!hasImage) { @@ -2081,7 +2086,7 @@ } document.addEventListener('click', (e) => { - console.log(toggleButton.contains(e.target), panel.contains(e.target)) + if (!toggleButton.contains(e.target) && !panel.contains(e.target)) { panel.classList.remove('show'); } @@ -2119,7 +2124,7 @@ sendParam.sessionId = activeChatId; getHistory(); sessIdList.forEach((item, index) => { - console.log(item, activeChatId); + const item1 = document.createElement("div"); item1.setAttribute("data-sessionid", item.sessId); @@ -2160,7 +2165,8 @@ if (msg.image_base64) { hasImage = true; // 只要有一个不为空,就置为 true } - addMessage(msg.content, msg.role, msg.image_base64,msg.thinking); + + addMessage(msg.content, msg.role, msg.image_base64,'',msg.thinking); }); scrollToBottom(); } @@ -2185,7 +2191,8 @@ } } // 内容 角色 图片 创造时间 - function addMessage(content = '', role = "assistant", imgUrl = null, time = null,thinking='') { + function addMessage(content = '', role = "assistant", imgUrl = null, time = null,thinking) { + const msg = document.createElement("div"); msg.className = `message ${role}`; @@ -2205,8 +2212,8 @@ // 思考过程(历史:已完成) // ========================= let thinkingContentDiv = null; - - if (role === "assistant" && thinking) { + if (thinking) { + const thinkingBox = document.createElement("div"); thinkingBox.className = "thinking-box"; @@ -2232,7 +2239,7 @@ header.onclick = () => { thinkingContentDiv.classList.toggle("collapsed"); toggle.textContent = - thinkingContentDiv.classList.contains("collapsed") ? "▶" : "⌄"; + thinkingContentDiv.classList.contains("collapsed") ? "▶" : ""; }; header.appendChild(title); @@ -2276,7 +2283,9 @@ shareSpan.style.opacity = "0.75"; shareSpan.title = "分享"; - shareSpan.innerHTML = ``; + shareSpan.innerHTML = ` + + `; shareSpan.onclick = () => { generatePoster(content, imgUrl); }; @@ -2289,7 +2298,9 @@ copySpan.style.opacity = "0.75"; copySpan.title = "复制内容"; - copySpan.innerHTML = ``; + copySpan.innerHTML = ` + + `; copySpan.onclick = async () => { await navigator.clipboard.writeText(content); showToast("复制成功"); @@ -2372,7 +2383,7 @@ function sendMessage(isPre) { - console.log("发送消息1122"); + if (controller) { showToast("请先停止对话"); return; @@ -2504,7 +2515,7 @@ const getAiResponse = async (img) => { thinkingToggleSpan = document.createElement("span"); thinkingToggleSpan.className = "thinking-toggle"; - thinkingToggleSpan.textContent = "⌄"; + thinkingToggleSpan.textContent = ""; thinkingContentDiv = document.createElement("div"); thinkingContentDiv.className = "thinking-content"; @@ -2582,11 +2593,111 @@ const getAiResponse = async (img) => { if (!line.startsWith("data:")) continue; const data = line.slice(5).trim(); - if (data === "[DONE]") { - scrollToBottom(); - controller = null; - return; - } + if (data == "[DONE]") { + console.log("完成回答"); + controller = null; + // 更新聊天滚动条 + + sendBtnImg.classList.remove('none'); + stopBtn.forEach(e => { + e.classList.add('none') + + }) + // renderChatList(); + // msgDiv.innerHTML = marked.parse(aiMsg.content); + // const shareSpan = document.createElement("span"); + // shareSpan.style.display = "inline-flex"; + // shareSpan.style.alignItems = "center"; + // shareSpan.style.cursor = "pointer"; + // shareSpan.style.marginTop = "8px"; + // shareSpan.style.opacity = "0.75"; + // shareSpan.style.transition = "opacity .2s"; + // shareSpan.title = "分享"; + + // shareSpan.onmouseenter = () => shareSpan.style.opacity = "1"; + // shareSpan.onmouseleave = () => shareSpan.style.opacity = "0.75"; + + // shareSpan.innerHTML = ` + // + // `; + + // shareSpan.onclick = () => { + // generatePoster(aiMsg.content); + // }; + + // if (role === "assistant") { + // 分享按钮 + const shareSpan = document.createElement("span"); + shareSpan.style.display = "inline-flex"; + shareSpan.style.alignItems = "center"; + shareSpan.style.cursor = "pointer"; + shareSpan.style.marginTop = "8px"; + shareSpan.style.opacity = "0.75"; + shareSpan.style.transition = "opacity .2s"; + shareSpan.title = "分享"; + + shareSpan.onmouseenter = () => shareSpan.style.opacity = "1"; + shareSpan.onmouseleave = () => shareSpan.style.opacity = "0.75"; + + shareSpan.innerHTML = ` + + `; + + + shareSpan.onclick = () => { + generatePoster(answerContentDiv.innerHTML, img); + }; + + // msg.appendChild(shareSpan); + + // ====================== + // ✅ 复制按钮(新增) + // ====================== + const copySpan = document.createElement("span"); + copySpan.style.display = "inline-flex"; + copySpan.style.alignItems = "center"; + copySpan.style.cursor = "pointer"; + copySpan.style.marginTop = "8px"; + copySpan.style.marginLeft = "10px"; + copySpan.style.opacity = "0.75"; + copySpan.style.transition = "opacity .2s"; + copySpan.title = "复制内容"; + + copySpan.onmouseenter = () => copySpan.style.opacity = "1"; + copySpan.onmouseleave = () => copySpan.style.opacity = "0.75"; + + copySpan.innerHTML = ` + + `; + + copySpan.onclick = async () => { + try { + await navigator.clipboard.writeText(answerBuffer); + copySpan.style.opacity = "1"; + showToast("复制成功") + copySpan.title = "复制成功"; + setTimeout(() => { copySpan.title = "复制内容"; }, 1200); + } catch (err) { + alert("复制失败,可能不支持 HTTPS 环境以外复制。"); + } + }; + + // } + + setTimeout(() => { + msgDiv.appendChild(shareSpan); + msgDiv.appendChild(copySpan); + }, 1000) + + + console.log("shareSpan appended?", msgDiv.contains(shareSpan)); + + + scrollToBottom(); + + + return; + } let obj; try { @@ -2886,12 +2997,12 @@ const getAiResponse = async (img) => { function uploadFile(file) { if (file) { - console.log("上传文件:", file.name, URL.createObjectURL(file)); + const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = async function () { - console.log(reader.result) + sendParam.imageBase64 = reader.result; document.querySelectorAll(".img-row").forEach(e => { e.classList.remove("none"); @@ -3073,7 +3184,7 @@ const getAiResponse = async (img) => { // } function getSessionList() { - console.log(userId, "huoqu"); + sendParam.userId = userId; fetch(`https://serviceai.satabot.com/sessions?userId=${sendParam.userId}`, { @@ -3128,8 +3239,10 @@ const getAiResponse = async (img) => { } } document.getElementById("stopBtn").addEventListener("click", () => { - + document.querySelectorAll('.thinking-loading').forEach(el=>el.classList.add('no-after')); console.log(controller, '暂停消息'); + debugger; + isStopped = true; if (reader) { @@ -3141,6 +3254,7 @@ const getAiResponse = async (img) => { controller.abort(); controller = null; } + if (sendBtnImg.classList.contains('none')) { sendBtnImg.classList.remove('none'); @@ -3149,8 +3263,17 @@ const getAiResponse = async (img) => { }) } + + fetch("https://serviceai.satabot.com/stop", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(sendParam), + + }); + }); document.getElementById("stopBtn1").addEventListener("click", () => { + document.querySelectorAll('.thinking-loading').forEach(el=>el.classList.add('no-after')); console.log(controller, '暂停消息'); isStopped = true; @@ -3172,6 +3295,12 @@ const getAiResponse = async (img) => { }) } + fetch("https://serviceai.satabot.com/stop", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(sendParam), + + }); }); // DOM 加载完成后执行 console.log('DOM Ready'); @@ -3238,7 +3367,7 @@ const getAiResponse = async (img) => { showToast("请选择场景"); return; } - console.log(data, "收集信息") + // 发送POST请求到后端 const res = await fetch('https://serviceai.satabot.com/submit', { diff --git a/server2.js b/server2.js index 8ab7e88..395a482 100755 --- a/server2.js +++ b/server2.js @@ -892,7 +892,7 @@ app.get("/history", async (req, res) => { // 查询数据库 const [rows] = await pool.query( - "SELECT user_id, role, content, image_base64, created_at,title FROM ai_chat_messages WHERE session_id = ? ORDER BY id ASC", + "SELECT user_id, role, content, image_base64, created_at,title,thinking FROM ai_chat_messages WHERE session_id = ? ORDER BY id ASC", [sessionId] ); @@ -1391,6 +1391,8 @@ app.post("/analyze", upload.single("file"), async (req, res) => { return res.status(400).json({ error: "对话次数已用完,请完成账号注册" }); } } + + console.log("====== 开始图像分析11... ======"); const { requestId } = req.body; @@ -1401,6 +1403,7 @@ app.post("/analyze", upload.single("file"), async (req, res) => { "X-Accel-Buffering": "no", }); res.flushHeaders?.(); + res.socket.setNoDelay(true); // 禁用缓冲,实时推送 let aborted = false; req.on("close", () => { console.log("🔴 前端断开连接,停止整个分析流程"); @@ -1442,6 +1445,8 @@ app.post("/analyze", upload.single("file"), async (req, res) => { "SELECT role, content, image_base64 FROM ai_chat_messages WHERE session_id = ? AND user_id = ? ORDER BY created_at ASC", [sessId, userId || null] ); + + console.log("====== 开始图像分析22... ======"); // 构建历史消息数组 const historyMessages = historyRows.slice(-MAX_HISTORY).map(row => { @@ -1469,6 +1474,8 @@ app.post("/analyze", upload.single("file"), async (req, res) => { return message; }); + + console.log("====== 开始图像分析33... ======"); // ------------------ 保存用户提问 ------------------ await pool.query( @@ -1478,6 +1485,8 @@ app.post("/analyze", upload.single("file"), async (req, res) => { // ------------------ 图像分析 ------------------ let sceneText = ""; + + console.log("====== 开始图像分析55... ======"); // If an image is uploaded, start with image analysis if (imageBase64) { @@ -1530,9 +1539,11 @@ app.post("/analyze", upload.single("file"), async (req, res) => { ].filter(Boolean), }, ]; + + console.log("====== 开始图像分析666... ======"); const completion = await client.chat.completions.create({ - model: "gpt-4.1-mini", + model: "gpt-4.1", stream: true, messages: imageAnalysisMessages }); @@ -1687,10 +1698,12 @@ app.post("/analyze", upload.single("file"), async (req, res) => { console.log("====== 最终分析结束 ======"); // ------------------ Save Results to Database ------------------ + + sceneText += thinkingText; if (existingTitle) { await pool.query( - "INSERT INTO ai_chat_messages (session_id, user_id, role, content) VALUES (?, ?, 'assistant', ?)", - [sessId, userId || null, llmText] + "INSERT INTO ai_chat_messages (session_id, user_id, role, content,thinking) VALUES (?, ?, 'assistant', ?,?)", + [sessId, userId || null, llmText,sceneText] ); } else { try { @@ -1709,9 +1722,10 @@ app.post("/analyze", upload.single("file"), async (req, res) => { ); console.log("生成的标题:", title); + await pool.query( - "INSERT INTO ai_chat_messages (session_id, user_id, role, content, title) VALUES (?, ?, 'assistant', ?, ?)", - [sessId, userId || null, llmText, title] + "INSERT INTO ai_chat_messages (session_id, user_id, role, content, title,thinking) VALUES (?, ?, 'assistant', ?, ?,?)", + [sessId, userId || null, llmText, title,sceneText] ); } catch (err) { console.error("生成标题失败:", err.message);