功能:好友添加通知改为居中大卡弹窗(同任命公告风格)

FriendAdded:
- 互相好友 → 绿色渐变大卡 + '你们现在互为好友 🎊',5秒自动消失
- 单向添加 → 蓝绿渐变大卡 + [ 回加好友] + [稍后再说] 按钮,手动关闭

FriendRemoved:保留右下角 Toast 通知

效果复用 appoint-pop 弹出动画关键帧
This commit is contained in:
2026-03-01 01:09:37 +08:00
parent d60a225368
commit 779179af01
+127 -22
View File
@@ -653,7 +653,9 @@
// ── 好友系统私有频道监听(仅本人可见) ────────────────
/**
* 监听当前用户的私有频道 `user.{username}`
* 收到 FriendAdded / FriendRemoved 事件时用任务弹窗通知。
* 收到 FriendAdded / FriendRemoved 事件时用弹窗通知。
* FriendAdded 居中大卡弹窗(同任命公告风格)
* FriendRemoved 右下角 Toast 通知
*/
function setupFriendNotification() {
if (!window.Echo || !window.chatContext) {
@@ -663,30 +665,14 @@
const myName = window.chatContext.username;
window.Echo.private(`user.${myName}`)
.listen('.FriendAdded', (e) => {
if (e.has_added_back) {
// 我已经把对方加了,现在对方也加了我 → 双向好友
showFriendToast(
`💚 <b>${e.from_username}</b> 将你加为好友了!<br>你们现在互为好友 🎉`,
'#16a34a'
);
} else {
// 对方加了我,但我还没加对方 → 提示可以回加
showFriendToast(
`💚 <b>${e.from_username}</b> 将你加为好友了!<br>
<span style="color:#6b7280; font-size:12px;">但你还没有添加对方为好友。</span>`,
'#16a34a', {
label: ` 回加 ${e.from_username}`,
username: e.from_username,
action: 'add'
}
);
}
// 用居中大卡弹窗通知(有无互相好友显示不同文案和按钮)
showFriendBanner(e.from_username, e.has_added_back);
})
.listen('.FriendRemoved', (e) => {
if (e.had_added_back) {
// 之前是互相好友,现在对方删除了我 → 提示可以同步删除
showFriendToast(
`💔 <b>${e.from_username}</b> 已将你从好友列表移除。<br>
` <b>${e.from_username}</b> 已将你从好友列表移除。<br>
<span style="color:#6b7280; font-size:12px;">你的好友列表中仍保留对方,可点击同步移除。</span>`,
'#6b7280', {
label: `🗑️ 同步移除 ${e.from_username}`,
@@ -695,9 +681,8 @@
}
);
} else {
// 对方删我,但原来就是单向的
showFriendToast(
`💔 <b>${e.from_username}</b> 已将你从他的好友列表移除。`,
` <b>${e.from_username}</b> 已将你从他的好友列表移除。`,
'#9ca3af'
);
}
@@ -705,6 +690,126 @@
}
document.addEventListener('DOMContentLoaded', setupFriendNotification);
/**
* 显示好友添加居中大卡弹窗(同任命公告风格)。
* 互相好友 绿色渐变 + 互为好友文案
* 单向添加 蓝绿渐变 + 提示回加 + [ 回加好友] 按钮
*
* @param {string} fromUsername 添加者用户名
* @param {boolean} hasAddedBack 接收方是否已将添加者加为好友
*/
function showFriendBanner(fromUsername, hasAddedBack) {
// 移除已有的好友弹窗(防止重叠)
const old = document.getElementById('friend-banner');
if (old) old.remove();
const banner = document.createElement('div');
banner.id = 'friend-banner';
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);
`;
if (hasAddedBack) {
// 已互相好友 → 绿色渐变卡片
banner.innerHTML = `
<div style="background: linear-gradient(135deg, #065f46, #059669, #10b981);
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);">
<div style="font-size: 40px; margin-bottom: 8px;">🎉💚🎉</div>
<div style="color: #a7f3d0; font-size: 13px; font-weight: bold; letter-spacing: 3px; margin-bottom: 12px;">
══ 好友通知 ══
</div>
<div style="color: white; font-size: 22px; font-weight: 900; text-shadow: 0 2px 8px rgba(0,0,0,0.3);">
${escapeHtml(fromUsername)}
</div>
<div style="color: #d1fae5; font-size: 14px; margin-top: 10px;">
将你加为好友!<strong style="color: #a7f3d0;">你们现在互为好友 🎊</strong>
</div>
<div style="color: rgba(255,255,255,0.45); font-size: 11px; margin-top: 10px;">
${new Date().toLocaleTimeString('zh-CN')}
</div>
</div>
`;
} else {
// 单向添加 → 蓝绿渐变 + 回加按钮(可点击)
const btnId = 'friend-banner-btn-' + Date.now();
banner.style.pointerEvents = 'auto'; // 允许点击按钮
banner.innerHTML = `
<div style="background: linear-gradient(135deg, #1e3a5f, #1d4ed8, #0891b2);
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);">
<div style="font-size: 40px; margin-bottom: 8px;">💚📩</div>
<div style="color: #bae6fd; font-size: 13px; font-weight: bold; letter-spacing: 3px; margin-bottom: 12px;">
══ 好友申请 ══
</div>
<div style="color: white; font-size: 22px; font-weight: 900; text-shadow: 0 2px 8px rgba(0,0,0,0.3);">
${escapeHtml(fromUsername)}
</div>
<div style="color: #bae6fd; font-size: 14px; margin-top: 10px;">
将你加为好友!
</div>
<div style="color: rgba(255,255,255,0.6); font-size: 12px; margin-top: 6px;">
但你还没有回加对方为好友
</div>
<div style="margin-top: 16px;">
<button id="${btnId}"
style="background: #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);">
回加好友
</button>
<button onclick="document.getElementById('friend-banner').remove()"
style="background: rgba(255,255,255,0.15); color: #fff; border: none; border-radius: 8px;
padding: 8px 16px; font-size: 13px; cursor: pointer; margin-left: 10px;">
稍后再说
</button>
</div>
<div style="color: rgba(255,255,255,0.35); font-size: 11px; margin-top: 12px;">
${new Date().toLocaleTimeString('zh-CN')}
</div>
</div>
`;
// 等 DOM 插入后再绑定按钮事件
setTimeout(() => {
const btn = document.getElementById(btnId);
if (btn) {
btn.addEventListener('click', () => quickFriendAction('add', fromUsername, btn));
}
}, 50);
}
document.body.appendChild(banner);
// 确保动画关键帧已注入
if (!document.getElementById('appoint-keyframes')) {
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);
}
// 非单向(互相好友):5 秒后自动淡出;单向:需手动关闭(有按钮)
if (hasAddedBack) {
setTimeout(() => {
banner.style.animation = 'appoint-fade-out 0.5s ease forwards';
setTimeout(() => banner.remove(), 500);
}, 5000);
}
}
/**
* 显示好友事件通知浮窗(右下角淡入淡出)。
*