fix:优化数据样式

This commit is contained in:
Daniel
2026-03-03 10:35:11 +08:00
parent 92914e6522
commit 4dd1f7e7dc
6 changed files with 51 additions and 18 deletions

View File

@@ -7,6 +7,7 @@ import { usePlaybackStore } from '@/store/playbackStore'
import { Wifi, WifiOff, Clock, Share2, Heart, Eye, MessageSquare } from 'lucide-react'
const STORAGE_LIKES = 'us-iran-dashboard-likes'
// 冲突时长显示在 TimelinePanel数据回放栏
function getStoredLikes(): number {
try {
@@ -155,13 +156,15 @@ export function HeaderPanel() {
return (
<header className="flex shrink-0 flex-wrap items-center justify-between gap-2 overflow-hidden border-b border-military-border bg-military-panel/95 px-2 py-2 font-orbitron sm:gap-3 sm:px-4 sm:py-3 lg:flex-nowrap lg:gap-4 lg:px-6">
<div className="flex min-w-0 flex-wrap items-center gap-2 sm:gap-3 lg:gap-6">
<h1 className="truncate text-sm font-bold uppercase tracking-wider text-military-accent sm:text-base sm:tracking-widest lg:text-2xl">
<h1 className="min-w-0 shrink truncate text-sm font-bold uppercase tracking-wider text-military-accent sm:text-base sm:tracking-widest lg:text-2xl">
</h1>
<div className="flex min-w-0 shrink-0 flex-col gap-0.5">
<div className="flex w-[12rem] shrink-0 flex-col gap-0.5 font-mono sm:w-[13rem]">
<div className="flex items-center gap-1.5 text-xs text-military-text-secondary sm:gap-2 sm:text-sm">
<Clock className="h-3.5 w-3.5 shrink-0 sm:h-4 sm:w-4" />
<span className="tabular-nums sm:min-w-[10rem]">{formatDateTime(now)}</span>
<span className="min-w-[10rem] tabular-nums sm:min-w-[11rem]">
{formatDateTime(now)}
</span>
</div>
{(isConnected || isReplayMode) && (
<span className={`text-[10px] ${isReplayMode ? 'text-military-accent' : 'text-green-500/90'}`}>

View File

@@ -1,10 +1,13 @@
import { useEffect, useRef } from 'react'
import { useEffect, useRef, useState } from 'react'
import { Play, Pause, SkipBack, SkipForward, History } from 'lucide-react'
import { usePlaybackStore, REPLAY_TICKS, REPLAY_START, REPLAY_END } from '@/store/playbackStore'
import { useSituationStore } from '@/store/situationStore'
import { NewsTicker } from './NewsTicker'
import { config } from '@/config'
/** 冲突开始时间2月28日凌晨 03:00本地时间 */
const CONFLICT_START = new Date(2026, 1, 28, 3, 0, 0, 0)
function formatTick(iso: string): string {
const d = new Date(iso)
return d.toLocaleString('zh-CN', {
@@ -16,8 +19,17 @@ function formatTick(iso: string): string {
})
}
function getConflictDuration(toTime: Date): { days: number; hours: number } {
const diffMs = toTime.getTime() - CONFLICT_START.getTime()
if (diffMs <= 0) return { days: 0, hours: 0 }
const days = Math.floor(diffMs / (24 * 60 * 60 * 1000))
const hours = Math.floor((diffMs % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000))
return { days, hours }
}
export function TimelinePanel() {
const situation = useSituationStore((s) => s.situation)
const [now, setNow] = useState(() => new Date())
const {
isReplayMode,
playbackTime,
@@ -33,6 +45,14 @@ export function TimelinePanel() {
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null)
useEffect(() => {
const t = setInterval(() => setNow(new Date()), 1000)
return () => clearInterval(t)
}, [])
const toTime = isReplayMode ? new Date(playbackTime) : now
const conflictDuration = getConflictDuration(toTime)
useEffect(() => {
if (!isPlaying || !isReplayMode) {
if (timerRef.current) {
@@ -64,8 +84,18 @@ export function TimelinePanel() {
}
return (
<div className="shrink-0 border-b border-military-border bg-military-panel/95 px-3 py-2">
<div className="flex flex-wrap items-center gap-3">
<div className="relative shrink-0 border-b border-military-border bg-military-panel/95 px-3 py-2">
{!isReplayMode && (
<div
className="pointer-events-none absolute inset-0 flex items-center justify-center px-2"
aria-hidden
>
<span className="tabular-nums font-bold text-red-500">
{conflictDuration.days} {conflictDuration.hours}
</span>
</div>
)}
<div className="relative flex flex-wrap items-center gap-3">
<button
type="button"
onClick={() => setReplayMode(!isReplayMode)}

View File

@@ -316,12 +316,12 @@ export function WarMap() {
const blink = 0.5 + 0.5 * Math.sin(elapsed * 0.003)
map.setPaintProperty('points-damaged', 'circle-opacity', blink)
}
// attacked: 红色脉冲 2s 循环, 半径随 zoom 缩放
// attacked: 红色脉冲 2s 循环, 半径随 zoom 缩放phase/r/opacity 钳位避免浮点或取模越界
if (map.getLayer('points-attacked-pulse')) {
const cycle = 2000
const phase = (elapsed % cycle) / cycle
const r = 40 * phase * zoomScale
const opacity = 1 - phase
const phase = Math.max(0, Math.min(1, (elapsed % cycle) / cycle))
const r = Math.max(0, 40 * phase * zoomScale)
const opacity = Math.min(1, Math.max(0, 1 - phase))
map.setPaintProperty('points-attacked-pulse', 'circle-radius', r)
map.setPaintProperty('points-attacked-pulse', 'circle-opacity', opacity)
}
@@ -385,12 +385,12 @@ export function WarMap() {
)
israelSrc.setData({ type: 'FeatureCollection', features })
}
// 伊朗被打击目标:蓝色脉冲 (2s 周期), 半径随 zoom 缩放
// 伊朗被打击目标:蓝色脉冲 (2s 周期), 半径随 zoom 缩放phase/r/opacity 钳位
if (map.getLayer('allied-strike-targets-pulse')) {
const cycle = 2000
const phase = (elapsed % cycle) / cycle
const r = 35 * phase * zoomScale
const opacity = Math.max(0, 1 - phase * 1.2)
const phase = Math.max(0, Math.min(1, (elapsed % cycle) / cycle))
const r = Math.max(0, 35 * phase * zoomScale)
const opacity = Math.min(1, Math.max(0, 1 - phase * 1.2))
map.setPaintProperty('allied-strike-targets-pulse', 'circle-radius', r)
map.setPaintProperty('allied-strike-targets-pulse', 'circle-opacity', opacity)
}
@@ -399,12 +399,12 @@ export function WarMap() {
const blink = 0.5 + 0.5 * Math.sin(elapsed * 0.004)
map.setPaintProperty('gdelt-events-orange', 'circle-opacity', blink)
}
// GDELT 红色 (710):脉冲扩散, 半径随 zoom 缩放
// GDELT 红色 (710):脉冲扩散, 半径随 zoom 缩放phase/r/opacity 钳位
if (map.getLayer('gdelt-events-red-pulse')) {
const cycle = 2200
const phase = (elapsed % cycle) / cycle
const r = 30 * phase * zoomScale
const opacity = Math.max(0, 1 - phase * 1.1)
const phase = Math.max(0, Math.min(1, (elapsed % cycle) / cycle))
const r = Math.max(0, 30 * phase * zoomScale)
const opacity = Math.min(1, Math.max(0, 1 - phase * 1.1))
map.setPaintProperty('gdelt-events-red-pulse', 'circle-radius', r)
map.setPaintProperty('gdelt-events-red-pulse', 'circle-opacity', opacity)
}