From 136240e5e1aa73a2099960b8710cd71797a16fab Mon Sep 17 00:00:00 2001 From: lkddi Date: Sat, 25 Apr 2026 13:58:40 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=81=E7=A7=BBAI=E5=B0=8F=E7=8F=AD=E9=95=BF?= =?UTF-8?q?=E8=81=8A=E5=A4=A9=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/js/chat-room.js | 8 ++ resources/js/chat-room/chat-bot.js | 134 ++++++++++++++++++ .../views/chat/partials/ai-chatbot.blade.php | 91 +----------- .../views/chat/partials/scripts.blade.php | 2 + 4 files changed, 149 insertions(+), 86 deletions(-) create mode 100644 resources/js/chat-room/chat-bot.js diff --git a/resources/js/chat-room.js b/resources/js/chat-room.js index a94ffa3..e14d5d0 100644 --- a/resources/js/chat-room.js +++ b/resources/js/chat-room.js @@ -4,6 +4,7 @@ export { escapeHtml, escapeHtmlWithLineBreaks } from "./chat-room/html.js"; export { bindAppointmentAnnouncementControls, showAppointmentBanner } from "./chat-room/appointment-announcement.js"; export { bindChatBanner } from "./chat-room/banner.js"; +export { bindChatBotControls, clearChatBotContext, sendToChatBot } from "./chat-room/chat-bot.js"; export { bindGlobalDialogControls } from "./chat-room/dialog.js"; export { bindDailySignInControls } from "./chat-room/daily-sign-in.js"; export { applyFontSize, bindChatFontSizeControl, CHAT_FONT_SIZE_STORAGE_KEY, restoreChatFontSize } from "./chat-room/font-size.js"; @@ -83,6 +84,7 @@ export { createMessageQueue } from "./chat-room/message-queue.js"; import { escapeHtml, escapeHtmlWithLineBreaks } from "./chat-room/html.js"; import { bindAppointmentAnnouncementControls, showAppointmentBanner } from "./chat-room/appointment-announcement.js"; import { bindChatBanner } from "./chat-room/banner.js"; +import { bindChatBotControls, clearChatBotContext, sendToChatBot } from "./chat-room/chat-bot.js"; import { bindGlobalDialogControls } from "./chat-room/dialog.js"; import { bindDailySignInControls } from "./chat-room/daily-sign-in.js"; import { applyFontSize, bindChatFontSizeControl, CHAT_FONT_SIZE_STORAGE_KEY, restoreChatFontSize } from "./chat-room/font-size.js"; @@ -167,6 +169,9 @@ if (typeof window !== "undefined") { bindAppointmentAnnouncementControls, showAppointmentBanner, bindChatBanner, + bindChatBotControls, + clearChatBotContext, + sendToChatBot, bindGlobalDialogControls, bindDailySignInControls, applyFontSize, @@ -265,6 +270,8 @@ if (typeof window !== "undefined") { window.scheduleRenderMobileUserList = scheduleRenderMobileUserList; window.switchMobileTab = switchMobileTab; window.switchTarget = switchTarget; + window.clearChatBotContext = clearChatBotContext; + window.sendToChatBot = sendToChatBot; window.runFeatureShortcut = runFeatureShortcut; window.runToolbarAction = runToolbarAction; window.openHolidayRunFromSystemMessage = openHolidayRunFromSystemMessage; @@ -284,6 +291,7 @@ if (typeof window !== "undefined") { // 页面加载后立即注册事件委托,具体业务逻辑仍由各子模块负责。 bindChatBanner(); + bindChatBotControls(); bindAppointmentAnnouncementControls(); bindGlobalDialogControls(); bindDailySignInControls(); diff --git a/resources/js/chat-room/chat-bot.js b/resources/js/chat-room/chat-bot.js new file mode 100644 index 0000000..94eedf1 --- /dev/null +++ b/resources/js/chat-room/chat-bot.js @@ -0,0 +1,134 @@ +// 聊天室 AI 小班长交互模块,提供 sendToChatBot/clearChatBotContext 兼容入口。 + +import { escapeHtml } from "./html.js"; + +let chatBotSending = false; + +/** + * 读取页面 CSRF Token。 + * + * @returns {string} + */ +function getCsrfToken() { + return document.querySelector('meta[name="csrf-token"]')?.getAttribute("content") || ""; +} + +/** + * 判断包厢窗口是否需要自动滚动到底部。 + * + * @returns {boolean} + */ +function shouldAutoScroll() { + return window.isChatAutoScrollEnabled?.() ?? true; +} + +/** + * 将系统提示追加到包厢窗口。 + * + * @param {string} html + * @returns {void} + */ +function appendPrivateSystemLine(html) { + const messageBox = document.getElementById("chat-messages-container2"); + if (!messageBox) { + return; + } + + const line = document.createElement("div"); + line.className = "msg-line"; + line.innerHTML = html; + messageBox.appendChild(line); + + if (shouldAutoScroll()) { + messageBox.scrollTop = messageBox.scrollHeight; + } +} + +/** + * 将 AI 错误提示追加到包厢窗口。 + * + * @param {string} text + * @returns {void} + */ +function appendBotError(text) { + appendPrivateSystemLine(`🤖【AI小班长】${escapeHtml(text)}`); +} + +/** + * 发送消息给 AI 机器人。 + * + * @param {string} content + * @param {boolean} [isSecret] + * @returns {Promise} + */ +export async function sendToChatBot(content, isSecret = false) { + if (chatBotSending) { + window.chatDialog?.alert?.("AI 正在思考中,请稍候...", "提示", "#336699"); + return; + } + + chatBotSending = true; + + try { + const response = await fetch(window.chatContext.chatBotUrl, { + method: "POST", + headers: { + "X-CSRF-TOKEN": getCsrfToken(), + "Content-Type": "application/json", + "Accept": "application/json", + }, + body: JSON.stringify({ + message: content, + room_id: window.chatContext.roomId, + is_secret: isSecret ? 1 : 0, + }), + }); + + const data = await response.json(); + + if (!response.ok || data.status !== "success") { + // AI 接口错误只写入包厢窗口,避免失败提示刷到公屏。 + appendBotError(data.message || "回复失败,请稍后重试"); + } + } catch (error) { + appendBotError("网络连接错误,请稍后重试"); + } finally { + chatBotSending = false; + } +} + +/** + * 清除与 AI 小助手的对话上下文。 + * + * @returns {Promise} + */ +export async function clearChatBotContext() { + try { + const response = await fetch(window.chatContext.chatBotClearUrl, { + method: "POST", + headers: { + "X-CSRF-TOKEN": getCsrfToken(), + "Accept": "application/json", + }, + }); + const data = await response.json(); + + appendPrivateSystemLine(`🤖【系统】${escapeHtml(data.message || "对话已重置")}`); + } catch (error) { + window.chatDialog?.alert?.(`清除失败:${error.message}`, "操作失败", "#cc4444"); + } +} + +/** + * 暴露 AI 小班长操作给存量发送消息逻辑。 + * + * @returns {void} + */ +export function bindChatBotControls() { + if (typeof window === "undefined") { + return; + } + + window.sendToChatBot = sendToChatBot; + window.clearChatBotContext = clearChatBotContext; +} diff --git a/resources/views/chat/partials/ai-chatbot.blade.php b/resources/views/chat/partials/ai-chatbot.blade.php index 7955e0d..6816900 100644 --- a/resources/views/chat/partials/ai-chatbot.blade.php +++ b/resources/views/chat/partials/ai-chatbot.blade.php @@ -1,87 +1,6 @@ - + sendToChatBot 与 clearChatBotContext 已迁移到 resources/js/chat-room/chat-bot.js, + 由 resources/js/chat-room.js 统一通过 Vite 加载并暴露兼容全局函数。 +--}} diff --git a/resources/views/chat/partials/scripts.blade.php b/resources/views/chat/partials/scripts.blade.php index 867689b..ecfd02c 100644 --- a/resources/views/chat/partials/scripts.blade.php +++ b/resources/views/chat/partials/scripts.blade.php @@ -47,6 +47,8 @@ // 暂时暴露给已迁移的手机抽屉 Vite 模块读取,后续在线名单整体迁移后可移除。 window.onlineUsers = onlineUsers; let autoScroll = true; + // 给已迁移到 Vite 的模块只读判断自动滚动状态,避免直接依赖 Blade 脚本作用域。 + window.isChatAutoScrollEnabled = () => autoScroll; let userBadgeRotationTick = 0; let userListRenderTimer = null; let _maxMsgId = 0; // 记录当前收到的最大消息 ID