fix:优化数据来源
This commit is contained in:
143
src/hooks/useReplaySituation.ts
Normal file
143
src/hooks/useReplaySituation.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
import { useMemo } from 'react'
|
||||
import type { MilitarySituation } from '@/data/mockData'
|
||||
import { useSituationStore } from '@/store/situationStore'
|
||||
import { usePlaybackStore } from '@/store/playbackStore'
|
||||
|
||||
/** 将系列时间映射到回放日 (2026-03-01) 以便按当天时刻插值 */
|
||||
function toReplayDay(iso: string, baseDay: string): string {
|
||||
const d = new Date(iso)
|
||||
const [y, m, day] = baseDay.slice(0, 10).split('-').map(Number)
|
||||
return new Date(y, (m || 1) - 1, day || 1, d.getUTCHours(), d.getUTCMinutes(), 0, 0).toISOString()
|
||||
}
|
||||
|
||||
function interpolateAt(
|
||||
series: { time: string; value: number }[],
|
||||
at: string,
|
||||
baseDay = '2026-03-01'
|
||||
): number {
|
||||
if (series.length === 0) return 0
|
||||
const t = new Date(at).getTime()
|
||||
const mapped = series.map((p) => ({
|
||||
time: toReplayDay(p.time, baseDay),
|
||||
value: p.value,
|
||||
}))
|
||||
const sorted = [...mapped].sort((a, b) => new Date(a.time).getTime() - new Date(b.time).getTime())
|
||||
const before = sorted.filter((p) => new Date(p.time).getTime() <= t)
|
||||
const after = sorted.filter((p) => new Date(p.time).getTime() > t)
|
||||
if (before.length === 0) return sorted[0].value
|
||||
if (after.length === 0) return sorted[sorted.length - 1].value
|
||||
const a = before[before.length - 1]
|
||||
const b = after[0]
|
||||
const ta = new Date(a.time).getTime()
|
||||
const tb = new Date(b.time).getTime()
|
||||
const f = tb === ta ? 1 : (t - ta) / (tb - ta)
|
||||
return a.value + f * (b.value - a.value)
|
||||
}
|
||||
|
||||
function linearProgress(start: string, end: string, at: string): number {
|
||||
const ts = new Date(start).getTime()
|
||||
const te = new Date(end).getTime()
|
||||
const ta = new Date(at).getTime()
|
||||
if (ta <= ts) return 0
|
||||
if (ta >= te) return 1
|
||||
return (ta - ts) / (te - ts)
|
||||
}
|
||||
|
||||
/** 根据回放时刻派生态势数据 */
|
||||
export function useReplaySituation(): MilitarySituation {
|
||||
const situation = useSituationStore((s) => s.situation)
|
||||
const { isReplayMode, playbackTime } = usePlaybackStore()
|
||||
|
||||
return useMemo(() => {
|
||||
if (!isReplayMode) return situation
|
||||
|
||||
const progress = linearProgress('2026-03-01T02:00:00.000Z', '2026-03-01T11:45:00.000Z', playbackTime)
|
||||
|
||||
// 华尔街趋势、反击情绪:按时间插值
|
||||
const wsValue = interpolateAt(situation.usForces.wallStreetInvestmentTrend, playbackTime)
|
||||
const retValue = interpolateAt(situation.iranForces.retaliationSentimentHistory, playbackTime)
|
||||
|
||||
// 战斗损失:从 0 线性增长到当前值
|
||||
const lerp = (a: number, b: number) => Math.round(a + progress * (b - a))
|
||||
const usLoss = situation.usForces.combatLosses
|
||||
const irLoss = situation.iranForces.combatLosses
|
||||
const civUs = usLoss.civilianCasualties ?? { killed: 0, wounded: 0 }
|
||||
const civIr = irLoss.civilianCasualties ?? { killed: 0, wounded: 0 }
|
||||
const usLossesAt = {
|
||||
bases: {
|
||||
destroyed: lerp(0, usLoss.bases.destroyed),
|
||||
damaged: lerp(0, usLoss.bases.damaged),
|
||||
},
|
||||
personnelCasualties: {
|
||||
killed: lerp(0, usLoss.personnelCasualties.killed),
|
||||
wounded: lerp(0, usLoss.personnelCasualties.wounded),
|
||||
},
|
||||
civilianCasualties: { killed: lerp(0, civUs.killed), wounded: lerp(0, civUs.wounded) },
|
||||
aircraft: lerp(0, usLoss.aircraft),
|
||||
warships: lerp(0, usLoss.warships),
|
||||
armor: lerp(0, usLoss.armor),
|
||||
vehicles: lerp(0, usLoss.vehicles),
|
||||
}
|
||||
const irLossesAt = {
|
||||
bases: {
|
||||
destroyed: lerp(0, irLoss.bases.destroyed),
|
||||
damaged: lerp(0, irLoss.bases.damaged),
|
||||
},
|
||||
personnelCasualties: {
|
||||
killed: lerp(0, irLoss.personnelCasualties.killed),
|
||||
wounded: lerp(0, irLoss.personnelCasualties.wounded),
|
||||
},
|
||||
civilianCasualties: { killed: lerp(0, civIr.killed), wounded: lerp(0, civIr.wounded) },
|
||||
aircraft: lerp(0, irLoss.aircraft),
|
||||
warships: lerp(0, irLoss.warships),
|
||||
armor: lerp(0, irLoss.armor),
|
||||
vehicles: lerp(0, irLoss.vehicles),
|
||||
}
|
||||
|
||||
// 被袭基地:按 damage_level 排序,高损毁先出现;根据 progress 决定显示哪些为 attacked
|
||||
const usLocs = situation.usForces.keyLocations || []
|
||||
const attackedBases = usLocs
|
||||
.filter((loc) => loc.status === 'attacked')
|
||||
.sort((a, b) => (b.damage_level ?? 0) - (a.damage_level ?? 0))
|
||||
const totalAttacked = attackedBases.length
|
||||
const shownAttackedCount = Math.round(progress * totalAttacked)
|
||||
const attackedNames = new Set(
|
||||
attackedBases.slice(0, shownAttackedCount).map((l) => l.name)
|
||||
)
|
||||
|
||||
const usLocsAt = usLocs.map((loc) => {
|
||||
if (loc.status === 'attacked' && !attackedNames.has(loc.name)) {
|
||||
return { ...loc, status: 'operational' as const }
|
||||
}
|
||||
return { ...loc }
|
||||
})
|
||||
|
||||
return {
|
||||
...situation,
|
||||
lastUpdated: playbackTime,
|
||||
usForces: {
|
||||
...situation.usForces,
|
||||
keyLocations: usLocsAt,
|
||||
combatLosses: usLossesAt,
|
||||
wallStreetInvestmentTrend: [
|
||||
...situation.usForces.wallStreetInvestmentTrend.filter((p) => new Date(p.time).getTime() <= new Date(playbackTime).getTime()),
|
||||
{ time: playbackTime, value: wsValue },
|
||||
].slice(-20),
|
||||
},
|
||||
iranForces: {
|
||||
...situation.iranForces,
|
||||
combatLosses: irLossesAt,
|
||||
retaliationSentiment: retValue,
|
||||
retaliationSentimentHistory: [
|
||||
...situation.iranForces.retaliationSentimentHistory.filter((p) => new Date(p.time).getTime() <= new Date(playbackTime).getTime()),
|
||||
{ time: playbackTime, value: retValue },
|
||||
].slice(-20),
|
||||
},
|
||||
recentUpdates: (situation.recentUpdates || []).filter(
|
||||
(u) => new Date(u.timestamp).getTime() <= new Date(playbackTime).getTime()
|
||||
),
|
||||
conflictEvents: situation.conflictEvents || [],
|
||||
conflictStats: situation.conflictStats || { total_events: 0, high_impact_events: 0, estimated_casualties: 0, estimated_strike_count: 0 },
|
||||
}
|
||||
}, [situation, isReplayMode, playbackTime])
|
||||
}
|
||||
Reference in New Issue
Block a user