feat: 实现远征任务事件与报告展示

新增远征任务事件逻辑,支持资源、暗物质、舰船发现及遭遇海盗/外星人等多种结果,并生成对应任务报告。MessagesView支持远征任务详情展示,包括获得资源、舰船及损失舰船。补充多语言包相关远征事件提示。
This commit is contained in:
谦君
2025-12-19 12:37:34 +08:00
parent 752cade67c
commit 20fb2bb6a4
13 changed files with 408 additions and 67 deletions

View File

@@ -9,7 +9,7 @@
},
"private": true,
"version": "1.4.0",
"buildDate": "2025/12/19 12:01:23",
"buildDate": "2025/12/19 12:29:46",
"main": "dist-electron/main.js",
"type": "module",
"scripts": {

View File

@@ -1048,6 +1048,60 @@
delete universeStore.planets[targetKey]
}
}
} else if (mission.missionType === MissionType.Expedition) {
// 处理远征任务
const expeditionResult = fleetLogic.processExpeditionArrival(mission)
// 生成远征任务报告
if (!gameStore.player.missionReports) {
gameStore.player.missionReports = []
}
// 根据事件类型生成不同的报告消息
let reportMessage = ''
let reportDetails: Record<string, unknown> = {}
switch (expeditionResult.eventType) {
case 'resources':
reportMessage = t('missionReports.expeditionResources')
reportDetails = { foundResources: expeditionResult.resources }
break
case 'darkMatter':
reportMessage = t('missionReports.expeditionDarkMatter')
reportDetails = { foundResources: expeditionResult.resources }
break
case 'fleet':
reportMessage = t('missionReports.expeditionFleet')
reportDetails = { foundFleet: expeditionResult.fleet }
break
case 'pirates':
reportMessage = expeditionResult.fleetLost
? t('missionReports.expeditionPiratesAttack')
: t('missionReports.expeditionPiratesEscaped')
reportDetails = expeditionResult.fleetLost ? { fleetLost: expeditionResult.fleetLost } : {}
break
case 'aliens':
reportMessage = expeditionResult.fleetLost
? t('missionReports.expeditionAliensAttack')
: t('missionReports.expeditionAliensEscaped')
reportDetails = expeditionResult.fleetLost ? { fleetLost: expeditionResult.fleetLost } : {}
break
default:
reportMessage = t('missionReports.expeditionNothing')
}
gameStore.player.missionReports.push({
id: `mission-report-${mission.id}`,
timestamp: Date.now(),
missionType: MissionType.Expedition,
originPlanetId: mission.originPlanetId,
originPlanetName,
targetPosition: mission.targetPosition,
success: expeditionResult.eventType !== 'nothing',
message: reportMessage,
details: reportDetails,
read: false
})
}
}

View File

@@ -11,17 +11,15 @@
</button>
<!-- 页码 - 椭圆形胶囊 -->
<div class="bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 border rounded-full py-2 px-3 shadow-lg flex items-center gap-1">
<div
class="bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 border rounded-full py-2 px-3 shadow-lg flex items-center gap-1"
>
<button
v-for="pageNum in pageNumbers"
:key="pageNum"
@click="emit('update:page', pageNum)"
class="h-8 min-w-8 px-2 rounded-full text-sm font-medium transition-colors"
:class="
pageNum === currentPage
? 'bg-primary text-primary-foreground'
: 'hover:bg-accent'
"
:class="pageNum === currentPage ? 'bg-primary text-primary-foreground' : 'hover:bg-accent'"
>
{{ pageNum }}
</button>
@@ -40,54 +38,54 @@
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { ChevronLeft, ChevronRight } from 'lucide-vue-next'
import { computed } from 'vue'
import { ChevronLeft, ChevronRight } from 'lucide-vue-next'
interface Props {
page: number
totalPages: number
maxVisible?: number
}
const props = withDefaults(defineProps<Props>(), {
maxVisible: 3
})
const emit = defineEmits<{
'update:page': [page: number]
}>()
const currentPage = computed(() => props.page)
// 生成页码列表 - 最多显示指定数量页码,不含省略号
const pageNumbers = computed(() => {
const pages: number[] = []
const { totalPages, maxVisible } = props
const current = currentPage.value
if (totalPages <= maxVisible) {
for (let i = 1; i <= totalPages; i++) {
pages.push(i)
}
} else {
let start = current - Math.floor(maxVisible / 2)
let end = current + Math.floor(maxVisible / 2)
// 边界调整
if (start < 1) {
start = 1
end = maxVisible
}
if (end > totalPages) {
end = totalPages
start = totalPages - maxVisible + 1
}
for (let i = start; i <= end; i++) {
pages.push(i)
}
interface Props {
page: number
totalPages: number
maxVisible?: number
}
return pages
})
const props = withDefaults(defineProps<Props>(), {
maxVisible: 3
})
const emit = defineEmits<{
'update:page': [page: number]
}>()
const currentPage = computed(() => props.page)
// 生成页码列表 - 最多显示指定数量页码,不含省略号
const pageNumbers = computed(() => {
const pages: number[] = []
const { totalPages, maxVisible } = props
const current = currentPage.value
if (totalPages <= maxVisible) {
for (let i = 1; i <= totalPages; i++) {
pages.push(i)
}
} else {
let start = current - Math.floor(maxVisible / 2)
let end = current + Math.floor(maxVisible / 2)
// 边界调整
if (start < 1) {
start = 1
end = maxVisible
}
if (end > totalPages) {
end = totalPages
start = totalPages - maxVisible + 1
}
for (let i = start; i <= end; i++) {
pages.push(i)
}
}
return pages
})
</script>

View File

@@ -68,7 +68,8 @@ export default {
buildingLevelZero: 'Gebäudelevel ist 0, kann nicht abgerissen werden',
researchQueueFull: 'Forschungsauftrag voll',
moonExists: 'Mond existiert bereits',
insufficientDebris: 'Unzureichendes Trümmerfeld'
insufficientDebris: 'Unzureichendes Trümmerfeld',
launchFailed: 'Start fehlgeschlagen'
},
nav: {
overview: 'Übersicht',
@@ -739,7 +740,19 @@ export default {
recycleSuccess: 'Recyclingmission erfolgreich abgeschlossen',
recycleFailed: 'Recyclingmission fehlgeschlagen, keine Trümmer am Zielort',
destroySuccess: 'Planetenzerstörungsmission erfolgreich ausgeführt',
destroyFailed: 'Planetenzerstörungsmission fehlgeschlagen'
destroyFailed: 'Planetenzerstörungsmission fehlgeschlagen',
missileAttackSuccess: 'Raketenangriff erfolgreich',
missileAttackFailed: 'Raketenangriff fehlgeschlagen, Zielplanet existiert nicht',
missileAttackIntercepted: 'Alle Raketen abgefangen',
hits: 'Treffer',
expeditionResources: 'Die Expedition hat Ressourcen gefunden!',
expeditionDarkMatter: 'Die Expedition hat Dunkle Materie gefunden!',
expeditionFleet: 'Die Expedition hat verlassene Schiffe gefunden!',
expeditionPiratesAttack: 'Die Expedition wurde von Piraten angegriffen und verlor einige Schiffe',
expeditionPiratesEscaped: 'Die Expedition traf auf Piraten, konnte aber entkommen',
expeditionAliensAttack: 'Die Expedition wurde von Aliens angegriffen und verlor einige Schiffe',
expeditionAliensEscaped: 'Die Expedition traf auf Aliens, konnte aber entkommen',
expeditionNothing: 'Die Expedition hat nichts gefunden'
},
simulatorView: {
title: 'Kampfsimulator',

View File

@@ -67,7 +67,8 @@ export default {
buildingLevelZero: 'Building level is 0, cannot demolish',
researchQueueFull: 'Research queue full',
moonExists: 'Moon already exists',
insufficientDebris: 'Insufficient debris field'
insufficientDebris: 'Insufficient debris field',
launchFailed: 'Launch failed'
},
nav: {
overview: 'Overview',
@@ -732,7 +733,19 @@ export default {
recycleSuccess: 'Recycling mission completed successfully',
recycleFailed: 'Recycling mission failed, no debris at target location',
destroySuccess: 'Planet destruction mission executed successfully',
destroyFailed: 'Planet destruction mission failed'
destroyFailed: 'Planet destruction mission failed',
missileAttackSuccess: 'Missile attack successful',
missileAttackFailed: 'Missile attack failed, target planet does not exist',
missileAttackIntercepted: 'All missiles intercepted',
hits: 'hits',
expeditionResources: 'The expedition found resources!',
expeditionDarkMatter: 'The expedition found dark matter!',
expeditionFleet: 'The expedition found abandoned ships!',
expeditionPiratesAttack: 'The expedition was attacked by pirates and lost some ships',
expeditionPiratesEscaped: 'The expedition encountered pirates but escaped successfully',
expeditionAliensAttack: 'The expedition was attacked by aliens and lost some ships',
expeditionAliensEscaped: 'The expedition encountered aliens but escaped successfully',
expeditionNothing: 'The expedition found nothing'
},
simulatorView: {
title: 'Battle Simulator',

View File

@@ -68,7 +68,8 @@ export default {
buildingLevelZero: '建物レベルが0のため、解体できません',
researchQueueFull: '研究キューが満杯です',
moonExists: '月は既に存在します',
insufficientDebris: '残骸フィールドが不足しています'
insufficientDebris: '残骸フィールドが不足しています',
launchFailed: '発射に失敗しました'
},
nav: {
overview: '概要',
@@ -732,7 +733,19 @@ export default {
recycleSuccess: '回収ミッションが正常に完了しました',
recycleFailed: '回収ミッションが失敗しました。目標位置にデブリがありません',
destroySuccess: '惑星破壊ミッションが正常に実行されました',
destroyFailed: '惑星破壊ミッションが失敗しました'
destroyFailed: '惑星破壊ミッションが失敗しました',
missileAttackSuccess: 'ミサイル攻撃成功',
missileAttackFailed: 'ミサイル攻撃失敗、目標惑星が存在しません',
missileAttackIntercepted: '全てのミサイルが迎撃されました',
hits: '命中',
expeditionResources: '探検隊が資源を発見しました!',
expeditionDarkMatter: '探検隊がダークマターを発見しました!',
expeditionFleet: '探検隊が廃棄された艦船を発見しました!',
expeditionPiratesAttack: '探検隊が海賊に襲撃され、一部の艦船を失いました',
expeditionPiratesEscaped: '探検隊が海賊に遭遇しましたが、無事に脱出しました',
expeditionAliensAttack: '探検隊がエイリアンに襲撃され、一部の艦船を失いました',
expeditionAliensEscaped: '探検隊がエイリアンに遭遇しましたが、無事に脱出しました',
expeditionNothing: '探検隊は何も発見できませんでした'
},
simulatorView: {
title: '戦闘シミュレーター',

View File

@@ -68,7 +68,8 @@ export default {
buildingLevelZero: '건물 레벨이 0이므로 철거할 수 없습니다',
researchQueueFull: '연구 대기열 가득 참',
moonExists: '위성이 이미 존재합니다',
insufficientDebris: '잔해장 부족'
insufficientDebris: '잔해장 부족',
launchFailed: '발사 실패'
},
nav: {
overview: '개요',
@@ -734,7 +735,19 @@ export default {
recycleSuccess: '회수 임무가 성공적으로 완료되었습니다',
recycleFailed: '회수 임무 실패, 목표 위치에 잔해가 없습니다',
destroySuccess: '행성 파괴 임무가 성공적으로 실행되었습니다',
destroyFailed: '행성 파괴 임무 실패'
destroyFailed: '행성 파괴 임무 실패',
missileAttackSuccess: '미사일 공격 성공',
missileAttackFailed: '미사일 공격 실패, 목표 행성이 존재하지 않습니다',
missileAttackIntercepted: '모든 미사일이 요격되었습니다',
hits: '명중',
expeditionResources: '탐험대가 자원을 발견했습니다!',
expeditionDarkMatter: '탐험대가 암흑 물질을 발견했습니다!',
expeditionFleet: '탐험대가 버려진 함선을 발견했습니다!',
expeditionPiratesAttack: '탐험대가 해적의 공격을 받아 일부 함선을 잃었습니다',
expeditionPiratesEscaped: '탐험대가 해적과 조우했지만 무사히 탈출했습니다',
expeditionAliensAttack: '탐험대가 외계인의 공격을 받아 일부 함선을 잃었습니다',
expeditionAliensEscaped: '탐험대가 외계인과 조우했지만 무사히 탈출했습니다',
expeditionNothing: '탐험대가 아무것도 발견하지 못했습니다'
},
simulatorView: {
title: '전투 시뮬레이터',

View File

@@ -68,7 +68,8 @@ export default {
buildingLevelZero: 'Уровень здания 0, нельзя снести',
researchQueueFull: 'Очередь исследований заполнена',
moonExists: 'Луна уже существует',
insufficientDebris: 'Недостаточно обломков'
insufficientDebris: 'Недостаточно обломков',
launchFailed: 'Запуск не удался'
},
nav: {
overview: 'Обзор',
@@ -740,7 +741,19 @@ export default {
recycleSuccess: 'Миссия переработки успешно завершена',
recycleFailed: 'Миссия переработки провалена, нет обломков в целевой позиции',
destroySuccess: 'Миссия уничтожения планеты успешно выполнена',
destroyFailed: 'Миссия уничтожения планеты провалена'
destroyFailed: 'Миссия уничтожения планеты провалена',
expeditionResources: 'Экспедиция обнаружила ресурсы!',
expeditionDarkMatter: 'Экспедиция обнаружила тёмную материю!',
expeditionFleet: 'Экспедиция обнаружила заброшенные корабли!',
expeditionPiratesAttack: 'Экспедиция попала в засаду пиратов, часть кораблей потеряна',
expeditionPiratesEscaped: 'Экспедиция столкнулась с пиратами, но успела скрыться',
expeditionAliensAttack: 'Экспедиция подверглась атаке инопланетян, часть кораблей потеряна',
expeditionAliensEscaped: 'Экспедиция столкнулась с инопланетянами, но успела скрыться',
expeditionNothing: 'Экспедиция ничего не обнаружила',
missileAttackSuccess: 'Ракетная атака успешна!',
missileAttackFailed: 'Ракетная атака провалена',
missileAttackIntercepted: 'Ракеты перехвачены',
hits: 'попаданий'
},
simulatorView: {
title: 'Симулятор боя',

View File

@@ -729,7 +729,15 @@ export default {
missileAttackSuccess: '导弹攻击成功',
missileAttackFailed: '导弹攻击失败,目标星球不存在',
missileAttackIntercepted: '所有导弹被拦截',
hits: '枚命中'
hits: '枚命中',
expeditionResources: '远征队发现了资源!',
expeditionDarkMatter: '远征队发现了暗物质!',
expeditionFleet: '远征队发现了废弃的舰船!',
expeditionPiratesAttack: '远征队遭遇海盗袭击,损失了部分舰船',
expeditionPiratesEscaped: '远征队遭遇海盗,但成功逃脱',
expeditionAliensAttack: '远征队遭遇外星人袭击,损失了部分舰船',
expeditionAliensEscaped: '远征队遭遇外星人,但成功逃脱',
expeditionNothing: '远征队什么也没有发现'
},
simulatorView: {
title: '战斗模拟器',

View File

@@ -68,7 +68,8 @@ export default {
buildingLevelZero: '建築等級為0無法拆除',
researchQueueFull: '研究隊列已滿',
moonExists: '已存在月球',
insufficientDebris: '殘骸場不足'
insufficientDebris: '殘骸場不足',
launchFailed: '發射失敗'
},
nav: {
overview: '總覽',
@@ -734,7 +735,19 @@ export default {
recycleSuccess: '回收任務成功完成',
recycleFailed: '回收任務失敗,目標位置沒有殘骸',
destroySuccess: '行星毀滅任務成功執行',
destroyFailed: '行星毀滅任務失敗'
destroyFailed: '行星毀滅任務失敗',
expeditionResources: '探險隊發現了資源!',
expeditionDarkMatter: '探險隊發現了暗物質!',
expeditionFleet: '探險隊發現了廢棄的艦船!',
expeditionPiratesAttack: '探險隊遭遇海盜襲擊,損失了部分艦船',
expeditionPiratesEscaped: '探險隊遭遇海盜,但成功逃脫',
expeditionAliensAttack: '探險隊遭遇外星人襲擊,損失了部分艦船',
expeditionAliensEscaped: '探險隊遭遇外星人,但成功逃脫',
expeditionNothing: '探險隊什麼也沒有發現',
missileAttackSuccess: '導彈攻擊成功!',
missileAttackFailed: '導彈攻擊失敗',
missileAttackIntercepted: '導彈被攔截',
hits: '命中'
},
simulatorView: {
title: '戰鬥模擬器',

View File

@@ -639,6 +639,162 @@ export const processRecycleArrival = (
}
}
/**
* 远征事件类型
*/
export type ExpeditionEventType = 'resources' | 'darkMatter' | 'fleet' | 'nothing' | 'pirates' | 'aliens'
/**
* 远征结果
*/
export interface ExpeditionResult {
eventType: ExpeditionEventType
resources?: Partial<Resources>
fleet?: Partial<Fleet>
fleetLost?: Partial<Fleet>
message: string
}
/**
* 处理远征任务到达
* 远征任务会随机触发各种事件
*/
export const processExpeditionArrival = (mission: FleetMission): ExpeditionResult => {
// 计算舰队总货舱容量
let totalCargoCapacity = 0
for (const [shipType, count] of Object.entries(mission.fleet)) {
if (count > 0) {
const shipConfig = getShipCargoCapacity(shipType as ShipType)
totalCargoCapacity += shipConfig * count
}
}
// 随机事件概率
const random = Math.random() * 100
let result: ExpeditionResult
if (random < 30) {
// 30% 概率发现资源
const resourceMultiplier = 0.1 + Math.random() * 0.3 // 10%-40% 的货舱容量
const resourceAmount = Math.floor(totalCargoCapacity * resourceMultiplier)
const metalAmount = Math.floor(resourceAmount * 0.5)
const crystalAmount = Math.floor(resourceAmount * 0.35)
const deuteriumAmount = Math.floor(resourceAmount * 0.15)
mission.cargo.metal += metalAmount
mission.cargo.crystal += crystalAmount
mission.cargo.deuterium += deuteriumAmount
result = {
eventType: 'resources',
resources: { metal: metalAmount, crystal: crystalAmount, deuterium: deuteriumAmount, darkMatter: 0, energy: 0 },
message: 'expedition.foundResources'
}
} else if (random < 40) {
// 10% 概率发现暗物质
const darkMatterAmount = Math.floor(50 + Math.random() * 150) // 50-200 暗物质
mission.cargo.darkMatter += darkMatterAmount
result = {
eventType: 'darkMatter',
resources: { metal: 0, crystal: 0, deuterium: 0, darkMatter: darkMatterAmount, energy: 0 },
message: 'expedition.foundDarkMatter'
}
} else if (random < 55) {
// 15% 概率发现废弃舰船
const foundFleet: Partial<Fleet> = {}
const possibleShips: ShipType[] = [ShipType.LightFighter, ShipType.HeavyFighter, ShipType.SmallCargo, ShipType.LargeCargo]
const shipTypeIndex = Math.floor(Math.random() * possibleShips.length)
const shipType = possibleShips[shipTypeIndex] ?? ShipType.LightFighter
const count = Math.floor(1 + Math.random() * 5) // 1-5 艘
foundFleet[shipType] = count
// 将发现的舰船添加到任务舰队中
mission.fleet[shipType] = (mission.fleet[shipType] || 0) + count
result = {
eventType: 'fleet',
fleet: foundFleet,
message: 'expedition.foundFleet'
}
} else if (random < 70) {
// 15% 概率遭遇海盗(损失部分舰队)
const fleetLost: Partial<Fleet> = {}
let hasLoss = false
for (const [shipType, count] of Object.entries(mission.fleet)) {
if (count > 0 && Math.random() < 0.3) {
// 30% 概率损失该类型舰船
const lossCount = Math.max(1, Math.floor(count * 0.1)) // 损失10%最少1艘
const actualLoss = Math.min(lossCount, count)
fleetLost[shipType as ShipType] = actualLoss
mission.fleet[shipType as ShipType] = count - actualLoss
hasLoss = true
}
}
result = {
eventType: 'pirates',
fleetLost: hasLoss ? fleetLost : undefined,
message: hasLoss ? 'expedition.piratesAttack' : 'expedition.piratesEscaped'
}
} else if (random < 80) {
// 10% 概率遭遇外星人(损失更多舰队)
const fleetLost: Partial<Fleet> = {}
let hasLoss = false
for (const [shipType, count] of Object.entries(mission.fleet)) {
if (count > 0 && Math.random() < 0.5) {
// 50% 概率损失该类型舰船
const lossCount = Math.max(1, Math.floor(count * 0.2)) // 损失20%最少1艘
const actualLoss = Math.min(lossCount, count)
fleetLost[shipType as ShipType] = actualLoss
mission.fleet[shipType as ShipType] = count - actualLoss
hasLoss = true
}
}
result = {
eventType: 'aliens',
fleetLost: hasLoss ? fleetLost : undefined,
message: hasLoss ? 'expedition.aliensAttack' : 'expedition.aliensEscaped'
}
} else {
// 20% 概率什么都没发现
result = {
eventType: 'nothing',
message: 'expedition.nothing'
}
}
mission.status = 'returning'
return result
}
/**
* 获取舰船货舱容量
*/
const getShipCargoCapacity = (shipType: ShipType): number => {
const cargoCapacities: Record<ShipType, number> = {
[ShipType.LightFighter]: 50,
[ShipType.HeavyFighter]: 100,
[ShipType.Cruiser]: 800,
[ShipType.Battleship]: 1500,
[ShipType.Battlecruiser]: 750,
[ShipType.Bomber]: 500,
[ShipType.Destroyer]: 2000,
[ShipType.SmallCargo]: 5000,
[ShipType.LargeCargo]: 25000,
[ShipType.ColonyShip]: 7500,
[ShipType.Recycler]: 20000,
[ShipType.EspionageProbe]: 5,
[ShipType.SolarSatellite]: 0,
[ShipType.DarkMatterHarvester]: 1000,
[ShipType.Deathstar]: 1000000
}
return cargoCapacities[shipType] || 0
}
/**
* 计算行星毁灭概率
*/

View File

@@ -430,6 +430,12 @@ export interface MissionReport {
missileHits?: number
missileIntercepted?: number
defenseLosses?: Partial<Record<DefenseType, number>>
// 探险任务:发现的资源
foundResources?: Partial<Resources>
// 探险任务:发现的舰船
foundFleet?: Partial<Fleet>
// 探险任务:损失的舰船
fleetLost?: Partial<Fleet>
}
read?: boolean
}

View File

@@ -525,6 +525,43 @@
</div>
</div>
</div>
<!-- 探险任务详情 - 发现资源 -->
<div v-if="selectedMissionReport.details?.foundResources" class="mt-3 space-y-1">
<p class="text-xs font-semibold text-muted-foreground">{{ t('messagesView.resources') }}:</p>
<div class="grid grid-cols-2 gap-2 text-sm p-2 bg-green-50 dark:bg-green-950/30 rounded">
<div v-for="res in allResourceFields" :key="res.key">
<template v-if="(selectedMissionReport.details?.foundResources?.[res.key] ?? 0) > 0">
<span class="text-muted-foreground">{{ t(`resources.${res.key}`) }}:</span>
<span class="ml-1 font-medium text-green-600 dark:text-green-400">
+{{ (selectedMissionReport.details?.foundResources?.[res.key] ?? 0).toLocaleString() }}
</span>
</template>
</div>
</div>
</div>
<!-- 探险任务详情 - 发现舰船 -->
<div v-if="selectedMissionReport.details?.foundFleet" class="mt-3 space-y-1">
<p class="text-xs font-semibold text-muted-foreground">{{ t('messagesView.fleet') }}:</p>
<div class="grid grid-cols-2 gap-2 text-sm p-2 bg-blue-50 dark:bg-blue-950/30 rounded">
<div v-for="(count, shipType) in selectedMissionReport.details.foundFleet" :key="shipType">
<span class="text-muted-foreground">{{ t('ships.' + shipType) }}:</span>
<span class="ml-1 font-medium text-blue-600 dark:text-blue-400">+{{ count }}</span>
</div>
</div>
</div>
<!-- 探险任务详情 - 损失舰船 -->
<div v-if="selectedMissionReport.details?.fleetLost" class="mt-3 space-y-1">
<p class="text-xs font-semibold text-muted-foreground">{{ t('messagesView.attackerLosses') }}:</p>
<div class="grid grid-cols-2 gap-2 text-sm p-2 bg-red-50 dark:bg-red-950/30 rounded">
<div v-for="(count, shipType) in selectedMissionReport.details.fleetLost" :key="shipType">
<span class="text-muted-foreground">{{ t('ships.' + shipType) }}:</span>
<span class="ml-1 font-medium text-red-600 dark:text-red-400">-{{ count }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -706,6 +743,10 @@
type DebrisResourceKey = 'metal' | 'crystal'
const debrisResourceFields: { key: DebrisResourceKey }[] = [{ key: 'metal' }, { key: 'crystal' }]
// 全部资源字段配置(包含暗物质,用于探险任务)
type AllResourceKey = 'metal' | 'crystal' | 'deuterium' | 'darkMatter'
const allResourceFields: { key: AllResourceKey }[] = [{ key: 'metal' }, { key: 'crystal' }, { key: 'deuterium' }, { key: 'darkMatter' }]
const hasSelectedAny = computed(() => {
return Object.values(clearOptions.value).some(v => v)
})