From 1e9f6673cce0e56bd7d337c7af3a82ee842baeb7 Mon Sep 17 00:00:00 2001 From: lkddi Date: Sat, 25 Apr 2026 13:55:47 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=81=E7=A7=BB=E4=BB=BB=E5=91=BD=E5=85=AC?= =?UTF-8?q?=E5=91=8A=E4=BA=8B=E4=BB=B6=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 | 5 + .../js/chat-room/appointment-announcement.js | 181 ++++++++++++++++++ .../chat/partials/system-events.blade.php | 132 +------------ 3 files changed, 191 insertions(+), 127 deletions(-) create mode 100644 resources/js/chat-room/appointment-announcement.js diff --git a/resources/js/chat-room.js b/resources/js/chat-room.js index d195cc5..7526304 100644 --- a/resources/js/chat-room.js +++ b/resources/js/chat-room.js @@ -2,6 +2,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 { bindGlobalDialogControls } from "./chat-room/dialog.js"; export { bindDailySignInControls } from "./chat-room/daily-sign-in.js"; @@ -79,6 +80,7 @@ export { 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 { bindGlobalDialogControls } from "./chat-room/dialog.js"; import { bindDailySignInControls } from "./chat-room/daily-sign-in.js"; @@ -160,6 +162,8 @@ if (typeof window !== "undefined") { window.ChatRoomTools = { escapeHtml, escapeHtmlWithLineBreaks, + bindAppointmentAnnouncementControls, + showAppointmentBanner, bindChatBanner, bindGlobalDialogControls, bindDailySignInControls, @@ -273,6 +277,7 @@ if (typeof window !== "undefined") { // 页面加载后立即注册事件委托,具体业务逻辑仍由各子模块负责。 bindChatBanner(); + bindAppointmentAnnouncementControls(); bindGlobalDialogControls(); bindDailySignInControls(); bindChatFontSizeControl(); diff --git a/resources/js/chat-room/appointment-announcement.js b/resources/js/chat-room/appointment-announcement.js new file mode 100644 index 0000000..dbd4c7c --- /dev/null +++ b/resources/js/chat-room/appointment-announcement.js @@ -0,0 +1,181 @@ +// 聊天室任命/撤销公告监听,负责渲染大卡片和频道内系统提示。 + +import { escapeHtml } from "./html.js"; + +const APPOINTMENT_PHRASES = [ + "望再接再厉,大展宏图,为大家服务!", + "期待在任期间带领大家更上一层楼!", + "众望所归,任重道远,加油!", + "新官上任,一展风采,前程似锦!", + "相信你能胜任,期待你的精彩表现!", +]; + +const REVOKE_PHRASES = [ + "感谢在任期间的辛勤付出,辛苦了!", + "江湖路长,愿前程似锦,未来可期!", + "感谢您为大家的奉献,一路顺风!", + "在任一场,情谊长存,感谢付出!", + "相信以后还有更多精彩,继续加油!", +]; + +/** + * 获取当前聊天消息时间。 + * + * @returns {string} + */ +function getChatTimeString() { + const now = new Date(); + + return [ + now.getHours().toString().padStart(2, "0"), + now.getMinutes().toString().padStart(2, "0"), + now.getSeconds().toString().padStart(2, "0"), + ].join(":"); +} + +/** + * 从候选文案中随机挑选一条公告补充语。 + * + * @param {string[]} phrases + * @returns {string} + */ +function pickAnnouncementPhrase(phrases) { + return phrases[Math.floor(Math.random() * phrases.length)]; +} + +/** + * 显示任命或撤销的大卡片公告。 + * + * @param {object} data + * @returns {void} + */ +export function showAppointmentBanner(data) { + if (!window.chatBanner) { + return; + } + + const department = data.department_name ? `${escapeHtml(data.department_name)} · ` : ""; + const isRevoke = data.type === "revoke"; + + if (isRevoke) { + window.chatBanner.show({ + id: "appointment-banner", + icon: "📋", + title: "职务撤销", + name: `${escapeHtml(data.position_icon)} ${escapeHtml(data.target_username)}`, + body: `${department}${escapeHtml(data.position_name)} 职务已被撤销`, + sub: `由 ${escapeHtml(data.operator_name)} 执行`, + gradient: ["#374151", "#4b5563", "#6b7280"], + titleColor: "#d1d5db", + autoClose: 4500, + }); + return; + } + + window.chatBanner.show({ + id: "appointment-banner", + icon: "🎊🎖️🎊", + title: "任命公告", + name: `${escapeHtml(data.position_icon)} ${escapeHtml(data.target_username)}`, + body: `荣任 ${department}${escapeHtml(data.position_name)}`, + sub: `由 ${escapeHtml(data.operator_name)} 任命`, + gradient: ["#4f46e5", "#7c3aed", "#db2777"], + titleColor: "#fde68a", + autoClose: 4500, + }); +} + +/** + * 创建任命/撤销频道内系统消息节点。 + * + * @param {object} data + * @param {boolean} isRevoke + * @param {string} department + * @param {string} phrase + * @param {string} timeString + * @returns {HTMLDivElement} + */ +function createAppointmentMessage(data, isRevoke, department, phrase, timeString) { + const message = document.createElement("div"); + message.className = "msg-line"; + + if (isRevoke) { + message.style.cssText = "background:#f3f4f6; border-left:3px solid #9ca3af; border-radius:4px; padding:4px 10px; margin:2px 0;"; + message.innerHTML = ` + 📋 + ${escapeHtml(data.target_username)} 的 ${escapeHtml(data.position_icon)} ${department}${escapeHtml(data.position_name)} 职务已被 ${escapeHtml(data.operator_name)} 撤销。${phrase} + (${timeString}) + `; + return message; + } + + message.style.cssText = "background:#f5f3ff; border-left:3px solid #7c3aed; border-radius:4px; padding:4px 10px; margin:2px 0;"; + message.innerHTML = ` + 🎖️ + 恭喜 ${escapeHtml(data.target_username)} 荣任 ${escapeHtml(data.position_icon)} ${department}${escapeHtml(data.position_name)},由 ${escapeHtml(data.operator_name)} 任命。${phrase} + (${timeString}) + `; + + return message; +} + +/** + * 将系统消息追加到指定聊天窗格并滚动到底部。 + * + * @param {HTMLElement|null} container + * @param {HTMLDivElement} message + * @returns {void} + */ +function appendAppointmentMessage(container, message) { + if (!container) { + return; + } + + container.appendChild(message); + container.scrollTop = container.scrollHeight; +} + +/** + * 处理任命/撤销公告事件。 + * + * @param {CustomEvent} event + * @returns {void} + */ +function handleAppointmentAnnouncement(event) { + const data = event.detail || {}; + const isRevoke = data.type === "revoke"; + const department = data.department_name ? `${escapeHtml(data.department_name)} · ` : ""; + + // 任命公告才播放礼花,撤销只显示灰色公告卡片。 + if (!isRevoke) { + window.EffectManager?.play?.("fireworks"); + } + + showAppointmentBanner(data); + + const phrase = pickAnnouncementPhrase(isRevoke ? REVOKE_PHRASES : APPOINTMENT_PHRASES); + const myName = window.chatContext?.username ?? ""; + const isInvolved = myName === data.operator_name || myName === data.target_username; + const targetContainer = isInvolved + ? document.getElementById("chat-messages-container2") + : document.getElementById("chat-messages-container"); + + // 操作者和被操作者写入私聊面板,其余用户写入公屏。 + appendAppointmentMessage( + targetContainer, + createAppointmentMessage(data, isRevoke, department, phrase, getChatTimeString()), + ); +} + +/** + * 绑定任命/撤销公告浏览器事件。 + * + * @returns {void} + */ +export function bindAppointmentAnnouncementControls() { + if (typeof window === "undefined") { + return; + } + + window.addEventListener("chat:appointment-announced", handleAppointmentAnnouncement); +} diff --git a/resources/views/chat/partials/system-events.blade.php b/resources/views/chat/partials/system-events.blade.php index 77f8a82..e19f23c 100644 --- a/resources/views/chat/partials/system-events.blade.php +++ b/resources/views/chat/partials/system-events.blade.php @@ -1,128 +1,6 @@ - + 任命/撤销公告监听已迁移到 resources/js/chat-room/appointment-announcement.js, + 由 resources/js/chat-room.js 统一通过 Vite 加载并绑定 chat:appointment-announced 事件。 +--}}