@@ -229,15 +269,11 @@
{{ t('messagesView.rejectedResources') }}:
-
- {{ t('resources.metal') }}: {{ rejection.rejectedResources.metal.toLocaleString() }}
-
-
- {{ t('resources.crystal') }}: {{ rejection.rejectedResources.crystal.toLocaleString() }}
-
-
- {{ t('resources.deuterium') }}: {{ rejection.rejectedResources.deuterium.toLocaleString() }}
-
+
+
+ {{ t(`resources.${res.key}`) }}: {{ rejection.rejectedResources[res.key].toLocaleString() }}
+
+
@@ -253,10 +289,13 @@
-
-
- {{ t('messagesView.noMissionReports') }}
-
+
+
+
+
+ {{ t('messagesView.noMissionReports') }}
+
+
+
+
+
@@ -305,6 +347,9 @@
{{ t('messagesView.spiedNotificationDetails') }}
+
+ {{ t('messagesView.spyDetected') }}
+
@@ -370,6 +415,9 @@
{{ t('messagesView.missionReportDetails') }}
+
+ {{ t('messagesView.missionDetails') }}
+
@@ -417,10 +465,8 @@
{{ t('messagesView.transportedResources') }}:
-
{{ t('resources.metal') }}: {{ selectedMissionReport.details.transportedResources.metal.toLocaleString() }}
-
{{ t('resources.crystal') }}: {{ selectedMissionReport.details.transportedResources.crystal.toLocaleString() }}
-
- {{ t('resources.deuterium') }}: {{ selectedMissionReport.details.transportedResources.deuterium.toLocaleString() }}
+
+ {{ t(`resources.${res.key}`) }}: {{ selectedMissionReport.details.transportedResources[res.key].toLocaleString() }}
@@ -429,14 +475,16 @@
{{ t('messagesView.recycledResources') }}:
-
{{ t('resources.metal') }}: {{ selectedMissionReport.details.recycledResources.metal.toLocaleString() }}
-
{{ t('resources.crystal') }}: {{ selectedMissionReport.details.recycledResources.crystal.toLocaleString() }}
+
+ {{ t(`resources.${res.key}`) }}: {{ selectedMissionReport.details.recycledResources[res.key].toLocaleString() }}
+
{{ t('messagesView.remainingDebris') }}:
-
{{ t('resources.metal') }}: {{ selectedMissionReport.details.remainingDebris.metal.toLocaleString() }}
-
{{ t('resources.crystal') }}: {{ selectedMissionReport.details.remainingDebris.crystal.toLocaleString() }}
+
+ {{ t(`resources.${res.key}`) }}: {{ selectedMissionReport.details.remainingDebris[res.key].toLocaleString() }}
+
@@ -449,6 +497,34 @@
{{ selectedMissionReport.details.newPlanetName }}
+
+
+
+
{{ t('galaxyView.missileAttack') }}:
+
+
+ {{ t('galaxyView.missileCount') }}:
+ {{ selectedMissionReport.details.missileCount }}
+
+
+ {{ t('missionReports.hits') }}:
+ {{ selectedMissionReport.details.missileHits }}
+
+
+ {{ t('galaxyView.intercepted') }}:
+ {{ selectedMissionReport.details.missileIntercepted }}
+
+
+
+
{{ t('galaxyView.defenseLosses') }}:
+
+
+ {{ t('defenses.' + defenseType) }}:
+ -{{ count }}
+
+
+
+
@@ -467,6 +543,9 @@
@@ -551,11 +630,15 @@
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
- import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'
+ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from '@/components/ui/dialog'
+ import { FixedPagination } from '@/components/ui/pagination'
+ import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
+ import { Checkbox } from '@/components/ui/checkbox'
import BattleReportDialog from '@/components/BattleReportDialog.vue'
import SpyReportDialog from '@/components/SpyReportDialog.vue'
import { formatDate } from '@/utils/format'
- import { X, Sword, Eye, AlertTriangle, Package, Recycle, Gift, Ban, Check, Users, Skull, Globe, Compass } from 'lucide-vue-next'
+ import { X, Sword, Eye, AlertTriangle, Package, Recycle, Gift, Ban, Check, Users, Skull, Globe, Compass, Trash2 } from 'lucide-vue-next'
+ import { Empty, EmptyContent, EmptyDescription } from '@/components/ui/empty'
import type {
BattleResult,
SpyReport,
@@ -568,6 +651,7 @@
import { MissionType } from '@/types/game'
import { useNPCStore } from '@/stores/npcStore'
import * as diplomaticLogic from '@/logic/diplomaticLogic'
+ import { toast } from 'vue-sonner'
const router = useRouter()
const gameStore = useGameStore()
@@ -575,6 +659,107 @@
const { t } = useI18n()
const activeTab = ref<'battles' | 'spy' | 'missions' | 'npc'>('battles')
+ // 清空消息功能
+ const showClearPopover = ref(false)
+ type ClearOptionKey =
+ | 'battles'
+ | 'spyReports'
+ | 'spiedNotifications'
+ | 'missionReports'
+ | 'npcActivity'
+ | 'giftNotifications'
+ | 'giftRejected'
+ const clearOptions = ref
>({
+ battles: false,
+ spyReports: false,
+ spiedNotifications: false,
+ missionReports: false,
+ npcActivity: false,
+ giftNotifications: false,
+ giftRejected: false
+ })
+
+ // 清空消息选项配置
+ const clearOptionFields = computed(() => [
+ { key: 'battles' as ClearOptionKey, labelKey: 'clearBattleReports', count: gameStore.player.battleReports.length },
+ { key: 'spyReports' as ClearOptionKey, labelKey: 'clearSpyReports', count: gameStore.player.spyReports.length },
+ {
+ key: 'spiedNotifications' as ClearOptionKey,
+ labelKey: 'clearSpiedNotifications',
+ count: gameStore.player.spiedNotifications?.length || 0
+ },
+ { key: 'missionReports' as ClearOptionKey, labelKey: 'clearMissionReports', count: gameStore.player.missionReports?.length || 0 },
+ { key: 'npcActivity' as ClearOptionKey, labelKey: 'clearNPCActivity', count: gameStore.player.npcActivityNotifications?.length || 0 },
+ {
+ key: 'giftNotifications' as ClearOptionKey,
+ labelKey: 'clearGiftNotifications',
+ count: gameStore.player.giftNotifications?.length || 0
+ },
+ { key: 'giftRejected' as ClearOptionKey, labelKey: 'clearGiftRejected', count: gameStore.player.giftRejectedNotifications?.length || 0 }
+ ])
+
+ // 基础资源字段配置(用于显示资源列表)
+ type BasicResourceKey = 'metal' | 'crystal' | 'deuterium'
+ const basicResourceFields: { key: BasicResourceKey }[] = [{ key: 'metal' }, { key: 'crystal' }, { key: 'deuterium' }]
+
+ // 残骸资源字段配置(只有金属和晶体)
+ type DebrisResourceKey = 'metal' | 'crystal'
+ const debrisResourceFields: { key: DebrisResourceKey }[] = [{ key: 'metal' }, { key: 'crystal' }]
+
+ const hasSelectedAny = computed(() => {
+ return Object.values(clearOptions.value).some(v => v)
+ })
+
+ const clearSelectedMessages = () => {
+ if (clearOptions.value.battles) {
+ gameStore.player.battleReports = []
+ }
+ if (clearOptions.value.spyReports) {
+ gameStore.player.spyReports = []
+ }
+ if (clearOptions.value.spiedNotifications) {
+ gameStore.player.spiedNotifications = []
+ }
+ if (clearOptions.value.missionReports) {
+ gameStore.player.missionReports = []
+ }
+ if (clearOptions.value.npcActivity) {
+ gameStore.player.npcActivityNotifications = []
+ }
+ if (clearOptions.value.giftNotifications) {
+ gameStore.player.giftNotifications = []
+ }
+ if (clearOptions.value.giftRejected) {
+ gameStore.player.giftRejectedNotifications = []
+ }
+
+ // 重置选项
+ clearOptions.value = {
+ battles: false,
+ spyReports: false,
+ spiedNotifications: false,
+ missionReports: false,
+ npcActivity: false,
+ giftNotifications: false,
+ giftRejected: false
+ }
+
+ // 关闭popover
+ showClearPopover.value = false
+
+ // 显示成功提示
+ toast.success(t('messagesView.clearSuccess'))
+ }
+
+ // 分页状态
+ const ITEMS_PER_PAGE = 10
+ const currentPage = ref({
+ battles: 1,
+ spy: 1,
+ missions: 1,
+ npc: 1
+ })
+
// 对话框状态
const showBattleDialog = ref(false)
const showSpyDialog = ref(false)
@@ -587,40 +772,107 @@
const selectedMissionReport = ref(null)
const selectedNPCActivityNotification = ref(null)
- // 排序后的战斗报告(最新的在前)
- const sortedBattleReports = computed(() => {
+ // 排序后的战斗报告(最新的在前)- 全部数据
+ const allBattleReports = computed(() => {
return [...gameStore.player.battleReports].sort((a, b) => b.timestamp - a.timestamp)
})
- // 排序后的间谍报告(最新的在前)
- const sortedSpyReports = computed(() => {
+ // 排序后的间谍报告(最新的在前)- 全部数据
+ const allSpyReports = computed(() => {
return [...gameStore.player.spyReports].sort((a, b) => b.timestamp - a.timestamp)
})
- // 排序后的被侦查通知(最新的在前)
- const sortedSpiedNotifications = computed(() => {
+ // 排序后的被侦查通知(最新的在前)- 全部数据
+ const allSpiedNotifications = computed(() => {
if (!gameStore.player.spiedNotifications) {
return []
}
return [...gameStore.player.spiedNotifications].sort((a, b) => b.timestamp - a.timestamp)
})
- // 排序后的任务报告(最新的在前)
- const sortedMissionReports = computed(() => {
+ // 排序后的任务报告(最新的在前)- 全部数据
+ const allMissionReports = computed(() => {
if (!gameStore.player.missionReports) {
return []
}
return [...gameStore.player.missionReports].sort((a, b) => b.timestamp - a.timestamp)
})
- // 排序后的NPC活动通知(最新的在前)
- const sortedNPCActivityNotifications = computed(() => {
+ // 排序后的NPC活动通知(最新的在前)- 全部数据
+ const allNPCActivityNotifications = computed(() => {
if (!gameStore.player.npcActivityNotifications) {
return []
}
return [...gameStore.player.npcActivityNotifications].sort((a, b) => b.timestamp - a.timestamp)
})
+ // 战斗报告分页
+ const battleReportsTotalPages = computed(() => Math.ceil(allBattleReports.value.length / ITEMS_PER_PAGE))
+ const sortedBattleReports = computed(() => {
+ const start = (currentPage.value.battles - 1) * ITEMS_PER_PAGE
+ const end = start + ITEMS_PER_PAGE
+ return allBattleReports.value.slice(start, end)
+ })
+
+ // 侦查标签页合并数据(侦查报告 + 被侦查通知)
+ const allSpyTabItems = computed(() => {
+ const spyReports = allSpyReports.value.map(item => ({ ...item, type: 'spy' as const }))
+ const spiedNotifications = allSpiedNotifications.value.map(item => ({ ...item, type: 'spied' as const }))
+ return [...spyReports, ...spiedNotifications].sort((a, b) => b.timestamp - a.timestamp)
+ })
+
+ const spyTabTotalPages = computed(() => Math.ceil(allSpyTabItems.value.length / ITEMS_PER_PAGE))
+ const paginatedSpyTabItems = computed(() => {
+ const start = (currentPage.value.spy - 1) * ITEMS_PER_PAGE
+ const end = start + ITEMS_PER_PAGE
+ return allSpyTabItems.value.slice(start, end)
+ })
+
+ // 从分页后的混合数据中分离出侦查报告和被侦查通知
+ const sortedSpyReports = computed(() => {
+ return paginatedSpyTabItems.value.filter(item => item.type === 'spy')
+ })
+
+ const sortedSpiedNotifications = computed(() => {
+ return paginatedSpyTabItems.value.filter(item => item.type === 'spied')
+ })
+
+ // 任务报告分页
+ const missionReportsTotalPages = computed(() => Math.ceil(allMissionReports.value.length / ITEMS_PER_PAGE))
+ const sortedMissionReports = computed(() => {
+ const start = (currentPage.value.missions - 1) * ITEMS_PER_PAGE
+ const end = start + ITEMS_PER_PAGE
+ return allMissionReports.value.slice(start, end)
+ })
+
+ // NPC标签页合并数据(活动通知 + 礼物通知 + 礼物被拒绝通知)
+ const allNPCTabItems = computed(() => {
+ const activities = allNPCActivityNotifications.value.map(item => ({ ...item, type: 'activity' as const }))
+ const gifts = allGiftNotifications.value.map(item => ({ ...item, type: 'gift' as const }))
+ const rejections = allGiftRejectedNotifications.value.map(item => ({ ...item, type: 'rejection' as const }))
+ return [...activities, ...gifts, ...rejections].sort((a, b) => b.timestamp - a.timestamp)
+ })
+
+ const npcTabTotalPages = computed(() => Math.ceil(allNPCTabItems.value.length / ITEMS_PER_PAGE))
+ const paginatedNPCTabItems = computed(() => {
+ const start = (currentPage.value.npc - 1) * ITEMS_PER_PAGE
+ const end = start + ITEMS_PER_PAGE
+ return allNPCTabItems.value.slice(start, end)
+ })
+
+ // 从分页后的混合数据中分离出各种NPC消息
+ const sortedNPCActivityNotifications = computed(() => {
+ return paginatedNPCTabItems.value.filter(item => item.type === 'activity')
+ })
+
+ const sortedGiftNotifications = computed(() => {
+ return paginatedNPCTabItems.value.filter(item => item.type === 'gift')
+ })
+
+ const sortedGiftRejectedNotifications = computed(() => {
+ return paginatedNPCTabItems.value.filter(item => item.type === 'rejection')
+ })
+
// 未读战斗报告数量
const unreadBattles = computed(() => {
return gameStore.player.battleReports.filter(r => !r.read).length
@@ -709,22 +961,41 @@
}
])
- // 排序后的礼物通知(最新的在前)
- const sortedGiftNotifications = computed(() => {
+ // 礼物通知和被拒绝通知的全部数据(用于NPC标签页合并)
+ const allGiftNotifications = computed(() => {
if (!gameStore.player.giftNotifications) {
return []
}
return [...gameStore.player.giftNotifications].sort((a, b) => b.timestamp - a.timestamp)
})
- // 排序后的礼物被拒绝通知(最新的在前)
- const sortedGiftRejectedNotifications = computed(() => {
+ const allGiftRejectedNotifications = computed(() => {
if (!gameStore.player.giftRejectedNotifications) {
return []
}
return [...gameStore.player.giftRejectedNotifications].sort((a, b) => b.timestamp - a.timestamp)
})
+ // 分页配置
+ type PageKey = 'battles' | 'spy' | 'missions' | 'npc'
+ const paginationConfigs = computed(() => ({
+ battles: {
+ totalPages: battleReportsTotalPages.value
+ },
+ spy: {
+ totalPages: spyTabTotalPages.value
+ },
+ missions: {
+ totalPages: missionReportsTotalPages.value
+ },
+ npc: {
+ totalPages: npcTabTotalPages.value
+ }
+ }))
+
+ // 获取指定标签页的分页配置
+ const getPaginationConfig = (key: PageKey) => paginationConfigs.value[key]
+
// 判断战斗结果Badge颜色
const getBattleResultVariant = (report: BattleResult): 'default' | 'destructive' | 'secondary' => {
if (report.winner === 'draw') {
@@ -765,17 +1036,19 @@
const openSpyReport = (report: SpyReport) => {
selectedSpyReport.value = report
showSpyDialog.value = true
- // 标记为已读
- if (!report.read) {
- report.read = true
+ // 找到原始间谍报告对象并标记为已读(因为sortedSpyReports是副本)
+ const originalReport = gameStore.player.spyReports.find(r => r.id === report.id)
+ if (originalReport && !originalReport.read) {
+ originalReport.read = true
}
}
// 打开被侦查通知
const openSpiedNotification = (notification: SpiedNotification) => {
- // 标记为已读
- if (!notification.read) {
- notification.read = true
+ // 找到原始通知对象并标记为已读(因为sortedSpiedNotifications是副本)
+ const originalNotification = gameStore.player.spiedNotifications?.find(n => n.id === notification.id)
+ if (originalNotification && !originalNotification.read) {
+ originalNotification.read = true
}
// 设置选中的通知并打开详情对话框
selectedSpiedNotification.value = notification
@@ -811,9 +1084,10 @@
// 打开NPC活动通知
const openNPCActivityNotification = (notification: NPCActivityNotification) => {
- // 标记为已读
- if (!notification.read) {
- notification.read = true
+ // 找到原始通知对象并标记为已读(因为sortedNPCActivityNotifications是副本)
+ const originalNotification = gameStore.player.npcActivityNotifications?.find(n => n.id === notification.id)
+ if (originalNotification && !originalNotification.read) {
+ originalNotification.read = true
}
// 设置选中的通知并打开详情对话框
selectedNPCActivityNotification.value = notification
@@ -869,8 +1143,10 @@
// 标记礼物通知为已读
const markGiftAsRead = (gift: GiftNotification) => {
- if (!gift.read) {
- gift.read = true
+ // 找到原始礼物通知对象并标记为已读(因为gifts是副本)
+ const originalGift = gameStore.player.giftNotifications?.find(g => g.id === gift.id)
+ if (originalGift && !originalGift.read) {
+ originalGift.read = true
}
}
@@ -903,8 +1179,10 @@
// 标记礼物被拒绝通知为已读
const markGiftRejectedAsRead = (rejection: GiftRejectedNotification) => {
- if (!rejection.read) {
- rejection.read = true
+ // 找到原始拒绝通知对象并标记为已读(因为rejections是副本)
+ const originalRejection = gameStore.player.giftRejectedNotifications?.find(r => r.id === rejection.id)
+ if (originalRejection && !originalRejection.read) {
+ originalRejection.read = true
}
}
diff --git a/src/views/SettingsView.vue b/src/views/SettingsView.vue
index 5f92226..ef279bf 100644
--- a/src/views/SettingsView.vue
+++ b/src/views/SettingsView.vue
@@ -65,8 +65,8 @@
{{ t('settings.gameSpeed') }}
{{ t('settings.gameSpeedDesc') }}
-