import { useState, useEffect } from 'react' import { StatCard } from './StatCard' import { useSituationStore } from '@/store/situationStore' import { fetchAndSetSituation } from '@/store/situationStore' import { useStatsStore } from '@/store/statsStore' import { useReplaySituation } from '@/hooks/useReplaySituation' import { usePlaybackStore } from '@/store/playbackStore' import { Wifi, WifiOff, Clock, Share2, Heart, Eye, MessageSquare, RefreshCw } from 'lucide-react' const STORAGE_LIKES = 'us-iran-dashboard-likes' // 开发环境下每标签一个 viewer-id,便于本地验证「在看」开/关标签变化 const getViewerId = (): string | undefined => { if (typeof import.meta !== 'undefined' && import.meta.env?.DEV && typeof crypto?.randomUUID === 'function') { try { let id = sessionStorage.getItem('us-iran-viewer-id') if (!id) { id = crypto.randomUUID() sessionStorage.setItem('us-iran-viewer-id', id) } return id } catch { return undefined } } return undefined } function getStoredLikes(): number { try { return parseInt(localStorage.getItem(STORAGE_LIKES) ?? '0', 10) } catch { return 0 } } export function HeaderPanel() { const situation = useReplaySituation() const isConnected = useSituationStore((s) => s.isConnected) const isReplayMode = usePlaybackStore((s) => s.isReplayMode) const { usForces, iranForces } = situation const [now, setNow] = useState(() => new Date()) const [likes, setLikes] = useState(getStoredLikes) const [liked, setLiked] = useState(false) const stats = useStatsStore((s) => s.stats) const setStats = useStatsStore((s) => s.setStats) const viewers = stats.viewers ?? 0 const cumulative = stats.cumulative ?? 0 const feedbackCount = stats.feedbackCount ?? 0 const shareCount = stats.shareCount ?? 0 const [feedbackOpen, setFeedbackOpen] = useState(false) const [feedbackText, setFeedbackText] = useState('') const [feedbackSending, setFeedbackSending] = useState(false) const [feedbackDone, setFeedbackDone] = useState(false) const [refreshing, setRefreshing] = useState(false) useEffect(() => { const timer = setInterval(() => setNow(new Date()), 1000) return () => clearInterval(timer) }, []) const fetchStats = async () => { try { const headers: HeadersInit = {} const vid = getViewerId() if (vid) headers['X-Viewer-Id'] = vid const res = await fetch('/api/visit', { method: 'POST', headers }) const data = await res.json() setStats({ viewers: data.viewers, cumulative: data.cumulative, feedbackCount: data.feedbackCount, shareCount: data.shareCount, }) } catch { setStats({ viewers: 0, cumulative: 0 }) } } useEffect(() => { fetchStats() const t = setInterval(fetchStats, 15000) return () => clearInterval(t) }, []) const handleShare = async () => { const url = window.location.href const title = '美伊军事态势显示' let shared = false if (typeof navigator.share === 'function') { try { await navigator.share({ title, url }) shared = true } catch (e) { if ((e as Error).name !== 'AbortError') { await copyToClipboard(url) shared = true } } } else { await copyToClipboard(url) shared = true } if (shared) { try { const res = await fetch('/api/share', { method: 'POST' }) const data = await res.json() if (data.shareCount != null) setStats({ shareCount: data.shareCount }) } catch {} } } const copyToClipboard = (text: string) => { return navigator.clipboard?.writeText(text) ?? Promise.resolve() } const handleFeedback = async () => { const text = feedbackText.trim() if (!text || feedbackSending) return setFeedbackSending(true) try { const res = await fetch('/api/feedback', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ content: text }), }) const data = await res.json() if (data.ok) { setFeedbackText('') setFeedbackDone(true) setStats({ feedbackCount: (feedbackCount ?? 0) + 1 }) setTimeout(() => { setFeedbackOpen(false) setFeedbackDone(false) }, 800) } } catch { setFeedbackDone(false) } finally { setFeedbackSending(false) } } const handleLike = () => { if (liked) return setLiked(true) const next = likes + 1 setLikes(next) try { localStorage.setItem(STORAGE_LIKES, String(next)) } catch {} } const formatDateTime = (d: Date) => d.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit', }) const formatDataTime = (iso: string) => { const d = new Date(iso) return d.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour12: false, hour: '2-digit', minute: '2-digit', }) } return (

美伊军事态势显示

{formatDateTime(now)}
{(isConnected || isReplayMode) && ( {formatDataTime(situation.lastUpdated)} {isReplayMode ? '(回放)' : '(实时更新)'} )}
在看 {viewers} | 看过 {cumulative}
{isConnected ? : } {isConnected ? '实时' : '已断开'}
国力对比
美 {usForces.powerIndex.overall} 伊 {iranForces.powerIndex.overall}
{/* 留言弹窗 */} {feedbackOpen && (
!feedbackSending && setFeedbackOpen(false)} >
e.stopPropagation()} >

后台留言

您的反馈将提交给开发者,用于后续优化