feat: 新增NPC与外交逻辑,优化UI组件结构

重构并精简了部分UI组件,移除冗余弹窗与详情组件,新增NPC相关逻辑(npcBehaviorLogic、npcGrowthLogic、npcStore等)及外交逻辑(diplomaticLogic、DiplomacyView)。完善分页、标签、复选框等通用UI组件。优化战报弹窗,调整README下载链接为相对路径,修复部分国际化内容。
This commit is contained in:
谦君
2025-12-15 08:23:45 +08:00
parent 44580909a3
commit 9b9fda0400
164 changed files with 18628 additions and 2775 deletions

View File

@@ -8,12 +8,18 @@
<CardTitle>{{ t('galaxyView.selectCoordinates') }}</CardTitle>
</CardHeader>
<CardContent>
<div class="grid grid-cols-2 sm:grid-cols-3 gap-3 sm:gap-4">
<div
:class="[
'grid gap-3 sm:gap-4',
highlightedNpc ? 'grid-cols-2 sm:grid-cols-4' : isInHomePlanetSystem ? 'grid-cols-2' : 'grid-cols-2 sm:grid-cols-3'
]"
>
<div class="space-y-2">
<Label for="select-galaxy" class="text-xs sm:text-sm">{{ t('galaxyView.galaxy') }}</Label>
<Select
:key="gameStore.locale"
:model-value="String(selectedGalaxy)"
:modal="false"
@update:model-value="
val => {
selectedGalaxy = Number(val)
@@ -24,7 +30,7 @@
<SelectTrigger id="select-galaxy" class="w-full">
<SelectValue :placeholder="t('galaxyView.selectGalaxy')" />
</SelectTrigger>
<SelectContent>
<SelectContent position="popper">
<SelectItem v-for="g in 9" :key="g" :value="String(g)">{{ t('galaxyView.galaxy') }} {{ g }}</SelectItem>
</SelectContent>
</Select>
@@ -34,6 +40,7 @@
<Select
:key="`${gameStore.locale}-system`"
:model-value="String(selectedSystem)"
:modal="false"
@update:model-value="
val => {
selectedSystem = Number(val)
@@ -44,16 +51,101 @@
<SelectTrigger id="select-system" class="w-full">
<SelectValue :placeholder="t('galaxyView.selectSystem')" />
</SelectTrigger>
<SelectContent>
<SelectContent position="popper">
<SelectItem v-for="s in 10" :key="s" :value="String(s)">{{ t('galaxyView.system') }} {{ s }}</SelectItem>
</SelectContent>
</Select>
</div>
<div class="col-span-2 sm:col-span-1 flex items-end">
<Button @click="goToCurrentPlanet" variant="outline" class="w-full">
<Home class="h-4 w-4 mr-2" />
{{ t('galaxyView.myPlanet') }}
</Button>
<div v-if="!isInHomePlanetSystem" :class="highlightedNpc ? '' : 'col-span-2 sm:col-span-1'" class="space-y-2">
<Label class="text-xs sm:text-sm opacity-0">{{ t('galaxyView.myPlanets') }}</Label>
<!-- 不在母星星系时显示Popover选择 -->
<Popover>
<PopoverTrigger as-child>
<Button variant="outline" class="w-full">
<Home class="h-4 w-4 mr-2" />
{{ t('galaxyView.myPlanets') }}
</Button>
</PopoverTrigger>
<PopoverContent class="w-72 p-2" align="start">
<div class="space-y-1">
<div class="px-2 py-1.5 text-xs font-semibold text-muted-foreground">
{{ t('galaxyView.selectPlanetToView') }}
</div>
<Button
v-for="p in myPlanets"
:key="p.id"
@click="goToPlanet(p)"
:disabled="p.position.galaxy === currentGalaxy && p.position.system === currentSystem"
variant="ghost"
:class="[
'w-full justify-start h-auto py-2 px-2 text-left',
p.position.galaxy === currentGalaxy &&
p.position.system === currentSystem &&
'bg-blue-100 dark:bg-blue-950/50 border border-blue-400 dark:border-blue-600'
]"
size="sm"
>
<div class="flex items-start gap-2 w-full min-w-0">
<Globe class="h-4 w-4 flex-shrink-0 mt-0.5" />
<div class="flex-1 min-w-0">
<div class="flex items-center gap-1.5 mb-0.5">
<span class="truncate font-medium text-sm">{{ p.name }}</span>
<Badge v-if="p.isMoon" variant="outline" class="text-[10px] px-1 py-0 h-4">
{{ t('planet.moon') }}
</Badge>
</div>
<div class="text-[11px] text-muted-foreground">
[{{ p.position.galaxy }}:{{ p.position.system }}:{{ p.position.position }}]
</div>
</div>
</div>
</Button>
</div>
</PopoverContent>
</Popover>
</div>
<!-- NPC星球列表 -->
<div v-if="highlightedNpc" :class="isInHomePlanetSystem ? 'col-span-2 sm:col-span-2' : ''" class="space-y-2">
<Label class="text-xs sm:text-sm opacity-0">{{ t('galaxyView.npcPlanets') }}</Label>
<Popover>
<PopoverTrigger as-child>
<Button variant="outline" class="w-full border-yellow-400 dark:border-yellow-600">
<Globe class="h-4 w-4 mr-2" />
{{ highlightedNpc.name }} ({{ highlightedNpc.planets.length }})
</Button>
</PopoverTrigger>
<PopoverContent class="w-72 p-2" align="start">
<div class="space-y-1">
<div class="px-2 py-1.5 text-xs font-semibold text-muted-foreground">
{{ t('galaxyView.selectPlanetToView') }}
</div>
<Button
v-for="p in highlightedNpc.planets"
:key="p.id"
@click="goToPlanet(p)"
:disabled="p.position.galaxy === currentGalaxy && p.position.system === currentSystem"
variant="ghost"
:class="[
'w-full justify-start h-auto py-2 px-2 text-left',
p.position.galaxy === currentGalaxy &&
p.position.system === currentSystem &&
'bg-yellow-100 dark:bg-yellow-950/50 border border-yellow-400 dark:border-yellow-600'
]"
size="sm"
>
<div class="flex items-start gap-2 w-full min-w-0">
<Globe class="h-4 w-4 flex-shrink-0 mt-0.5" />
<div class="flex-1 min-w-0">
<div class="truncate font-medium text-sm mb-0.5">{{ p.name }}</div>
<div class="text-[11px] text-muted-foreground">
[{{ p.position.galaxy }}:{{ p.position.system }}:{{ p.position.position }}]
</div>
</div>
</div>
</Button>
</div>
</PopoverContent>
</Popover>
</div>
</div>
</CardContent>
@@ -72,8 +164,32 @@
: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="{
'bg-blue-50 dark:bg-blue-950 border-blue-300 dark:border-blue-700': isMyPlanet(slot.planet),
'bg-muted/30': !slot.planet
// 空位置
'bg-muted/30': !slot.planet,
// 我的星球 - 蓝色
'bg-blue-50 dark:bg-blue-950 border-blue-300 dark:border-blue-700': slot.planet && isMyPlanet(slot.planet),
// 高亮NPC - 黄色
'bg-yellow-50 dark:bg-yellow-950/30 border-yellow-400 dark:border-yellow-600 ring-2 ring-yellow-400 dark:ring-yellow-500':
slot.planet && isHighlightedNpcPlanet(slot.planet) && !isMyPlanet(slot.planet),
// 友好NPC - 绿色
'bg-green-50 dark:bg-green-950/30 border-green-300 dark:border-green-700':
slot.planet &&
!isMyPlanet(slot.planet) &&
!isHighlightedNpcPlanet(slot.planet) &&
getRelation(slot.planet)?.status === RelationStatus.Friendly,
// 敌对NPC - 红色
'bg-red-50 dark:bg-red-950/30 border-red-300 dark:border-red-700':
slot.planet &&
!isMyPlanet(slot.planet) &&
!isHighlightedNpcPlanet(slot.planet) &&
getRelation(slot.planet)?.status === RelationStatus.Hostile,
// 中立NPC - 灰色
'bg-gray-50 dark:bg-gray-950/30 border-gray-300 dark:border-gray-700':
slot.planet &&
!isMyPlanet(slot.planet) &&
!isHighlightedNpcPlanet(slot.planet) &&
getPlanetNPC(slot.planet) &&
(!getRelation(slot.planet) || getRelation(slot.planet)?.status === RelationStatus.Neutral)
}"
>
<!-- 位置编号 -->
@@ -84,32 +200,104 @@
<!-- 星球信息 -->
<div class="flex-1 min-w-0">
<div v-if="slot.planet" class="space-y-1">
<div class="flex items-center gap-2">
<h3 class="font-semibold text-sm sm:text-base truncate">{{ slot.planet.name }}</h3>
<Badge v-if="isMyPlanet(slot.planet)" variant="default" class="text-xs">{{ t('galaxyView.mine') }}</Badge>
<Badge v-else variant="secondary" class="text-xs">{{ t('galaxyView.hostile') }}</Badge>
<!-- 移动端:垂直布局 / 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>
<!-- 徽章组 -->
<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>
<!-- 残骸场徽章 - 紧凑显示 -->
<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 class="hidden sm:inline">{{ 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>
<p class="text-xs text-muted-foreground">
<!-- 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="text-sm text-muted-foreground">{{ t('galaxyView.emptySlot') }}</div>
<!-- 残骸场信息 -->
<div v-if="getDebrisFieldAt(currentGalaxy, currentSystem, slot.position)" class="mt-2 p-2 bg-amber-50 dark:bg-amber-950/30 border border-amber-200 dark:border-amber-800 rounded text-xs">
<div class="flex items-center gap-2 text-amber-700 dark:text-amber-400 font-medium mb-1">
<span>{{ t('galaxyView.debrisField') }}</span>
</div>
<div class="flex gap-3 text-xs">
<span class="flex items-center gap-1">
<span class="text-muted-foreground">{{ t('resources.metal') }}:</span>
<span class="font-medium">{{ formatNumber(getDebrisFieldAt(currentGalaxy, currentSystem, slot.position)!.resources.metal) }}</span>
</span>
<span class="flex items-center gap-1">
<span class="text-muted-foreground">{{ t('resources.crystal') }}:</span>
<span class="font-medium">{{ formatNumber(getDebrisFieldAt(currentGalaxy, currentSystem, slot.position)!.resources.crystal) }}</span>
</span>
</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>
@@ -136,6 +324,16 @@
<p>{{ t('galaxyView.attack') }}</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 sm:h-4 sm:w-4" />
</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">
@@ -158,7 +356,12 @@
</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">
<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 sm:h-4 sm:w-4" />
</Button>
</TooltipTrigger>
@@ -174,44 +377,122 @@
</Card>
<!-- 快速派遣对话框 -->
<AlertDialog ref="actionDialog" />
<AlertDialog :open="alertDialogOpen" @update:open="alertDialogOpen = $event">
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>{{ alertDialogTitle }}</AlertDialogTitle>
<AlertDialogDescription class="whitespace-pre-line">
{{ alertDialogMessage }}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>{{ t('common.cancel') }}</AlertDialogCancel>
<AlertDialogAction @click="handleAlertDialogConfirm">{{ t('common.confirm') }}</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
</template>
<script setup lang="ts">
import { useGameStore } from '@/stores/gameStore'
import { useUniverseStore } from '@/stores/universeStore'
import { useNPCStore } from '@/stores/npcStore'
import { useI18n } from '@/composables/useI18n'
import { ref, onMounted } from 'vue'
import { ref, onMounted, computed } from 'vue'
import type { Planet, DebrisField } from '@/types/game'
import { RelationStatus } from '@/types/game'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Label } from '@/components/ui/label'
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 AlertDialog from '@/components/AlertDialog.vue'
import { Home, Eye, Sword, Rocket, Recycle } from 'lucide-vue-next'
import { useRouter } from 'vue-router'
import { Popover, PopoverTrigger, PopoverContent } from '@/components/ui/popover'
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
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 { useRouter, useRoute } from 'vue-router'
import * as gameLogic from '@/logic/gameLogic'
import { formatNumber } from '@/utils/format'
const gameStore = useGameStore()
const universeStore = useUniverseStore()
const npcStore = useNPCStore()
const router = useRouter()
const route = useRoute()
const { t } = useI18n()
const actionDialog = ref<InstanceType<typeof AlertDialog> | null>(null)
// AlertDialog 状态
const alertDialogOpen = ref(false)
const alertDialogTitle = ref('')
const alertDialogMessage = ref('')
const alertDialogConfirmAction = ref<(() => void) | null>(null)
const selectedGalaxy = ref(1)
const selectedSystem = ref(1)
const currentGalaxy = ref(1)
const currentSystem = ref(1)
// 保存要高亮的NPC ID从URL参数初始化之后即使URL清除也保持
const highlightNpcId = ref<string | undefined>(undefined)
// 获取要高亮的NPC对象
const highlightedNpc = computed(() => {
if (!highlightNpcId.value) return null
return npcStore.npcs.find(n => n.id === highlightNpcId.value) || null
})
const systemSlots = ref<Array<{ position: number; planet: Planet | null }>>([])
// 获取玩家的母星
const homePlanet = computed(() => {
// 第一个非月球星球就是母星
return gameStore.player.planets.find(p => !p.isMoon)
})
// 获取玩家所有非月球星球
const myPlanets = computed(() => {
return gameStore.player.planets.filter(p => !p.isMoon)
})
// 判断当前是否在母星所在星系
const isInHomePlanetSystem = computed(() => {
if (!homePlanet.value) return false
return currentGalaxy.value === homePlanet.value.position.galaxy && currentSystem.value === homePlanet.value.position.system
})
onMounted(() => {
// 默认显示当前星球所在的星系
if (gameStore.currentPlanet) {
// 从URL参数中读取并保存高亮NPC ID
if (route.query.highlightNpc) {
highlightNpcId.value = route.query.highlightNpc as string
}
// 优先检查URL参数中的星系坐标
const queryGalaxy = route.query.galaxy ? Number(route.query.galaxy) : null
const querySystem = route.query.system ? Number(route.query.system) : null
if (queryGalaxy && querySystem) {
// 如果URL中有坐标参数跳转到指定星系
currentGalaxy.value = queryGalaxy
currentSystem.value = querySystem
selectedGalaxy.value = queryGalaxy
selectedSystem.value = querySystem
loadSystem()
// 立即清除URL参数但保持本地变量中的highlightNpcId
clearUrlParams()
} else if (gameStore.currentPlanet) {
// 否则默认显示当前星球所在的星系
currentGalaxy.value = gameStore.currentPlanet.position.galaxy
currentSystem.value = gameStore.currentPlanet.position.system
selectedGalaxy.value = currentGalaxy.value
@@ -225,11 +506,12 @@
return positions.map(pos => {
const key = gameLogic.generatePositionKey(galaxy, system, pos.position)
// 先从玩家星球中查找,再从宇宙地图中查找
const planet = gameStore.player.planets.find(p =>
p.position.galaxy === galaxy &&
p.position.system === system &&
p.position.position === pos.position
) || universeStore.planets[key] || null
const planet =
gameStore.player.planets.find(
p => p.position.galaxy === galaxy && p.position.system === system && p.position.position === pos.position
) ||
universeStore.planets[key] ||
null
return { position: pos.position, planet }
})
}
@@ -240,6 +522,13 @@
return universeStore.debrisFields[debrisId] || null
}
// 清除URL参数
const clearUrlParams = () => {
if (route.query.highlightNpc || route.query.galaxy || route.query.system) {
router.replace({ query: {} })
}
}
// 加载星系
const loadSystem = () => {
currentGalaxy.value = selectedGalaxy.value
@@ -247,15 +536,13 @@
systemSlots.value = getSystemPlanets(currentGalaxy.value, currentSystem.value)
}
// 跳转到当前星球
const goToCurrentPlanet = () => {
if (gameStore.currentPlanet) {
currentGalaxy.value = gameStore.currentPlanet.position.galaxy
currentSystem.value = gameStore.currentPlanet.position.system
selectedGalaxy.value = currentGalaxy.value
selectedSystem.value = currentSystem.value
loadSystem()
}
// 跳转到指定星球的星系
const goToPlanet = (planet: Planet) => {
currentGalaxy.value = planet.position.galaxy
currentSystem.value = planet.position.system
selectedGalaxy.value = currentGalaxy.value
selectedSystem.value = currentSystem.value
systemSlots.value = getSystemPlanets(currentGalaxy.value, currentSystem.value)
}
// 判断是否为我的星球
@@ -264,14 +551,95 @@
return planet.ownerId === gameStore.player.id
}
// 判断星球是否属于高亮显示的NPC
const isHighlightedNpcPlanet = (planet: Planet | null): boolean => {
if (!planet || !highlightNpcId.value) return false
const npc = npcStore.npcs.find(n => n.id === highlightNpcId.value)
if (!npc) return false
return npc.planets.some(p => p.id === planet.id)
}
// 获取星球所属的NPC
const getPlanetNPC = (planet: Planet | null) => {
if (!planet || isMyPlanet(planet)) return null
// 通过坐标匹配而不是ID因为universeStore中的星球和npcStore中的星球可能是不同的对象
return npcStore.npcs.find(npc =>
npc.planets.some(
p =>
p.position.galaxy === planet.position.galaxy &&
p.position.system === planet.position.system &&
p.position.position === planet.position.position
)
)
}
// 获取外交关系
const getRelation = (planet: Planet | null) => {
const npc = getPlanetNPC(planet)
if (!npc) return null
return gameStore.player.diplomaticRelations?.[npc.id]
}
// 获取关系状态Badge样式
const getRelationBadgeVariant = (planet: Planet | null) => {
const relation = getRelation(planet)
if (!relation) return 'secondary'
switch (relation.status) {
case RelationStatus.Friendly:
return 'default'
case RelationStatus.Hostile:
return 'destructive'
default:
return 'secondary'
}
}
// 获取关系状态文本
const getRelationStatusText = (planet: Planet | null) => {
const relation = getRelation(planet)
if (!relation) return t('diplomacy.status.neutral')
switch (relation.status) {
case RelationStatus.Friendly:
return t('diplomacy.status.friendly')
case RelationStatus.Hostile:
return t('diplomacy.status.hostile')
default:
return t('diplomacy.status.neutral')
}
}
// 获取好感度值
const getReputationValue = (planet: Planet | null): number | null => {
const relation = getRelation(planet)
return relation?.reputation ?? null
}
// 获取好感度颜色
const getReputationColor = (reputation: number | null) => {
if (reputation === null) return 'text-muted-foreground'
if (reputation >= 20) return 'text-green-600 dark:text-green-400'
if (reputation <= -20) return 'text-red-600 dark:text-red-400'
return 'text-muted-foreground'
}
// 切换到指定星球
const switchToPlanet = (planetId: string) => {
gameStore.currentPlanetId = planetId
router.push('/')
}
// AlertDialog 确认处理
const handleAlertDialogConfirm = () => {
if (alertDialogConfirmAction.value) {
alertDialogConfirmAction.value()
}
alertDialogOpen.value = false
}
// 显示星球操作
const showPlanetActions = (planet: Planet | null, action: 'spy' | 'attack' | 'colonize' | 'recycle', position?: number) => {
const showPlanetActions = (planet: Planet | null, action: 'spy' | 'attack' | 'colonize' | 'recycle' | 'gift', position?: number) => {
const targetPos = planet ? planet.position : { galaxy: currentGalaxy.value, system: currentSystem.value, position: position! }
const coordinates = `${targetPos.galaxy}:${targetPos.system}:${targetPos.position}`
@@ -289,23 +657,27 @@
} else if (action === 'recycle') {
title = t('galaxyView.recyclePlanetTitle')
message = t('galaxyView.recyclePlanetMessage').replace('{coordinates}', coordinates)
} else if (action === 'gift') {
title = t('galaxyView.giftPlanetTitle')
message = t('galaxyView.giftPlanetMessage').replace('{coordinates}', coordinates)
}
actionDialog.value?.show({
title,
message,
onConfirm: () => {
// 跳转到舰队页面并填充目标坐标
router.push({
path: '/fleet',
query: {
galaxy: targetPos.galaxy,
system: targetPos.system,
position: targetPos.position,
mission: action
}
})
}
})
// 设置对话框状态
alertDialogTitle.value = title
alertDialogMessage.value = message
alertDialogConfirmAction.value = () => {
// 跳转到舰队页面并填充目标坐标
router.push({
path: '/fleet',
query: {
galaxy: targetPos.galaxy,
system: targetPos.system,
position: targetPos.position,
mission: action === 'gift' ? undefined : action,
gift: action === 'gift' ? '1' : undefined
}
})
}
alertDialogOpen.value = true
}
</script>