mirror of
https://github.com/setube/ogame-vue-ts.git
synced 2026-05-12 07:55:11 +08:00
feat: 新增NPC与外交逻辑,优化UI组件结构
重构并精简了部分UI组件,移除冗余弹窗与详情组件,新增NPC相关逻辑(npcBehaviorLogic、npcGrowthLogic、npcStore等)及外交逻辑(diplomaticLogic、DiplomacyView)。完善分页、标签、复选框等通用UI组件。优化战报弹窗,调整README下载链接为相对路径,修复部分国际化内容。
This commit is contained in:
@@ -1,131 +1,137 @@
|
||||
<template>
|
||||
<div class="container mx-auto p-4 sm:p-6 space-y-6">
|
||||
<h1 class="text-2xl sm:text-3xl font-bold">{{ t('simulatorView.title') }}</h1>
|
||||
|
||||
<!-- 标签切换 -->
|
||||
<div class="flex gap-2 border-b">
|
||||
<Button @click="activeTab = 'attacker'" :variant="activeTab === 'attacker' ? 'default' : 'ghost'" class="rounded-b-none">
|
||||
<Sword />
|
||||
{{ t('simulatorView.attacker') }}
|
||||
</Button>
|
||||
<Button @click="activeTab = 'defender'" :variant="activeTab === 'defender' ? 'default' : 'ghost'" class="rounded-b-none">
|
||||
<Shield />
|
||||
{{ t('simulatorView.defender') }}
|
||||
</Button>
|
||||
</div>
|
||||
<!-- 攻击方配置 -->
|
||||
<Card v-if="activeTab === 'attacker'">
|
||||
<CardHeader>
|
||||
<CardTitle>{{ t('simulatorView.attackerConfig') }}</CardTitle>
|
||||
<CardDescription>{{ t('simulatorView.attackerConfigDesc') }}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-4">
|
||||
<!-- 舰队配置 -->
|
||||
<div>
|
||||
<h3 class="text-sm font-medium mb-3">{{ t('simulatorView.fleet') }}</h3>
|
||||
<div class="grid grid-cols-2 md:grid-cols-3 gap-3">
|
||||
<div v-for="shipType in Object.values(ShipType)" :key="shipType" class="space-y-1">
|
||||
<Label :for="`attacker-${shipType}`" class="text-xs">{{ SHIPS[shipType].name }}</Label>
|
||||
<Input :id="`attacker-${shipType}`" v-model.number="attackerFleet[shipType]" type="number" min="0" class="h-8" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 科技等级 -->
|
||||
<div>
|
||||
<h3 class="text-sm font-medium mb-3">{{ t('simulatorView.techLevels') }}</h3>
|
||||
<div class="grid grid-cols-3 gap-3">
|
||||
<div class="space-y-1">
|
||||
<Label for="attacker-weapon" class="text-xs">{{ t('simulatorView.weapon') }}</Label>
|
||||
<Input id="attacker-weapon" v-model.number="attackerTech.weapon" type="number" min="0" class="h-8" />
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="attacker-shield" class="text-xs">{{ t('simulatorView.shield') }}</Label>
|
||||
<Input id="attacker-shield" v-model.number="attackerTech.shield" type="number" min="0" class="h-8" />
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="attacker-armor" class="text-xs">{{ t('simulatorView.armor') }}</Label>
|
||||
<Input id="attacker-armor" v-model.number="attackerTech.armor" type="number" min="0" class="h-8" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<!-- 防守方配置 -->
|
||||
<Card v-else>
|
||||
<CardHeader>
|
||||
<CardTitle>{{ t('simulatorView.defenderConfig') }}</CardTitle>
|
||||
<CardDescription>{{ t('simulatorView.defenderConfigDesc') }}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-4">
|
||||
<!-- 舰队配置 -->
|
||||
<div>
|
||||
<h3 class="text-sm font-medium mb-3">{{ t('simulatorView.fleet') }}</h3>
|
||||
<div class="grid grid-cols-2 md:grid-cols-3 gap-3">
|
||||
<div v-for="shipType in Object.values(ShipType)" :key="shipType" class="space-y-1">
|
||||
<Label :for="`defender-${shipType}`" class="text-xs">{{ SHIPS[shipType].name }}</Label>
|
||||
<Input :id="`defender-${shipType}`" v-model.number="defenderFleet[shipType]" type="number" min="0" class="h-8" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Tabs v-model="activeTab" class="w-full">
|
||||
<TabsList class="grid w-full grid-cols-2">
|
||||
<TabsTrigger value="attacker">
|
||||
<Sword class="h-4 w-4 mr-2" />
|
||||
{{ t('simulatorView.attacker') }}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="defender">
|
||||
<Shield class="h-4 w-4 mr-2" />
|
||||
{{ t('simulatorView.defender') }}
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<!-- 防御设施 -->
|
||||
<div>
|
||||
<h3 class="text-sm font-medium mb-3">{{ t('simulatorView.defenseStructures') }}</h3>
|
||||
<div class="grid grid-cols-2 md:grid-cols-3 gap-3">
|
||||
<div v-for="defenseType in Object.values(DefenseType)" :key="defenseType" class="space-y-1">
|
||||
<Label :for="`defense-${defenseType}`" class="text-xs">{{ DEFENSES[defenseType].name }}</Label>
|
||||
<Input :id="`defense-${defenseType}`" v-model.number="defenderDefense[defenseType]" type="number" min="0" class="h-8" />
|
||||
<!-- 攻击方配置 -->
|
||||
<TabsContent value="attacker" class="mt-4">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{{ t('simulatorView.attackerConfig') }}</CardTitle>
|
||||
<CardDescription>{{ t('simulatorView.attackerConfigDesc') }}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-4">
|
||||
<!-- 舰队配置 -->
|
||||
<div>
|
||||
<h3 class="text-sm font-medium mb-3">{{ t('simulatorView.fleet') }}</h3>
|
||||
<div class="grid grid-cols-2 md:grid-cols-3 gap-3">
|
||||
<div v-for="shipType in Object.values(ShipType)" :key="shipType" class="space-y-1">
|
||||
<Label :for="`attacker-${shipType}`" class="text-xs">{{ SHIPS[shipType].name }}</Label>
|
||||
<Input
|
||||
:id="`attacker-${shipType}`"
|
||||
:model-value="attackerFleet[shipType] ?? 0"
|
||||
@update:model-value="val => (attackerFleet[shipType] = typeof val === 'number' ? val : 0)"
|
||||
type="number"
|
||||
min="0"
|
||||
class="h-8"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 科技等级 -->
|
||||
<div>
|
||||
<h3 class="text-sm font-medium mb-3">{{ t('simulatorView.techLevels') }}</h3>
|
||||
<div class="grid grid-cols-3 gap-3">
|
||||
<div v-for="techType in techTypes" :key="techType" class="space-y-1">
|
||||
<Label :for="`attacker-${techType}`" class="text-xs">{{ t(`simulatorView.${techType}`) }}</Label>
|
||||
<Input :id="`attacker-${techType}`" v-model.number="attackerTech[techType]" type="number" min="0" class="h-8" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
<!-- 科技等级 -->
|
||||
<div>
|
||||
<h3 class="text-sm font-medium mb-3">{{ t('simulatorView.techLevels') }}</h3>
|
||||
<div class="grid grid-cols-3 gap-3">
|
||||
<div class="space-y-1">
|
||||
<Label for="defender-weapon" class="text-xs">{{ t('simulatorView.weapon') }}</Label>
|
||||
<Input id="defender-weapon" v-model.number="defenderTech.weapon" type="number" min="0" class="h-8" />
|
||||
<!-- 防守方配置 -->
|
||||
<TabsContent value="defender" class="mt-4">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{{ t('simulatorView.defenderConfig') }}</CardTitle>
|
||||
<CardDescription>{{ t('simulatorView.defenderConfigDesc') }}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-4">
|
||||
<!-- 舰队配置 -->
|
||||
<div>
|
||||
<h3 class="text-sm font-medium mb-3">{{ t('simulatorView.fleet') }}</h3>
|
||||
<div class="grid grid-cols-2 md:grid-cols-3 gap-3">
|
||||
<div v-for="shipType in Object.values(ShipType)" :key="shipType" class="space-y-1">
|
||||
<Label :for="`defender-${shipType}`" class="text-xs">{{ SHIPS[shipType].name }}</Label>
|
||||
<Input
|
||||
:id="`defender-${shipType}`"
|
||||
:model-value="defenderFleet[shipType] ?? 0"
|
||||
@update:model-value="val => (defenderFleet[shipType] = typeof val === 'number' ? val : 0)"
|
||||
type="number"
|
||||
min="0"
|
||||
class="h-8"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="defender-shield" class="text-xs">{{ t('simulatorView.shield') }}</Label>
|
||||
<Input id="defender-shield" v-model.number="defenderTech.shield" type="number" min="0" class="h-8" />
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="defender-armor" class="text-xs">{{ t('simulatorView.armor') }}</Label>
|
||||
<Input id="defender-armor" v-model.number="defenderTech.armor" type="number" min="0" class="h-8" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 防守方资源 -->
|
||||
<div>
|
||||
<h3 class="text-sm font-medium mb-3">{{ t('simulatorView.defenderResources') }}</h3>
|
||||
<div class="grid grid-cols-3 gap-3">
|
||||
<div class="space-y-1">
|
||||
<Label for="defender-metal" class="text-xs flex items-center gap-1">
|
||||
<ResourceIcon type="metal" size="sm" />
|
||||
{{ t('resources.metal') }}
|
||||
</Label>
|
||||
<Input id="defender-metal" v-model.number="defenderResources.metal" type="number" min="0" class="h-8" />
|
||||
<!-- 防御设施 -->
|
||||
<div>
|
||||
<h3 class="text-sm font-medium mb-3">{{ t('simulatorView.defenseStructures') }}</h3>
|
||||
<div class="grid grid-cols-2 md:grid-cols-3 gap-3">
|
||||
<div v-for="defenseType in Object.values(DefenseType)" :key="defenseType" class="space-y-1">
|
||||
<Label :for="`defense-${defenseType}`" class="text-xs">{{ DEFENSES[defenseType].name }}</Label>
|
||||
<Input
|
||||
:id="`defense-${defenseType}`"
|
||||
:model-value="defenderDefense[defenseType] ?? 0"
|
||||
@update:model-value="val => (defenderDefense[defenseType] = typeof val === 'number' ? val : 0)"
|
||||
type="number"
|
||||
min="0"
|
||||
class="h-8"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="defender-crystal" class="text-xs flex items-center gap-1">
|
||||
<ResourceIcon type="crystal" size="sm" />
|
||||
{{ t('resources.crystal') }}
|
||||
</Label>
|
||||
<Input id="defender-crystal" v-model.number="defenderResources.crystal" type="number" min="0" class="h-8" />
|
||||
|
||||
<!-- 科技等级 -->
|
||||
<div>
|
||||
<h3 class="text-sm font-medium mb-3">{{ t('simulatorView.techLevels') }}</h3>
|
||||
<div class="grid grid-cols-3 gap-3">
|
||||
<div v-for="techType in techTypes" :key="techType" class="space-y-1">
|
||||
<Label :for="`defender-${techType}`" class="text-xs">{{ t(`simulatorView.${techType}`) }}</Label>
|
||||
<Input :id="`defender-${techType}`" v-model.number="defenderTech[techType]" type="number" min="0" class="h-8" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="defender-deuterium" class="text-xs flex items-center gap-1">
|
||||
<ResourceIcon type="deuterium" size="sm" />
|
||||
{{ t('resources.deuterium') }}
|
||||
</Label>
|
||||
<Input id="defender-deuterium" v-model.number="defenderResources.deuterium" type="number" min="0" class="h-8" />
|
||||
|
||||
<!-- 防守方资源 -->
|
||||
<div>
|
||||
<h3 class="text-sm font-medium mb-3">{{ t('simulatorView.defenderResources') }}</h3>
|
||||
<div class="grid grid-cols-3 gap-3">
|
||||
<div v-for="resourceType in resourceTypes" :key="resourceType.key" class="space-y-1">
|
||||
<Label :for="`defender-${resourceType.key}`" class="text-xs flex items-center gap-1">
|
||||
<ResourceIcon :type="resourceType.key" size="sm" />
|
||||
{{ t(`resources.${resourceType.key}`) }}
|
||||
</Label>
|
||||
<Input
|
||||
:id="`defender-${resourceType.key}`"
|
||||
v-model.number="defenderResources[resourceType.key]"
|
||||
type="number"
|
||||
min="0"
|
||||
class="h-8"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="flex gap-2">
|
||||
@@ -149,6 +155,7 @@
|
||||
import { useI18n } from '@/composables/useI18n'
|
||||
import { useGameConfig } from '@/composables/useGameConfig'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
@@ -163,19 +170,23 @@
|
||||
const { t } = useI18n()
|
||||
const { SHIPS, DEFENSES } = useGameConfig()
|
||||
|
||||
// 科技类型配置
|
||||
const techTypes = ['weapon', 'shield', 'armor'] as const
|
||||
|
||||
// 资源类型配置(用于防守方资源输入)
|
||||
const resourceTypes = [{ key: 'metal' as const }, { key: 'crystal' as const }, { key: 'deuterium' as const }]
|
||||
|
||||
// 动态初始化所有舰船类型为0
|
||||
const initializeFleet = (): Partial<Fleet> => {
|
||||
const fleet: Partial<Fleet> = {}
|
||||
Object.values(ShipType).forEach(shipType => {
|
||||
fleet[shipType] = 0
|
||||
})
|
||||
return fleet
|
||||
}
|
||||
|
||||
// 攻击方配置
|
||||
const attackerFleet = ref<Partial<Fleet>>({
|
||||
[ShipType.LightFighter]: 0,
|
||||
[ShipType.HeavyFighter]: 0,
|
||||
[ShipType.Cruiser]: 0,
|
||||
[ShipType.Battleship]: 0,
|
||||
[ShipType.SmallCargo]: 0,
|
||||
[ShipType.LargeCargo]: 0,
|
||||
[ShipType.ColonyShip]: 0,
|
||||
[ShipType.Recycler]: 0,
|
||||
[ShipType.EspionageProbe]: 0,
|
||||
[ShipType.DarkMatterHarvester]: 0
|
||||
})
|
||||
const attackerFleet = ref<Partial<Fleet>>(initializeFleet())
|
||||
|
||||
const activeTab = ref('attacker')
|
||||
|
||||
@@ -186,29 +197,18 @@
|
||||
})
|
||||
|
||||
// 防守方配置
|
||||
const defenderFleet = ref<Partial<Fleet>>({
|
||||
[ShipType.LightFighter]: 0,
|
||||
[ShipType.HeavyFighter]: 0,
|
||||
[ShipType.Cruiser]: 0,
|
||||
[ShipType.Battleship]: 0,
|
||||
[ShipType.SmallCargo]: 0,
|
||||
[ShipType.LargeCargo]: 0,
|
||||
[ShipType.ColonyShip]: 0,
|
||||
[ShipType.Recycler]: 0,
|
||||
[ShipType.EspionageProbe]: 0,
|
||||
[ShipType.DarkMatterHarvester]: 0
|
||||
})
|
||||
const defenderFleet = ref<Partial<Fleet>>(initializeFleet())
|
||||
|
||||
const defenderDefense = ref<Partial<Record<DefenseType, number>>>({
|
||||
[DefenseType.RocketLauncher]: 0,
|
||||
[DefenseType.LightLaser]: 0,
|
||||
[DefenseType.HeavyLaser]: 0,
|
||||
[DefenseType.GaussCannon]: 0,
|
||||
[DefenseType.IonCannon]: 0,
|
||||
[DefenseType.PlasmaTurret]: 0,
|
||||
[DefenseType.SmallShieldDome]: 0,
|
||||
[DefenseType.LargeShieldDome]: 0
|
||||
})
|
||||
// 动态初始化所有防御类型为0
|
||||
const initializeDefense = (): Partial<Record<DefenseType, number>> => {
|
||||
const defense: Partial<Record<DefenseType, number>> = {}
|
||||
Object.values(DefenseType).forEach(defenseType => {
|
||||
defense[defenseType] = 0
|
||||
})
|
||||
return defense
|
||||
}
|
||||
|
||||
const defenderDefense = ref<Partial<Record<DefenseType, number>>>(initializeDefense())
|
||||
|
||||
const defenderTech = ref({
|
||||
weapon: 0,
|
||||
@@ -294,15 +294,9 @@
|
||||
|
||||
// 重置模拟
|
||||
const resetSimulation = () => {
|
||||
Object.keys(attackerFleet.value).forEach(key => {
|
||||
attackerFleet.value[key as ShipType] = 0
|
||||
})
|
||||
Object.keys(defenderFleet.value).forEach(key => {
|
||||
defenderFleet.value[key as ShipType] = 0
|
||||
})
|
||||
Object.keys(defenderDefense.value).forEach(key => {
|
||||
defenderDefense.value[key as DefenseType] = 0
|
||||
})
|
||||
attackerFleet.value = initializeFleet()
|
||||
defenderFleet.value = initializeFleet()
|
||||
defenderDefense.value = initializeDefense()
|
||||
attackerTech.value = { weapon: 0, shield: 0, armor: 0 }
|
||||
defenderTech.value = { weapon: 0, shield: 0, armor: 0 }
|
||||
simulationResult.value = null
|
||||
|
||||
Reference in New Issue
Block a user