Files
test1/game/ui/WeeklyReportModal.ts

286 lines
8.1 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<typeof setInterval> | 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()
}
}