/** * 频道通知系统 - 环节7 (晨星陪伴版) * 功能:模块更新提醒、未读小红点、通知面板、高亮点击修复 * 修改:通过卡片文字内容匹配模块ID(解决moduleId undefined问题) */ (function() { // ========== 初始化模拟数据 ========== const STORAGE_KEY = 'channel_notifications'; // 默认模拟数据(基于已完成模块 M06, M08, M11, 以及当前频道模块) const DEFAULT_UPDATES = { 'M06': { hasUpdate: true, count: 2, updates: [ { time: '10:30', summary: '修复了拖拽排序的bug' }, { time: '昨天', summary: '新增统计面板' } ] }, 'M08': { hasUpdate: true, count: 1, updates: [ { time: '昨天', summary: '优化了模块加载性能' } ] }, 'M11': { hasUpdate: true, count: 3, updates: [ { time: '15:20', summary: '新增键盘快捷键' }, { time: '昨天', summary: '修复焦点管理' }, { time: '3月10日', summary: '模块生命周期完善' } ] }, 'channel': { hasUpdate: true, count: 1, updates: [ { time: '现在', summary: '通知系统上线啦!' } ] } }; // 加载或初始化数据 let notificationData = JSON.parse(localStorage.getItem(STORAGE_KEY)); if (!notificationData) { notificationData = DEFAULT_UPDATES; localStorage.setItem(STORAGE_KEY, JSON.stringify(notificationData)); } // ========== 工具函数:从卡片文字猜测模块ID ========== function guessModuleIdFromCard(card) { // 尝试找卡片里的标题文字 const titleElem = card.querySelector('h3, .module-title, .card-title, .module-name'); if (titleElem) { const text = titleElem.textContent.trim(); // 简单映射:如果文字包含“工单” -> M06 if (text.includes('工单')) return 'M06'; if (text.includes('数据统计')) return 'M08'; if (text.includes('组件库')) return 'M11'; if (text.includes('调试面板')) return 'M11'; // 暂时用 M11 } // 后备:用卡片内所有文字尝试匹配 const fullText = card.textContent; if (fullText.includes('工单')) return 'M06'; if (fullText.includes('数据统计')) return 'M08'; if (fullText.includes('组件库')) return 'M11'; return null; // 实在猜不到就返回 null } function getModuleCards() { return document.querySelectorAll('.module-card'); } // ========== 更新标签渲染(带日志) ========== function renderUpdateBadges() { console.log('🟦 renderUpdateBadges 开始执行'); const cards = document.querySelectorAll('.module-card'); console.log('找到卡片数量:', cards.length); cards.forEach(card => { // 先尝试原有方法,失败则用猜测 let moduleId = card.dataset.moduleId || card.id || card.querySelector('.module-name')?.textContent.trim(); if (!moduleId) { moduleId = guessModuleIdFromCard(card); } console.log('卡片最终 moduleId:', moduleId); // 移除旧的标签 const oldBadge = card.querySelector('.update-badge'); if (oldBadge) oldBadge.remove(); const modData = moduleId ? notificationData[moduleId] : null; console.log('模块数据:', moduleId, modData); if (modData && modData.hasUpdate) { console.log('✅ 应该添加标签的模块:', moduleId); const badge = document.createElement('span'); badge.className = 'update-badge'; badge.textContent = '有更新'; // 内联样式保证可见 badge.style.backgroundColor = '#2196f3'; badge.style.color = 'white'; badge.style.padding = '2px 8px'; badge.style.borderRadius = '12px'; badge.style.fontSize = '12px'; badge.style.display = 'inline-block'; badge.style.marginTop = '8px'; // 直接追加到卡片末尾 card.appendChild(badge); console.log('✅ 标签已追加到卡片', moduleId); } else { console.log('❌ 不需要添加标签的模块:', moduleId); } }); console.log('🟦 renderUpdateBadges 执行完毕'); } // ========== 小红点渲染 ========== function renderUnreadDots() { const cards = getModuleCards(); cards.forEach(card => { let moduleId = card.dataset.moduleId || card.id || card.querySelector('.module-name')?.textContent.trim(); if (!moduleId) { moduleId = guessModuleIdFromCard(card); } if (!moduleId) return; const oldDot = card.querySelector('.unread-dot'); if (oldDot) oldDot.remove(); const modData = notificationData[moduleId]; if (modData && modData.hasUpdate && modData.count > 0) { const dot = document.createElement('span'); dot.className = 'unread-dot'; dot.textContent = modData.count > 9 ? '9+' : modData.count; card.appendChild(dot); } }); } // ========== 标记模块已读 ========== function markModuleAsRead(moduleId) { if (notificationData[moduleId]) { notificationData[moduleId].hasUpdate = false; notificationData[moduleId].count = 0; localStorage.setItem(STORAGE_KEY, JSON.stringify(notificationData)); renderUpdateBadges(); renderUnreadDots(); updateBellBadge(); renderNotificationPanel(); } } // ========== 铃铛相关 ========== let bellContainer, panel, overlay; let isPanelOpen = false; function createBell() { const header = document.querySelector('.channel-header') || document.querySelector('header') || document.body; bellContainer = document.createElement('div'); bellContainer.className = 'bell-container'; bellContainer.innerHTML = ` `; const title = header.querySelector('h1, h2'); if (title) { title.insertAdjacentElement('afterend', bellContainer); } else { header.appendChild(bellContainer); } panel = document.createElement('div'); panel.className = 'notification-panel'; panel.innerHTML = `