import { GameManager } from '../GameManager' import { generateWeeklyOptions } from '../data/buzzwords' export type WeeklyReportCallbacks = { onBossInspection: () => void onFullStamina: () => void } /** * 周报结算弹窗 * 每3波结束后触发,仿钉钉/飞书全屏通知风格 * 用 DOM 层实现(z-index: 9999),覆盖 canvas */ export class WeeklyReportModal { private container: HTMLElement | null = null private timerInterval: ReturnType | null = null private callbacks: WeeklyReportCallbacks private onClose?: () => void constructor(callbacks: WeeklyReportCallbacks) { this.callbacks = callbacks } /** 显示弹窗 */ show(onClose?: () => void): void { this.onClose = onClose this.createDOM() this.startTimer() } private createDOM(): void { // 移除旧弹窗(防止重复) this.cleanup() const { options, correctIndex } = generateWeeklyOptions() // 背景蒙层 const overlay = document.createElement('div') overlay.id = 'weekly-report-overlay' overlay.style.cssText = ` position: fixed; inset: 0; background: rgba(0,0,0,0.82); z-index: 9999; display: flex; align-items: center; justify-content: center; font-family: 'VT323', monospace; animation: fadeInOverlay 0.3s ease; ` // 弹窗卡片 const card = document.createElement('div') card.style.cssText = ` background: #1a1a2e; border: 2px solid #7c3aed; border-radius: 12px; padding: 32px 40px; max-width: 520px; width: 90%; box-shadow: 0 0 40px rgba(124,58,237,0.5); position: relative; animation: slideInCard 0.35s ease; ` // 顶部进度条容器 const progressWrap = document.createElement('div') progressWrap.style.cssText = ` position: absolute; top: 0; left: 0; right: 0; height: 6px; background: #374151; border-radius: 12px 12px 0 0; overflow: hidden; ` const progressBar = document.createElement('div') progressBar.id = 'weekly-progress-bar' progressBar.style.cssText = ` height: 100%; background: #ef4444; width: 100%; transition: width linear; ` progressWrap.appendChild(progressBar) card.appendChild(progressWrap) // 标题 const title = document.createElement('div') title.textContent = '📊 周报提交时间!' title.style.cssText = ` font-size: 28px; color: #a78bfa; text-align: center; margin-bottom: 8px; letter-spacing: 2px; ` card.appendChild(title) // 副标题 const subtitle = document.createElement('div') subtitle.textContent = '请选择正确的互联网黑话,提交本周工作总结' subtitle.style.cssText = ` font-size: 16px; color: #9ca3af; text-align: center; margin-bottom: 8px; ` card.appendChild(subtitle) // 倒计时文字 const timerText = document.createElement('div') timerText.id = 'weekly-timer-text' timerText.textContent = '10' timerText.style.cssText = ` font-size: 20px; color: #fcd34d; text-align: center; margin-bottom: 24px; ` card.appendChild(timerText) // 选项区域 const optionsWrap = document.createElement('div') optionsWrap.style.cssText = ` display: flex; gap: 12px; justify-content: center; margin-bottom: 20px; ` options.forEach((opt, idx) => { const btn = document.createElement('button') btn.textContent = opt btn.dataset.index = String(idx) btn.style.cssText = ` flex: 1; padding: 14px 8px; background: #0f172a; border: 2px solid #4b5563; border-radius: 8px; color: #e2e8f0; font-family: 'VT323', monospace; font-size: 20px; cursor: pointer; transition: border-color 0.2s, background 0.2s; ` btn.addEventListener('mouseenter', () => { if (!btn.disabled) { btn.style.borderColor = '#7c3aed' btn.style.background = '#1e1b4b' } }) btn.addEventListener('mouseleave', () => { if (!btn.disabled) { btn.style.borderColor = '#4b5563' btn.style.background = '#0f172a' } }) btn.addEventListener('click', () => { this.handleAnswer(idx === correctIndex, options[correctIndex]) }) optionsWrap.appendChild(btn) }) card.appendChild(optionsWrap) // 结果区域(初始隐藏) const resultArea = document.createElement('div') resultArea.id = 'weekly-result' resultArea.style.cssText = ` display: none; text-align: center; padding: 12px; border-radius: 8px; font-size: 20px; ` card.appendChild(resultArea) // 注入动画样式 const style = document.createElement('style') style.textContent = ` @keyframes fadeInOverlay { from { opacity: 0 } to { opacity: 1 } } @keyframes slideInCard { from { transform: translateY(-30px); opacity: 0 } to { transform: translateY(0); opacity: 1 } } ` document.head.appendChild(style) overlay.appendChild(card) document.body.appendChild(overlay) this.container = overlay } private startTimer(): void { let remaining = 10 const progressBar = document.getElementById('weekly-progress-bar') const timerText = document.getElementById('weekly-timer-text') if (progressBar) { // 使用 CSS transition 驱动进度条(10秒内从100%到0%) requestAnimationFrame(() => { progressBar.style.transition = 'width 10s linear' progressBar.style.width = '0%' }) } this.timerInterval = setInterval(() => { remaining-- if (timerText) timerText.textContent = String(remaining) if (remaining <= 0) { this.stopTimer() this.handleAnswer(false, '') } }, 1000) } private stopTimer(): void { if (this.timerInterval !== null) { clearInterval(this.timerInterval) this.timerInterval = null } } private handleAnswer(isCorrect: boolean, correctWord: string): void { this.stopTimer() // 禁用所有按钮 const buttons = this.container?.querySelectorAll('button') buttons?.forEach(btn => { (btn as HTMLButtonElement).disabled = true ;(btn as HTMLButtonElement).style.opacity = '0.5' ;(btn as HTMLButtonElement).style.cursor = 'default' }) const resultArea = document.getElementById('weekly-result') if (!resultArea) return if (isCorrect) { // 随机给 HC 或补满精力 if (Math.random() > 0.5) { const reward = 50 + Math.floor(Math.random() * 51) GameManager.getInstance().addHC(reward) resultArea.textContent = `✓ 正确!"${correctWord}" 就是本周黑话!获得 ${reward} HC!` resultArea.style.background = 'rgba(16,185,129,0.15)' resultArea.style.border = '1px solid #10b981' resultArea.style.color = '#6ee7b7' } else { this.callbacks.onFullStamina() resultArea.textContent = `✓ 正确!"${correctWord}" 完美!全场精力已补满!` resultArea.style.background = 'rgba(16,185,129,0.15)' resultArea.style.border = '1px solid #10b981' resultArea.style.color = '#6ee7b7' } } else { this.callbacks.onBossInspection() const msg = correctWord ? `✗ 答错了!正确答案是"${correctWord}"!老板来视察了!全场禁锢3秒!` : '✗ 超时了!老板来视察了!全场禁锢3秒!' resultArea.textContent = msg resultArea.style.background = 'rgba(239,68,68,0.15)' resultArea.style.border = '1px solid #ef4444' resultArea.style.color = '#fca5a5' } resultArea.style.display = 'block' // 2秒后自动关闭 setTimeout(() => { this.close() }, 2000) } private close(): void { this.cleanup() this.onClose?.() } private cleanup(): void { this.stopTimer() const existing = document.getElementById('weekly-report-overlay') if (existing) existing.remove() this.container = null } destroy(): void { this.cleanup() } }