新增 屏蔽消息功能

This commit is contained in:
2026-04-14 22:25:16 +08:00
parent b76b6559ea
commit 0183de66dd
3 changed files with 197 additions and 1 deletions
@@ -10,6 +10,7 @@
6. 钓鱼小游戏(startFishing / reelFish / autoFish
7. 发送消息(sendMessageIME 防重触发)
8. 特效控制(triggerEffect / applyFontSize / toggleSoundMute
9. 系统播报屏蔽(toggleBlockMenu / toggleBlockedSystemSender
已拆分至独立文件:
- window.chatBanner chat-banner.blade.php
@@ -36,6 +37,8 @@
const toUserSelect = document.getElementById('to_user');
const onlineCount = document.getElementById('online-count');
const onlineCountBottom = document.getElementById('online-count-bottom');
const BLOCKED_SYSTEM_SENDERS_STORAGE_KEY = 'chat_blocked_system_senders';
const BLOCKABLE_SYSTEM_SENDERS = ['钓鱼播报', '星海小博士'];
// ── 消息区:手机端双触发打开用户名片(PC 端靠 ondblclick 内联属性)──
// span[data-u] 由 clickableUser() 生成,touchend 委托至容器避免每条消息单独绑定
@@ -67,6 +70,133 @@
let onlineUsers = {};
let autoScroll = true;
let _maxMsgId = 0; // 记录当前收到的最大消息 ID
let blockedSystemSenders = new Set(loadBlockedSystemSenders());
/**
* localStorage 读取已屏蔽的系统播报发送者列表。
*
* @returns {string[]}
*/
function loadBlockedSystemSenders() {
try {
const saved = JSON.parse(localStorage.getItem(BLOCKED_SYSTEM_SENDERS_STORAGE_KEY) || '[]');
if (!Array.isArray(saved)) {
return [];
}
// 仅允许白名单内的系统播报项进入配置,避免脏数据污染。
return saved.filter(sender => BLOCKABLE_SYSTEM_SENDERS.includes(sender));
} catch (error) {
return [];
}
}
/**
* 将当前屏蔽配置持久化到 localStorage。
*/
function persistBlockedSystemSenders() {
localStorage.setItem(
BLOCKED_SYSTEM_SENDERS_STORAGE_KEY,
JSON.stringify(Array.from(blockedSystemSenders))
);
}
/**
* 同步屏蔽菜单中的复选框状态。
*/
function syncBlockedSystemSenderCheckboxes() {
const fishingCheckbox = document.getElementById('block-sender-fishing');
const doctorCheckbox = document.getElementById('block-sender-doctor');
if (fishingCheckbox) {
fishingCheckbox.checked = blockedSystemSenders.has('钓鱼播报');
}
if (doctorCheckbox) {
doctorCheckbox.checked = blockedSystemSenders.has('星海小博士');
}
}
/**
* 判断当前消息发送者是否已被用户屏蔽。
*
* @param {string} fromUser 发送者名称
* @returns {boolean}
*/
function isBlockedSystemSender(fromUser) {
return blockedSystemSenders.has(String(fromUser || ''));
}
/**
* 从当前已渲染的聊天窗口中移除指定发送者的所有消息。
*
* @param {string} sender 发送者名称
*/
function removeRenderedMessagesBySender(sender) {
[container, container2].forEach(targetContainer => {
if (!targetContainer) {
return;
}
targetContainer.querySelectorAll('[data-from-user]').forEach(node => {
if (node.dataset.fromUser === sender) {
node.remove();
}
});
});
}
/**
* 切换系统播报屏蔽菜单的显示状态。
*
* @param {Event} event 点击事件
*/
function toggleBlockMenu(event) {
event.stopPropagation();
const menu = document.getElementById('block-menu');
const welcomeMenu = document.getElementById('welcome-menu');
const adminMenu = document.getElementById('admin-menu');
if (!menu) {
return;
}
if (welcomeMenu) {
welcomeMenu.style.display = 'none';
}
if (adminMenu) {
adminMenu.style.display = 'none';
}
syncBlockedSystemSenderCheckboxes();
menu.style.display = menu.style.display === 'none' ? 'block' : 'none';
}
/**
* 更新指定系统播报项的屏蔽状态,并在勾选后立即清理当前窗口。
*
* @param {string} sender 系统播报发送者
* @param {boolean} blocked 是否屏蔽
*/
function toggleBlockedSystemSender(sender, blocked) {
if (!BLOCKABLE_SYSTEM_SENDERS.includes(sender)) {
return;
}
if (blocked) {
blockedSystemSenders.add(sender);
// 勾选后立刻移除聊天室窗口内已显示的对应播报内容。
removeRenderedMessagesBySender(sender);
} else {
blockedSystemSenders.delete(sender);
}
persistBlockedSystemSenders();
syncBlockedSystemSenderCheckboxes();
}
syncBlockedSystemSenderCheckboxes();
/**
* 转义会员横幅文本,避免横幅层被注入 HTML。
@@ -319,12 +449,16 @@
event.stopPropagation();
const menu = document.getElementById('welcome-menu');
const adminMenu = document.getElementById('admin-menu');
const blockMenu = document.getElementById('block-menu');
if (!menu) {
return;
}
if (adminMenu) {
adminMenu.style.display = 'none';
}
if (blockMenu) {
blockMenu.style.display = 'none';
}
menu.style.display = menu.style.display === 'none' ? 'block' : 'none';
}
@@ -335,12 +469,16 @@
event.stopPropagation();
const menu = document.getElementById('admin-menu');
const welcomeMenu = document.getElementById('welcome-menu');
const blockMenu = document.getElementById('block-menu');
if (!menu) {
return;
}
if (welcomeMenu) {
welcomeMenu.style.display = 'none';
}
if (blockMenu) {
blockMenu.style.display = 'none';
}
menu.style.display = menu.style.display === 'none' ? 'block' : 'none';
}
@@ -441,6 +579,11 @@
if (adminMenu) {
adminMenu.style.display = 'none';
}
const blockMenu = document.getElementById('block-menu');
if (blockMenu) {
blockMenu.style.display = 'none';
}
});
// ── 动作选择 ──────────────────────────────────────
@@ -656,11 +799,19 @@
_maxMsgId = msg.id;
}
// 用户勾选屏蔽后,历史消息和实时消息统一在这里拦截,不再进入渲染流程。
if (isBlockedSystemSender(msg?.from_user)) {
return;
}
const isMe = msg.from_user === window.chatContext.username;
const fontColor = msg.font_color || '#000000';
const div = document.createElement('div');
div.className = 'msg-line';
if (msg?.from_user) {
div.dataset.fromUser = msg.from_user;
}
const timeStr = msg.sent_at || '';
let timeStrOverride = false;
@@ -1351,9 +1502,11 @@
}).catch(err => console.error('特效触发失败:', err));
}
window.toggleAdminMenu = toggleAdminMenu;
window.toggleBlockMenu = toggleBlockMenu;
window.runAdminAction = runAdminAction;
window.selectEffect = selectEffect;
window.triggerEffect = triggerEffect;
window.toggleBlockedSystemSender = toggleBlockedSystemSender;
// ── 字号设置(持久化到 localStorage)─────────────────
/**
@@ -1391,6 +1544,7 @@
const muted = localStorage.getItem('chat_sound_muted') === '1';
const muteChk = document.getElementById('sound_muted');
if (muteChk) muteChk.checked = muted;
syncBlockedSystemSenderCheckboxes();
});
// ── 特效禁音开关 ─────────────────────────────────────────────────