Files
chatroom/resources/views/chat/partials/chat-banner.blade.php
lkddi bfb1a3bca4 重构(chat): 聊天室 Partials 第二阶段分类拆分及修复红包弹窗隐藏 Bug
- 完成对 scripts.blade.php 中非核心业务逻辑(钓鱼游戏、AI机器人、系统全局公告)的深度抽象隔离
- 修复抢红包逻辑中 setInterval 缺失时间参数(1000)引发浏览器前端主线程挂起的重度阻塞问题
- 修复 lottery-panel 组件结尾漏写 </div> 导致的连锁级渲染树崩溃(该崩溃导致红包节点被意外当作隐藏后代节点渲染,造成彻底不可见)
- 对相关模板规范代码结构,执行 Laravel Pint 格式化并提交
2026-03-09 11:30:11 +08:00

179 lines
7.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{--
文件功能全局大卡片通知组件chatBanner
提供全局 JS API
window.chatBanner.show(opts) 显示居中大卡片
window.chatBanner.close(id?) 关闭指定卡片
opts 参数:
id: string (可选) ID 会替换旧弹窗,防止重叠
icon: string Emoji 图标(如 '🎉💚🎉'
title: string 小标题(如 '好友通知'
name: string 大名字行(可留空)
body: string 主内容(支持 HTML
sub: string 副内容(小字)
gradient: string[] 渐变颜色数组3个颜色
titleColor: string 小标题颜色
autoClose: number 自动关闭 ms0=不关闭,默认 5000
buttons: Array<{
label: string
color: string
onClick(btn, close): Function
}>
scripts.blade.php 拆分,确保页面全局组件独立维护。
@author ChatRoom Laravel
@version 1.0.0
--}}
<script>
/**
* 全局大卡片通知公共组件。
* 对标 window.chatDialog 和 window.chatToast 的设计风格,
* 用于展示任命公告、好友通知、红包选择等需要居中展示的大卡片。
*/
window.chatBanner = (function() {
/** 注入入场/退场动画(全局只注入一次) */
function ensureKeyframes() {
if (document.getElementById('appoint-keyframes')) {
return;
}
const style = document.createElement('style');
style.id = 'appoint-keyframes';
style.textContent = `
@keyframes appoint-pop {
0% { opacity: 0; transform: translate(-50%,-50%) scale(0.5); }
70% { transform: translate(-50%,-50%) scale(1.05); }
100% { opacity: 1; transform: translate(-50%,-50%) scale(1); }
}
@keyframes appoint-fade-out {
from { opacity: 1; }
to { opacity: 0; transform: translate(-50%,-50%) scale(0.9); }
}
`;
document.head.appendChild(style);
}
/**
* 显示大卡片通知。
*
* @param {Object} opts 选项(见上方注释)
*/
function show(opts = {}) {
ensureKeyframes();
// 大卡片弹出时播放叮咚通知音
if (window.chatSound) {
window.chatSound.ding();
}
const id = opts.id || 'chat-banner-default';
const gradient = (opts.gradient || ['#4f46e5', '#7c3aed', '#db2777']).join(', ');
const titleColor = opts.titleColor || '#fde68a';
const autoClose = opts.autoClose ?? 5000;
// 移除同 ID 的旧弹窗,避免重叠
const old = document.getElementById(id);
if (old) {
old.remove();
}
// 构建按钮 HTML
const hasButtons = opts.buttons && opts.buttons.length > 0;
let buttonsHtml = '';
if (hasButtons) {
buttonsHtml = '<div style="margin-top:18px; display:flex; gap:10px; justify-content:center;">';
opts.buttons.forEach((btn, idx) => {
buttonsHtml += `<button data-banner-btn="${idx}"
style="background:${btn.color || '#10b981'}; color:#fff; border:none; border-radius:8px;
padding:8px 20px; font-size:13px; font-weight:bold; cursor:pointer;
box-shadow:0 4px 12px rgba(0,0,0,0.25);">
${btn.label || '确定'}
</button>`;
});
buttonsHtml += '</div>';
}
const banner = document.createElement('div');
banner.id = id;
banner.style.cssText = `
position: fixed; top: 50%; left: 50%; transform: translate(-50%,-50%);
z-index: 99999; text-align: center;
animation: appoint-pop 0.5s cubic-bezier(0.175,0.885,0.32,1.275);
${hasButtons ? 'pointer-events: auto;' : 'pointer-events: none;'}
`;
banner.innerHTML = `
<div style="background: linear-gradient(135deg, ${gradient});
border-radius: 20px; padding: 28px 44px;
box-shadow: 0 20px 60px rgba(0,0,0,0.4);
border: 2px solid rgba(255,255,255,0.25); backdrop-filter: blur(8px);
min-width: 260px;">
${opts.icon ? `<div style="font-size:40px; margin-bottom:8px;">${opts.icon}</div>` : ''}
${opts.title ? `<div style="color:${titleColor}; font-size:13px; font-weight:bold; letter-spacing:3px; margin-bottom:12px;">
══ ${opts.title} ══
</div>` : ''}
${opts.name ? `<div style="color:white; font-size:22px; font-weight:900; text-shadow:0 2px 8px rgba(0,0,0,0.3);">
${escapeHtml(opts.name)}
</div>` : ''}
${opts.body ? `<div style="color:rgba(255,255,255,0.9); font-size:14px; margin-top:10px;">${opts.body}</div>` : ''}
${opts.sub ? `<div style="color:rgba(255,255,255,0.6); font-size:12px; margin-top:6px;">${opts.sub}</div>` : ''}
${buttonsHtml}
<div style="color:rgba(255,255,255,0.35); font-size:11px; margin-top:14px;">
${new Date().toLocaleTimeString('zh-CN')}
</div>
</div>
`;
document.body.appendChild(banner);
// 绑定按钮点击事件
if (hasButtons) {
const closeFn = () => {
banner.style.animation = 'appoint-fade-out 0.5s ease forwards';
setTimeout(() => banner.remove(), 500);
};
opts.buttons.forEach((btn, idx) => {
const el = banner.querySelector(`[data-banner-btn="${idx}"]`);
if (el && btn.onClick) {
el.addEventListener('click', () => btn.onClick(el, closeFn));
} else if (el) {
el.addEventListener('click', closeFn);
}
});
}
// 自动关闭
if (autoClose > 0) {
setTimeout(() => {
if (!document.getElementById(id)) {
return;
}
banner.style.animation = 'appoint-fade-out 0.5s ease forwards';
setTimeout(() => banner.remove(), 500);
}, autoClose);
}
}
/**
* 关闭指定 ID 的 banner。
*
* @param {string} id banner 的 DOM ID
*/
function close(id) {
const el = document.getElementById(id || 'chat-banner-default');
if (!el) {
return;
}
el.style.animation = 'appoint-fade-out 0.5s ease forwards';
setTimeout(() => el.remove(), 500);
}
return {
show,
close
};
})();
</script>