feat: 重构战报弹窗与模拟器视图,优化UI与逻辑

重构BattleReportDialog和BattleSimulatorView相关静态资源,替换旧版JS/CSS文件,提升界面一致性和交互体验。新增和优化空状态、滚动区域等通用UI组件,移除部分冗余composable,完善多语言内容。引入导弹逻辑,补充版本检测工具,提升整体代码结构和可维护性。
This commit is contained in:
谦君
2025-12-15 20:04:40 +08:00
parent 9b9fda0400
commit 59dd7bfd05
126 changed files with 3944 additions and 1487 deletions

View File

@@ -162,7 +162,7 @@
<div
v-for="slot in systemSlots"
:key="slot.position"
class="flex items-center gap-2 sm:gap-4 p-2 sm:p-3 border rounded-lg hover:bg-muted/50 transition-colors"
class="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4 p-2 sm:p-3 border rounded-lg hover:bg-muted/50 transition-colors"
:class="{
// 空位置
'bg-muted/30': !slot.planet,
@@ -192,38 +192,81 @@
(!getRelation(slot.planet) || getRelation(slot.planet)?.status === RelationStatus.Neutral)
}"
>
<!-- 位置编号 -->
<div class="w-8 sm:w-12 text-center">
<Badge variant="outline" class="text-xs sm:text-sm">{{ slot.position }}</Badge>
</div>
<!-- 星球信息 -->
<div class="flex-1 min-w-0">
<div v-if="slot.planet" class="space-y-1">
<!-- 移动端:垂直布局 / PC端水平布局 -->
<div class="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2">
<!-- 星球名称和坐标 -->
<div class="flex items-baseline gap-1.5 min-w-0">
<h3 class="font-semibold text-sm sm:text-base truncate">{{ slot.planet.name }}</h3>
<span class="text-xs text-muted-foreground whitespace-nowrap flex-shrink-0 sm:hidden">
[{{ slot.planet.position.galaxy }}:{{ slot.planet.position.system }}:{{ slot.planet.position.position }}]
</span>
<!-- 移动端布局 -->
<div class="sm:hidden w-full space-y-2">
<!-- 第一行:位置编号 + 星球信息(名称、坐标、状态、残骸) -->
<div class="flex items-start gap-2 w-full">
<!-- 位置编号 -->
<div class="w-8 text-center flex-shrink-0">
<Badge variant="outline" class="text-xs">{{ slot.position }}</Badge>
</div>
<!-- 星球信息 -->
<div class="flex-1 min-w-0">
<div v-if="slot.planet" class="space-y-1">
<!-- 第一行:名称、坐标、状态、残骸 -->
<div class="flex items-center gap-1.5 min-w-0 flex-wrap">
<h3 class="font-semibold text-sm truncate">{{ slot.planet.name }}</h3>
<span class="text-xs text-muted-foreground whitespace-nowrap flex-shrink-0">
[{{ slot.planet.position.galaxy }}:{{ slot.planet.position.system }}:{{ slot.planet.position.position }}]
</span>
<Badge v-if="isMyPlanet(slot.planet)" variant="default" class="text-xs flex-shrink-0">
{{ t('galaxyView.mine') }}
</Badge>
<Badge v-else :variant="getRelationBadgeVariant(slot.planet)" class="text-xs flex-shrink-0">
{{ getRelationStatusText(slot.planet) }}
</Badge>
<Popover v-if="getDebrisFieldAt(currentGalaxy, currentSystem, slot.position)">
<PopoverTrigger as-child>
<Badge
variant="outline"
class="text-xs cursor-pointer hover:bg-amber-50 dark:hover:bg-amber-950/30 border-amber-300 dark:border-amber-700 text-amber-700 dark:text-amber-400 gap-1"
>
<Recycle class="h-3 w-3" />
</Badge>
</PopoverTrigger>
<PopoverContent class="w-auto p-3" side="top" align="start">
<div class="space-y-2">
<p class="text-xs font-semibold text-amber-700 dark:text-amber-400">{{ t('galaxyView.debrisField') }}</p>
<div class="space-y-1 text-xs">
<div class="flex items-center gap-2">
<ResourceIcon type="metal" size="sm" />
<span class="text-muted-foreground">{{ t('resources.metal') }}:</span>
<span class="font-medium">
{{ formatNumber(getDebrisFieldAt(currentGalaxy, currentSystem, slot.position)!.resources.metal) }}
</span>
</div>
<div class="flex items-center gap-2">
<ResourceIcon type="crystal" size="sm" />
<span class="text-muted-foreground">{{ t('resources.crystal') }}:</span>
<span class="font-medium">
{{ formatNumber(getDebrisFieldAt(currentGalaxy, currentSystem, slot.position)!.resources.crystal) }}
</span>
</div>
</div>
</div>
</PopoverContent>
</Popover>
</div>
<!-- 第二行:好感度 -->
<div v-if="!isMyPlanet(slot.planet) && getReputationValue(slot.planet) !== null" class="text-xs">
<span class="text-muted-foreground">{{ t('diplomacy.reputation') }}:</span>
<span class="ml-1 font-semibold" :class="getReputationColor(getReputationValue(slot.planet))">
{{ getReputationValue(slot.planet)! > 0 ? '+' : '' }}{{ getReputationValue(slot.planet) }}
</span>
</div>
</div>
<!-- 徽章组 -->
<div class="flex items-center gap-2 flex-wrap">
<Badge v-if="isMyPlanet(slot.planet)" variant="default" class="text-xs">{{ t('galaxyView.mine') }}</Badge>
<Badge v-else :variant="getRelationBadgeVariant(slot.planet)" class="text-xs">
{{ getRelationStatusText(slot.planet) }}
</Badge>
<!-- 残骸场徽章 - 紧凑显示 -->
<!-- 空位置 -->
<div v-else class="space-y-1">
<div class="text-sm text-muted-foreground">{{ t('galaxyView.emptySlot') }}</div>
<!-- 残骸场徽章 - 空位置时也显示 -->
<Popover v-if="getDebrisFieldAt(currentGalaxy, currentSystem, slot.position)">
<PopoverTrigger as-child>
<Badge
variant="outline"
class="text-xs cursor-pointer hover:bg-amber-50 dark:hover:bg-amber-950/30 border-amber-300 dark:border-amber-700 text-amber-700 dark:text-amber-400 gap-1"
class="text-xs cursor-pointer hover:bg-amber-50 dark:hover:bg-amber-950/30 border-amber-300 dark:border-amber-700 text-amber-700 dark:text-amber-400 gap-1 inline-flex"
>
<Recycle class="h-3 w-3" />
<span class="hidden sm:inline">{{ t('galaxyView.debris') }}</span>
<span>{{ t('galaxyView.debris') }}</span>
</Badge>
</PopoverTrigger>
<PopoverContent class="w-auto p-3" side="top" align="start">
@@ -250,59 +293,195 @@
</Popover>
</div>
</div>
<!-- PC端显示坐标 -->
<p class="text-xs text-muted-foreground hidden sm:block">
[{{ slot.planet.position.galaxy }}:{{ slot.planet.position.system }}:{{ slot.planet.position.position }}]
</p>
<!-- 好感度显示仅NPC星球 -->
<div v-if="!isMyPlanet(slot.planet) && getReputationValue(slot.planet) !== null" class="text-xs">
<span class="text-muted-foreground">{{ t('diplomacy.reputation') }}:</span>
<span class="ml-1 font-semibold" :class="getReputationColor(getReputationValue(slot.planet))">
{{ getReputationValue(slot.planet)! > 0 ? '+' : '' }}{{ getReputationValue(slot.planet) }}
</span>
</div>
</div>
<!-- 空位置 -->
<div v-else class="space-y-1">
<div class="text-sm text-muted-foreground">{{ t('galaxyView.emptySlot') }}</div>
<!-- 残骸场徽章 - 空位置时也显示 -->
<Popover v-if="getDebrisFieldAt(currentGalaxy, currentSystem, slot.position)">
<PopoverTrigger as-child>
<Badge
variant="outline"
class="text-xs cursor-pointer hover:bg-amber-50 dark:hover:bg-amber-950/30 border-amber-300 dark:border-amber-700 text-amber-700 dark:text-amber-400 gap-1 inline-flex"
>
<Recycle class="h-3 w-3" />
<span>{{ t('galaxyView.debris') }}</span>
</Badge>
</PopoverTrigger>
<PopoverContent class="w-auto p-3" side="top" align="start">
<div class="space-y-2">
<p class="text-xs font-semibold text-amber-700 dark:text-amber-400">{{ t('galaxyView.debrisField') }}</p>
<div class="space-y-1 text-xs">
<div class="flex items-center gap-2">
<ResourceIcon type="metal" size="sm" />
<span class="text-muted-foreground">{{ t('resources.metal') }}:</span>
<span class="font-medium">
{{ formatNumber(getDebrisFieldAt(currentGalaxy, currentSystem, slot.position)!.resources.metal) }}
</span>
</div>
<div class="flex items-center gap-2">
<ResourceIcon type="crystal" size="sm" />
<span class="text-muted-foreground">{{ t('resources.crystal') }}:</span>
<span class="font-medium">
{{ formatNumber(getDebrisFieldAt(currentGalaxy, currentSystem, slot.position)!.resources.crystal) }}
</span>
</div>
</div>
</div>
</PopoverContent>
</Popover>
<!-- 第三行:操作按钮 -->
<div class="flex gap-1 pl-10">
<TooltipProvider :delay-duration="300">
<Tooltip v-if="slot.planet && !isMyPlanet(slot.planet)">
<TooltipTrigger as-child>
<Button @click="showPlanetActions(slot.planet, 'spy')" variant="outline" size="sm" class="h-8 w-8 p-0">
<Eye class="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>{{ t('galaxyView.scout') }}</p>
</TooltipContent>
</Tooltip>
<Tooltip v-if="slot.planet && !isMyPlanet(slot.planet)">
<TooltipTrigger as-child>
<Button @click="showPlanetActions(slot.planet, 'attack')" variant="outline" size="sm" class="h-8 w-8 p-0">
<Sword class="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>{{ t('galaxyView.attack') }}</p>
</TooltipContent>
</Tooltip>
<Tooltip v-if="slot.planet && !isMyPlanet(slot.planet) && hasInterplanetaryMissiles">
<TooltipTrigger as-child>
<Button @click="showMissileAttackDialog(slot.planet)" variant="outline" size="sm" class="h-8 w-8 p-0">
<Bomb class="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>{{ t('galaxyView.missileAttack') }}</p>
</TooltipContent>
</Tooltip>
<Tooltip v-if="slot.planet && !isMyPlanet(slot.planet) && getPlanetNPC(slot.planet)">
<TooltipTrigger as-child>
<Button @click="showPlanetActions(slot.planet, 'gift')" variant="outline" size="sm" class="h-8 w-8 p-0">
<Gift class="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>{{ t('galaxyView.sendGift') }}</p>
</TooltipContent>
</Tooltip>
<Tooltip v-if="!slot.planet">
<TooltipTrigger as-child>
<Button @click="showPlanetActions(null, 'colonize', slot.position)" variant="outline" size="sm" class="h-8 w-8 p-0">
<Rocket class="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>{{ t('galaxyView.colonize') }}</p>
</TooltipContent>
</Tooltip>
<Tooltip v-if="slot.planet && isMyPlanet(slot.planet)">
<TooltipTrigger as-child>
<Button @click="switchToPlanet(slot.planet.id)" variant="outline" size="sm" class="h-8 w-8 p-0">
<Home class="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>{{ t('galaxyView.switch') }}</p>
</TooltipContent>
</Tooltip>
<Tooltip v-if="getDebrisFieldAt(currentGalaxy, currentSystem, slot.position)">
<TooltipTrigger as-child>
<Button
@click="showPlanetActions(slot.planet, 'recycle', slot.position)"
variant="outline"
size="sm"
class="h-8 w-8 p-0"
>
<Recycle class="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>{{ t('galaxyView.recycle') }}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
</div>
<!-- 操作按钮 -->
<div class="flex gap-1 sm:gap-2 flex-shrink-0">
<!-- PC端布局位置编号 + 星球信息(水平) -->
<div class="hidden sm:flex items-center gap-4 flex-1 min-w-0">
<!-- 位置编号 -->
<div class="w-12 text-center flex-shrink-0">
<Badge variant="outline" class="text-sm">{{ slot.position }}</Badge>
</div>
<!-- 星球信息 -->
<div class="flex-1 min-w-0">
<div v-if="slot.planet" class="space-y-1">
<!-- PC端标题和徽章 -->
<div class="flex items-center gap-2 flex-wrap">
<h3 class="font-semibold text-base">{{ slot.planet.name }}</h3>
<Badge v-if="isMyPlanet(slot.planet)" variant="default" class="text-xs">{{ t('galaxyView.mine') }}</Badge>
<Badge v-else :variant="getRelationBadgeVariant(slot.planet)" class="text-xs">
{{ getRelationStatusText(slot.planet) }}
</Badge>
<!-- 残骸场徽章 -->
<Popover v-if="getDebrisFieldAt(currentGalaxy, currentSystem, slot.position)">
<PopoverTrigger as-child>
<Badge
variant="outline"
class="text-xs cursor-pointer hover:bg-amber-50 dark:hover:bg-amber-950/30 border-amber-300 dark:border-amber-700 text-amber-700 dark:text-amber-400 gap-1"
>
<Recycle class="h-3 w-3" />
<span>{{ t('galaxyView.debris') }}</span>
</Badge>
</PopoverTrigger>
<PopoverContent class="w-auto p-3" side="top" align="start">
<div class="space-y-2">
<p class="text-xs font-semibold text-amber-700 dark:text-amber-400">{{ t('galaxyView.debrisField') }}</p>
<div class="space-y-1 text-xs">
<div class="flex items-center gap-2">
<ResourceIcon type="metal" size="sm" />
<span class="text-muted-foreground">{{ t('resources.metal') }}:</span>
<span class="font-medium">
{{ formatNumber(getDebrisFieldAt(currentGalaxy, currentSystem, slot.position)!.resources.metal) }}
</span>
</div>
<div class="flex items-center gap-2">
<ResourceIcon type="crystal" size="sm" />
<span class="text-muted-foreground">{{ t('resources.crystal') }}:</span>
<span class="font-medium">
{{ formatNumber(getDebrisFieldAt(currentGalaxy, currentSystem, slot.position)!.resources.crystal) }}
</span>
</div>
</div>
</div>
</PopoverContent>
</Popover>
</div>
<!-- PC端坐标 -->
<p class="text-xs text-muted-foreground">
[{{ slot.planet.position.galaxy }}:{{ slot.planet.position.system }}:{{ slot.planet.position.position }}]
</p>
<!-- PC端好感度显示仅NPC星球 -->
<div v-if="!isMyPlanet(slot.planet) && getReputationValue(slot.planet) !== null" class="text-xs">
<span class="text-muted-foreground">{{ t('diplomacy.reputation') }}:</span>
<span class="ml-1 font-semibold" :class="getReputationColor(getReputationValue(slot.planet))">
{{ getReputationValue(slot.planet)! > 0 ? '+' : '' }}{{ getReputationValue(slot.planet) }}
</span>
</div>
</div>
<!-- 空位置 -->
<div v-else class="space-y-1">
<div class="text-sm text-muted-foreground">{{ t('galaxyView.emptySlot') }}</div>
<!-- 残骸场徽章 - 空位置时也显示 -->
<Popover v-if="getDebrisFieldAt(currentGalaxy, currentSystem, slot.position)">
<PopoverTrigger as-child>
<Badge
variant="outline"
class="text-xs cursor-pointer hover:bg-amber-50 dark:hover:bg-amber-950/30 border-amber-300 dark:border-amber-700 text-amber-700 dark:text-amber-400 gap-1 inline-flex"
>
<Recycle class="h-3 w-3" />
<span>{{ t('galaxyView.debris') }}</span>
</Badge>
</PopoverTrigger>
<PopoverContent class="w-auto p-3" side="top" align="start">
<div class="space-y-2">
<p class="text-xs font-semibold text-amber-700 dark:text-amber-400">{{ t('galaxyView.debrisField') }}</p>
<div class="space-y-1 text-xs">
<div class="flex items-center gap-2">
<ResourceIcon type="metal" size="sm" />
<span class="text-muted-foreground">{{ t('resources.metal') }}:</span>
<span class="font-medium">
{{ formatNumber(getDebrisFieldAt(currentGalaxy, currentSystem, slot.position)!.resources.metal) }}
</span>
</div>
<div class="flex items-center gap-2">
<ResourceIcon type="crystal" size="sm" />
<span class="text-muted-foreground">{{ t('resources.crystal') }}:</span>
<span class="font-medium">
{{ formatNumber(getDebrisFieldAt(currentGalaxy, currentSystem, slot.position)!.resources.crystal) }}
</span>
</div>
</div>
</div>
</PopoverContent>
</Popover>
</div>
</div>
</div>
<!-- 操作按钮 (PC端) -->
<div class="hidden sm:flex gap-1 sm:gap-2 flex-shrink-0">
<TooltipProvider :delay-duration="300">
<Tooltip v-if="slot.planet && !isMyPlanet(slot.planet)">
<TooltipTrigger as-child>
@@ -324,6 +503,16 @@
<p>{{ t('galaxyView.attack') }}</p>
</TooltipContent>
</Tooltip>
<Tooltip v-if="slot.planet && !isMyPlanet(slot.planet) && hasInterplanetaryMissiles">
<TooltipTrigger as-child>
<Button @click="showMissileAttackDialog(slot.planet)" variant="outline" size="sm" class="h-8 w-8 p-0">
<Bomb class="h-3 w-3 sm:h-4 sm:w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>{{ t('galaxyView.missileAttack') }}</p>
</TooltipContent>
</Tooltip>
<Tooltip v-if="slot.planet && !isMyPlanet(slot.planet) && getPlanetNPC(slot.planet)">
<TooltipTrigger as-child>
<Button @click="showPlanetActions(slot.planet, 'gift')" variant="outline" size="sm" class="h-8 w-8 p-0">
@@ -376,6 +565,60 @@
</CardContent>
</Card>
<!-- 导弹攻击对话框 -->
<Dialog :open="missileDialogOpen" @update:open="missileDialogOpen = $event">
<DialogContent>
<DialogHeader>
<DialogTitle>{{ t('galaxyView.missileAttackTitle') }}</DialogTitle>
<DialogDescription v-if="missileTargetPlanet">
{{
t('galaxyView.missileAttackMessage').replace(
'{coordinates}',
`${missileTargetPlanet.position.galaxy}:${missileTargetPlanet.position.system}:${missileTargetPlanet.position.position}`
)
}}
</DialogDescription>
</DialogHeader>
<div v-if="gameStore.currentPlanet && missileTargetPlanet" class="space-y-4">
<!-- 导弹数量输入 -->
<div class="space-y-2">
<Label>{{ t('galaxyView.missileCount') }}</Label>
<Input
v-model.number="missileCount"
type="number"
min="1"
:max="gameStore.currentPlanet.defense['interplanetaryMissile'] || 0"
/>
<p class="text-sm text-muted-foreground">
{{ t('galaxyView.availableMissiles') }}: {{ gameStore.currentPlanet.defense['interplanetaryMissile'] || 0 }}
</p>
</div>
<!-- 射程和距离信息 -->
<div class="space-y-2 text-sm">
<div class="flex justify-between">
<span class="text-muted-foreground">{{ t('galaxyView.missileRange') }}:</span>
<span>{{ calculateMissileRange() }} {{ t('galaxyView.systems') }}</span>
</div>
<div class="flex justify-between">
<span class="text-muted-foreground">{{ t('galaxyView.distance') }}:</span>
<span>{{ calculateDistance(missileTargetPlanet) }} {{ t('galaxyView.systems') }}</span>
</div>
<div class="flex justify-between">
<span class="text-muted-foreground">{{ t('galaxyView.flightTime') }}:</span>
<span>{{ formatFlightTime(calculateDistance(missileTargetPlanet)) }}</span>
</div>
</div>
</div>
<DialogFooter>
<Button variant="outline" @click="missileDialogOpen = false">{{ t('galaxyView.cancel') }}</Button>
<Button @click="launchMissileAttack">{{ t('galaxyView.launchMissile') }}</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<!-- 快速派遣对话框 -->
<AlertDialog :open="alertDialogOpen" @update:open="alertDialogOpen = $event">
<AlertDialogContent>
@@ -405,10 +648,12 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Label } from '@/components/ui/label'
import { Input } from '@/components/ui/input'
import { Badge } from '@/components/ui/badge'
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '@/components/ui/select'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
import { Popover, PopoverTrigger, PopoverContent } from '@/components/ui/popover'
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
import {
AlertDialog,
AlertDialogAction,
@@ -420,7 +665,7 @@
AlertDialogTitle
} from '@/components/ui/alert-dialog'
import ResourceIcon from '@/components/ResourceIcon.vue'
import { Home, Eye, Sword, Rocket, Recycle, Gift, Globe } from 'lucide-vue-next'
import { Home, Eye, Sword, Rocket, Recycle, Gift, Globe, Bomb } from 'lucide-vue-next'
import { useRouter, useRoute } from 'vue-router'
import * as gameLogic from '@/logic/gameLogic'
import { formatNumber } from '@/utils/format'
@@ -438,6 +683,11 @@
const alertDialogMessage = ref('')
const alertDialogConfirmAction = ref<(() => void) | null>(null)
// 导弹攻击对话框状态
const missileDialogOpen = ref(false)
const missileTargetPlanet = ref<Planet | null>(null)
const missileCount = ref(1)
const selectedGalaxy = ref(1)
const selectedSystem = ref(1)
const currentGalaxy = ref(1)
@@ -465,6 +715,12 @@
return gameStore.player.planets.filter(p => !p.isMoon)
})
// 检查当前星球是否有星际导弹
const hasInterplanetaryMissiles = computed(() => {
if (!gameStore.currentPlanet) return false
return (gameStore.currentPlanet.defense['interplanetaryMissile'] || 0) > 0
})
// 判断当前是否在母星所在星系
const isInHomePlanetSystem = computed(() => {
if (!homePlanet.value) return false
@@ -680,4 +936,80 @@
}
alertDialogOpen.value = true
}
// 显示导弹攻击对话框
const showMissileAttackDialog = (planet: Planet) => {
missileTargetPlanet.value = planet
missileCount.value = 1
missileDialogOpen.value = true
}
// 执行导弹攻击
const launchMissileAttack = () => {
if (!missileTargetPlanet.value || !gameStore.currentPlanet) return
// 导入missileLogic进行验证和执行
import('@/logic/missileLogic').then(missileLogic => {
const validation = missileLogic.validateMissileLaunch(
gameStore.currentPlanet!,
missileTargetPlanet.value!.position,
missileCount.value,
gameStore.player.technologies
)
if (!validation.valid) {
alertDialogTitle.value = t('errors.launchFailed')
alertDialogMessage.value = t(validation.reason || 'errors.unknown')
alertDialogOpen.value = true
return
}
// 创建导弹攻击任务
const missileAttack = missileLogic.createMissileAttack(
gameStore.player.id,
gameStore.currentPlanet!,
missileTargetPlanet.value!.position,
missileTargetPlanet.value!.id,
missileCount.value
)
// 扣除导弹
missileLogic.executeMissileLaunch(gameStore.currentPlanet!, missileCount.value)
// 添加到玩家的导弹攻击列表
gameStore.player.missileAttacks.push(missileAttack)
// 关闭对话框
missileDialogOpen.value = false
// 显示成功提示
alertDialogTitle.value = t('common.success')
alertDialogMessage.value = t('galaxyView.missileLaunched')
alertDialogOpen.value = true
})
}
// 计算导弹射程
const calculateMissileRange = () => {
const impulseDriveLevel = gameStore.player.technologies['impulseDrive'] || 0
if (impulseDriveLevel === 0) return 0
return 5 * impulseDriveLevel - 1
}
// 计算到目标的距离
const calculateDistance = (target: Planet) => {
if (!gameStore.currentPlanet) return 0
const from = gameStore.currentPlanet.position
const to = target.position
if (from.galaxy !== to.galaxy) return Infinity
return Math.abs(from.system - to.system)
}
// 格式化飞行时间
const formatFlightTime = (distance: number) => {
const seconds = 30 + distance * 60
const minutes = Math.floor(seconds / 60)
const secs = seconds % 60
return `${minutes}:${secs.toString().padStart(2, '0')}`
}
</script>