163 lines
4.9 KiB
JavaScript
163 lines
4.9 KiB
JavaScript
// 聊天室右下角 Toast 通知组件,提供 window.chatToast.show 兼容入口。
|
||
|
||
/**
|
||
* 淡出并移除 Toast 卡片。
|
||
*
|
||
* @param {HTMLElement} card
|
||
* @returns {void}
|
||
*/
|
||
function dismissToast(card) {
|
||
if (card.dataset.toastClosing === "1") {
|
||
return;
|
||
}
|
||
|
||
card.dataset.toastClosing = "1";
|
||
card.style.opacity = "0";
|
||
window.setTimeout(() => card.remove(), 400);
|
||
}
|
||
|
||
/**
|
||
* 关闭容器内所有 Toast 卡片。
|
||
*
|
||
* @param {HTMLElement} container
|
||
* @returns {void}
|
||
*/
|
||
function dismissAllToasts(container) {
|
||
container.querySelectorAll(".chat-toast-card").forEach((card) => {
|
||
if (card instanceof HTMLElement) {
|
||
dismissToast(card);
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 绑定点击 Toast 外部区域时自动关闭。
|
||
*
|
||
* @param {HTMLElement} container
|
||
* @returns {void}
|
||
*/
|
||
function bindOutsideDismiss(container) {
|
||
document.addEventListener("pointerdown", (event) => {
|
||
if (container.childElementCount === 0) {
|
||
return;
|
||
}
|
||
|
||
const target = event.target;
|
||
if (!(target instanceof Element)) {
|
||
return;
|
||
}
|
||
|
||
if (target.closest(".chat-toast-card")) {
|
||
return;
|
||
}
|
||
|
||
dismissAllToasts(container);
|
||
}, true);
|
||
}
|
||
|
||
/**
|
||
* 创建右下角 Toast 通知 API。
|
||
*
|
||
* @param {HTMLElement} container
|
||
* @returns {{show: Function}}
|
||
*/
|
||
function createChatToast(container) {
|
||
return {
|
||
/**
|
||
* 显示一条 Toast 通知卡片。
|
||
*
|
||
* @param {object} options
|
||
* @param {string} options.title 标题文字
|
||
* @param {string} options.message 内容 HTML
|
||
* @param {string} [options.icon] 左侧 Emoji
|
||
* @param {string} [options.color] 强调色
|
||
* @param {number} [options.duration] 自动消失毫秒,0 表示不自动消失
|
||
* @param {object|null} [options.action] 操作按钮 { label, onClick }
|
||
* @returns {void}
|
||
*/
|
||
show({
|
||
title,
|
||
message,
|
||
icon = "💬",
|
||
color = "#336699",
|
||
duration = 6000,
|
||
action = null,
|
||
}) {
|
||
const card = document.createElement("div");
|
||
card.className = "chat-toast-card";
|
||
card.style.cssText = `
|
||
background:#fff; border-radius:10px; overflow:hidden;
|
||
box-shadow:0 8px 32px rgba(0,0,0,.18);
|
||
min-width:260px; max-width:320px;
|
||
font-size:13px; color:#374151; line-height:1.6;
|
||
pointer-events:all;
|
||
animation:toastSlideIn .3s ease;
|
||
opacity:1; transition:opacity .4s;
|
||
`;
|
||
|
||
const actionHtml = action ? `
|
||
<div style="margin-top:8px;">
|
||
<button class="chat-toast-action-btn"
|
||
style="background:${color}; color:#fff; border:none; border-radius:6px;
|
||
padding:5px 14px; font-size:12px; font-weight:bold; cursor:pointer;">
|
||
${action.label}
|
||
</button>
|
||
</div>` : "";
|
||
|
||
card.innerHTML = `
|
||
<div style="background:${color}; padding:8px 14px;
|
||
display:flex; align-items:center; justify-content:space-between;">
|
||
<span style="color:#fff; font-weight:bold; font-size:13px;">
|
||
${icon} ${title}
|
||
</span>
|
||
<button class="chat-toast-close"
|
||
style="background:rgba(255,255,255,.25); border:none; color:#fff;
|
||
width:22px; height:22px; border-radius:50%; cursor:pointer;
|
||
font-size:14px; line-height:22px; text-align:center; padding:0;">×</button>
|
||
</div>
|
||
<div style="padding:12px 14px 10px;">
|
||
<div>${message}</div>
|
||
${actionHtml}
|
||
</div>
|
||
`;
|
||
|
||
card.querySelector(".chat-toast-close")?.addEventListener("click", () => dismissToast(card));
|
||
|
||
if (action) {
|
||
card.querySelector(".chat-toast-action-btn")?.addEventListener("click", () => {
|
||
action.onClick?.();
|
||
dismissToast(card);
|
||
});
|
||
}
|
||
|
||
container.appendChild(card);
|
||
|
||
// Toast 出现时沿用旧体验播放提示音。
|
||
window.chatSound?.ding?.();
|
||
|
||
if (duration > 0) {
|
||
window.setTimeout(() => dismissToast(card), duration);
|
||
}
|
||
},
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 绑定全局 Toast API。
|
||
*
|
||
* @returns {void}
|
||
*/
|
||
export function bindChatToast() {
|
||
if (typeof document === "undefined" || window.chatToast) {
|
||
return;
|
||
}
|
||
|
||
const container = document.getElementById("chat-toast-container");
|
||
if (!container) {
|
||
return;
|
||
}
|
||
|
||
bindOutsideDismiss(container);
|
||
window.chatToast = createChatToast(container);
|
||
}
|