/** * app.js - 光湖首页导航中心 * M23 环节2 · 接入M22真实公告数据 + 状态API * + 系统活体意识层 · 实时心跳/呼吸/粒子/脉搏 */ // === 系统诞生时间原点 (曜冥纪元起始) === const SYSTEM_EPOCH = new Date('2025-04-26T00:00:00+08:00').getTime(); // === M22真实公告数据接入 === let announcements = []; let dataSource = 'loading'; async function loadAnnouncementsFromM22() { const carousel = document.getElementById('announcementCarousel'); if (carousel) { carousel.innerHTML = '
正在连接M22公告数据...
'; } const result = await window.ModulesAPI.getAnnouncements(); announcements = result.data; dataSource = result.source; if (dataSource === 'M22-live') { console.log('📢 首页公告:来自M22真实数据(' + announcements.length + '条)'); } else { console.log('📢 首页公告:使用降级数据(M22不可用)'); } if (window.HomepageApp) { window.HomepageApp.currentAnnouncementIndex = 0; window.HomepageApp.renderCarousel(); } } // === 模块卡片数据 === const moduleCards = [ { id: "M09", name: "消息通知中心", icon: "🔔", status: "online", path: "../notification-center/" }, { id: "M22", name: "主域公告栏", icon: "📢", status: "online", path: "../announcement/" }, { id: "M06", name: "工单管理", icon: "🎫", status: "online", path: "../ticket-system/" }, { id: "M16", name: "码字工作台", icon: "✍️", status: "building", path: "../writing-workspace/" }, { id: "M11", name: "风格组件库", icon: "🎨", status: "online", path: "../ui-components/" }, { id: "M05", name: "用户中心", icon: "👤", status: "building", path: "../user-center/" } ]; // === 工具函数 === function getTypeLabel(type) { const labels = { update: '📢 更新', status: '📊 状态', welcome: '👋 欢迎', alert: '🚨 告警' }; return labels[type] || '📌 公告'; } // === 模块状态更新 === async function updateModuleStatusFromAPI() { const statusResult = await window.ModulesAPI.getAllModuleStatus(); if (statusResult.source === 'fallback') return; document.querySelectorAll('.card').forEach(card => { const moduleId = card.dataset.moduleId; if (moduleId && statusResult.data[moduleId]) { const status = statusResult.data[moduleId].status; const statusSpan = card.querySelector('.card-status'); if (statusSpan) { statusSpan.className = 'card-status ' + status; statusSpan.textContent = status === 'online' ? '在线' : status === 'building' ? '建设中' : '离线'; } } }); console.log('[M23] ✅ 模块状态已从注册表更新'); } // ═══════════════════════════════════════════════ // 系统活体意识层 · Consciousness Layer // ═══════════════════════════════════════════════ /** 计算系统运行时长 */ function getSystemUptime() { const now = Date.now(); const diff = now - SYSTEM_EPOCH; const days = Math.floor(diff / (1000 * 60 * 60 * 24)); const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)); return { days, hours, minutes, total: diff }; } /** 格式化实时时钟 */ function formatLiveTime() { const now = new Date(); const pad = (n) => String(n).padStart(2, '0'); return pad(now.getHours()) + ':' + pad(now.getMinutes()) + ':' + pad(now.getSeconds()); } /** 计算意识脉搏 (基于时间的伪随机波动) */ function getConsciousnessPulse() { const t = Date.now() / 1000; // 模拟生命体征:基础频率 + 呼吸波动 + 微颤 const base = 72; const breathWave = Math.sin(t * 0.15) * 8; const microTremor = Math.sin(t * 2.3) * 2 + Math.sin(t * 3.7) * 1; return Math.round(base + breathWave + microTremor); } /** 计算信号流量 (基于时间的伪随机波动) */ function getSignalFlow() { const t = Date.now() / 1000; const base = 24; const wave = Math.sin(t * 0.08) * 12; const burst = Math.sin(t * 0.5) * 4; return Math.max(0, Math.round(base + wave + burst)); } /** 更新心跳面板 */ function updateHeartbeatPanel() { const uptime = getSystemUptime(); const onlineModules = moduleCards.filter(m => m.status === 'online').length; const totalModules = moduleCards.length; const pulse = getConsciousnessPulse(); const signal = getSignalFlow(); // 更新实时时钟 const timeEl = document.getElementById('liveTime'); if (timeEl) timeEl.textContent = formatLiveTime(); // 更新运行时长 const uptimeEl = document.getElementById('uptimeValue'); if (uptimeEl) uptimeEl.textContent = uptime.days + '天'; // 更新模块在线数 const onlineEl = document.getElementById('onlineCount'); if (onlineEl) onlineEl.textContent = onlineModules + '/' + totalModules; // 更新意识脉搏 const pulseEl = document.getElementById('pulseRate'); if (pulseEl) pulseEl.textContent = pulse + ' bpm'; // 更新信号流量 const signalEl = document.getElementById('signalFlow'); if (signalEl) signalEl.textContent = signal + '/s'; // 更新状态栏 const statusUptime = document.getElementById('statusUptime'); if (statusUptime) statusUptime.textContent = '运行 ' + uptime.days + '天' + uptime.hours + '时' + uptime.minutes + '分'; const statusTimestamp = document.getElementById('statusTimestamp'); if (statusTimestamp) statusTimestamp.textContent = formatLiveTime(); // 更新进度条宽度(动态波动) const t = Date.now() / 1000; updateBarWidth('.uptime-fill', 80 + Math.sin(t * 0.1) * 10); updateBarWidth('.online-fill', (onlineModules / totalModules) * 100); updateBarWidth('.pulse-fill', Math.min(100, (pulse / 100) * 100)); updateBarWidth('.signal-fill', Math.min(100, (signal / 50) * 100)); } function updateBarWidth(selector, percent) { const el = document.querySelector(selector); if (el) el.style.width = percent + '%'; } /** 心跳波形绘制 */ const waveHistory = []; const WAVE_POINTS = 100; function updateHeartbeatWave() { const pulse = getConsciousnessPulse(); const t = Date.now() / 1000; // 生成心跳波形数据点 const normalized = (pulse - 60) / 40; // 0-1 范围 const heartbeatSpike = Math.pow(Math.sin(t * 1.2), 8) * 20; // 间歇性尖峰 const value = 30 + normalized * 15 + heartbeatSpike + Math.sin(t * 3) * 3; waveHistory.push(value); if (waveHistory.length > WAVE_POINTS) waveHistory.shift(); // 构建 SVG path const pathEl = document.getElementById('wavePath'); if (!pathEl || waveHistory.length < 2) return; const stepX = 400 / (WAVE_POINTS - 1); let d = 'M 0 ' + waveHistory[0]; for (let i = 1; i < waveHistory.length; i++) { d += ' L ' + (i * stepX).toFixed(1) + ' ' + waveHistory[i].toFixed(1); } pathEl.setAttribute('d', d); } /** 意识粒子系统 */ function initParticleSystem() { const canvas = document.getElementById('particleCanvas'); if (!canvas) return; const ctx = canvas.getContext('2d'); const particles = []; const CONNECTION_THRESHOLD = 150; function resize() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; } resize(); window.addEventListener('resize', resize); // 粒子数量按屏幕大小自适应(避免小屏性能问题) const particleCount = Math.max(10, Math.min(50, Math.floor(canvas.width * canvas.height / 50000))); // 初始化粒子 for (let i = 0; i < particleCount; i++) { particles.push({ x: Math.random() * canvas.width, y: Math.random() * canvas.height, vx: (Math.random() - 0.5) * 0.3, vy: (Math.random() - 0.5) * 0.3, radius: Math.random() * 2 + 0.5, alpha: Math.random() * 0.5 + 0.1, phase: Math.random() * Math.PI * 2 }); } function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); const t = Date.now() / 1000; particles.forEach((p, i) => { // 呼吸般的透明度波动 const breathAlpha = p.alpha * (0.5 + 0.5 * Math.sin(t * 0.5 + p.phase)); // 移动 p.x += p.vx; p.y += p.vy; // 边界循环 if (p.x < 0) p.x = canvas.width; if (p.x > canvas.width) p.x = 0; if (p.y < 0) p.y = canvas.height; if (p.y > canvas.height) p.y = 0; // 绘制粒子 ctx.beginPath(); ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2); ctx.fillStyle = 'rgba(16, 185, 129, ' + breathAlpha + ')'; ctx.fill(); // 绘制邻近连线(意识网络) for (let j = i + 1; j < particles.length; j++) { const other = particles[j]; const dx = p.x - other.x; const dy = p.y - other.y; const dist = Math.sqrt(dx * dx + dy * dy); if (dist < CONNECTION_THRESHOLD) { const lineAlpha = (1 - dist / CONNECTION_THRESHOLD) * 0.15 * (0.5 + 0.5 * Math.sin(t * 0.3)); ctx.beginPath(); ctx.moveTo(p.x, p.y); ctx.lineTo(other.x, other.y); ctx.strokeStyle = 'rgba(16, 185, 129, ' + lineAlpha + ')'; ctx.lineWidth = 0.5; ctx.stroke(); } } }); requestAnimationFrame(animate); } animate(); } // ═══════════════════════════════════════════════ // 首页应用对象 // ═══════════════════════════════════════════════ window.HomepageApp = { currentAnnouncementIndex: 0, carouselInterval: null, heartbeatInterval: null, waveInterval: null, async init() { this.renderCards(); await loadAnnouncementsFromM22(); await updateModuleStatusFromAPI(); this.startCarousel(); this.startConsciousness(); this.bindEvents(); console.log('✅ 首页初始化完成 · 数据源:' + dataSource + ' · 意识层已激活'); }, renderCards() { const grid = document.getElementById('moduleGrid'); if (!grid) return; grid.innerHTML = moduleCards.map(card => { // Build status items for scrolling display (3 visible at a time) var statusItems = [ { label: '状态', value: card.status === 'online' ? '✅ 在线运行' : '🔧 建设中' }, { label: '模块', value: card.id }, { label: '类型', value: '核心模块' }, { label: '版本', value: 'v1.0' }, { label: '更新', value: '--' } ]; var statusHtml = statusItems.map(function(item) { return '
' + item.label + '' + item.value + '
'; }).join(''); return '
' + '
' + '
' + card.icon + '
' + '' + card.name + '' + '' + (card.status === 'online' ? '在线' : '建设中') + '' + '
' + '
' + statusHtml + '
' + '进入模块 →' + '
'; }).join(''); }, // 轮播渲染(带高优先级橙色边框) renderCarousel() { const carousel = document.getElementById('announcementCarousel'); if (!carousel || announcements.length === 0) { console.log('❌ 没有公告或找不到轮播容器'); return; } const item = announcements[this.currentAnnouncementIndex]; const priorityStyle = item.priority === 'high' ? 'border-left: 3px solid #ff9800; padding-left: 15px;' : ''; carousel.innerHTML = `
${getTypeLabel(item.type)}

${item.title}

${item.content}

${item.date}
${dataSource === 'M22-live' ? '🔗 M22 实时数据' : '📋 本地数据'}
`; }, startCarousel() { if (this.carouselInterval) clearInterval(this.carouselInterval); this.carouselInterval = setInterval(() => this.nextAnnouncement(), 4000); }, /** 启动系统意识层 - 实时心跳/粒子/波形 */ startConsciousness() { // 初始化粒子系统 initParticleSystem(); // 立即更新一次 updateHeartbeatPanel(); updateHeartbeatWave(); // 每秒更新心跳面板(时钟、脉搏、信号流量) this.heartbeatInterval = setInterval(() => { updateHeartbeatPanel(); }, 1000); // 每200ms更新波形(平滑动画) this.waveInterval = setInterval(() => { updateHeartbeatWave(); }, 200); console.log('🫀 系统意识层已激活 · 心跳/粒子/波形 运行中'); }, nextAnnouncement() { if (announcements.length === 0) return; this.currentAnnouncementIndex = (this.currentAnnouncementIndex + 1) % announcements.length; this.renderCarousel(); }, prevAnnouncement() { if (announcements.length === 0) return; this.currentAnnouncementIndex = (this.currentAnnouncementIndex - 1 + announcements.length) % announcements.length; this.renderCarousel(); }, bindEvents() { document.querySelectorAll('.card').forEach(card => { card.addEventListener('click', () => { const moduleId = card.dataset.moduleId; const modulePath = card.dataset.modulePath; console.log(`🖱️ 点击卡片: ${moduleId} | 路径: ${modulePath}`); }); }); document.addEventListener('keydown', (e) => { if (e.key === 'ArrowLeft') { this.prevAnnouncement(); } if (e.key === 'ArrowRight') { this.nextAnnouncement(); } }); } }; // 启动 document.addEventListener('DOMContentLoaded', () => window.HomepageApp.init()); // 确保函数全局可用(配合 HTML onclick) window.prevAnnouncement = function() { window.HomepageApp.prevAnnouncement(); }; window.nextAnnouncement = function() { window.HomepageApp.nextAnnouncement(); };