refactor: 优化主界面布局与通知系统

重构App.vue,首页独立无侧边栏,其他页面采用统一侧边栏布局。新增右下角固定通知区,集成返回顶部、队列通知、外交通知和敌方警报。移除新手引导组件,替换为弱引导提示系统。支持星球重命名弹窗。优化NPC成长与行为定时器逻辑,提升性能和可维护性。删除issue模板及相关文档描述。
This commit is contained in:
谦君
2025-12-19 12:01:45 +08:00
parent a689ce21b7
commit 752cade67c
61 changed files with 5774 additions and 2817 deletions

View File

@@ -65,8 +65,8 @@
<h3 class="font-medium">{{ t('settings.gameSpeed') }}</h3>
<p class="text-sm text-muted-foreground">{{ t('settings.gameSpeedDesc') }}</p>
</div>
<div class="flex items-center gap-2 sm:gap-4 w-full sm:w-auto">
<div class="flex items-center gap-2 flex-1 sm:flex-initial">
<div class="flex items-center gap-2 sm:gap-4 w-full sm:w-auto">
<div class="flex items-center gap-2 flex-1 sm:flex-initial">
<Button @click="decreaseSpeed" variant="outline" size="sm" :disabled="gameStore.gameSpeed <= 0.5">-</Button>
<span class="min-w-[60px] text-center font-medium">{{ gameStore.gameSpeed }}x</span>
<Button @click="increaseSpeed" variant="outline" size="sm" :disabled="gameStore.gameSpeed >= 10">+</Button>
@@ -107,9 +107,12 @@
</div>
<!-- 页面聚焦时不发送 -->
<div class="flex items-center justify-between pl-4 border-l-2" :class="{ 'opacity-50 pointer-events-none': !gameStore.notificationSettings?.browser }">
<div
class="flex items-center justify-between pl-4 border-l-2"
:class="{ 'opacity-50 pointer-events-none': !gameStore.notificationSettings?.browser }"
>
<Label class="font-normal">{{ t('settings.suppressInFocus') }}</Label>
<Switch
<Switch
:checked="gameStore.notificationSettings?.suppressInFocus"
@update:checked="updateSuppressSetting"
:disabled="!gameStore.notificationSettings?.browser"
@@ -123,26 +126,29 @@
<h3 class="font-medium">{{ t('settings.inAppNotifications') }}</h3>
<p class="text-sm text-muted-foreground">{{ t('settings.inAppNotificationsDesc') || t('settings.inAppNotifications') }}</p>
</div>
<Switch
:checked="gameStore.notificationSettings?.inApp"
@update:checked="(val: boolean) => updateInAppSetting(val)"
/>
<Switch :checked="gameStore.notificationSettings?.inApp" @update:checked="(val: boolean) => updateInAppSetting(val)" />
</div>
<!-- 具体通知类型 -->
<div class="border rounded-lg overflow-hidden" :class="{ 'opacity-50 pointer-events-none': areMainSwitchesOff }">
<div
<div
class="flex items-center justify-between p-4 bg-muted/50 cursor-pointer select-none"
@click="!areMainSwitchesOff && (isTypesExpanded = !isTypesExpanded)"
>
<div class="space-y-1">
<h3 class="font-medium">{{ t('settings.notificationTypes') }}</h3>
<p class="text-sm text-muted-foreground">
{{ areMainSwitchesOff ? t('settings.notificationsDisabled') : (isTypesExpanded ? t('settings.collapseTypes') : t('settings.expandTypes')) }}
{{
areMainSwitchesOff
? t('settings.notificationsDisabled')
: isTypesExpanded
? t('settings.collapseTypes')
: t('settings.expandTypes')
}}
</p>
</div>
<Button variant="ghost" size="sm" class="h-8 w-8 p-0">
<component :is="isTypesExpanded ? ChevronUp : ChevronDown" class="h-4 w-4" />
<component :is="isTypesExpanded ? ChevronUp : ChevronDown" class="h-4 w-4" />
</Button>
</div>
@@ -157,8 +163,8 @@
</div>
<!-- 研究完成 -->
<div class="flex items-center justify-between">
<Label class="font-normal cursor-pointer" @click="toggleType('research')">{{ t('settings.researchComplete') }}</Label>
<Switch
<Label class="font-normal cursor-pointer" @click="toggleType('research')">{{ t('settings.researchComplete') }}</Label>
<Switch
:checked="gameStore.notificationSettings?.types.research"
@update:checked="(val: boolean) => updateTypeSetting('research', val)"
/>
@@ -168,6 +174,54 @@
</CardContent>
</Card>
<!-- 帮助提示设置 -->
<Card>
<CardHeader>
<CardTitle>{{ t('hints.hintsEnabled') }}</CardTitle>
<CardDescription>{{ t('hints.hintsEnabledDesc') }}</CardDescription>
</CardHeader>
<CardContent class="space-y-4">
<!-- 启用/禁用提示 -->
<div class="flex items-center justify-between p-4 border rounded-lg">
<div class="space-y-1">
<h3 class="font-medium">{{ t('hints.hintsEnabled') }}</h3>
<p class="text-sm text-muted-foreground">{{ t('hints.hintsEnabledDesc') }}</p>
</div>
<Switch :checked="hintsEnabled" @update:checked="setHintsEnabled" />
</div>
<!-- 重置提示 -->
<div class="flex items-center justify-between p-4 border rounded-lg">
<div class="space-y-1">
<h3 class="font-medium">{{ t('hints.resetHints') }}</h3>
<p class="text-sm text-muted-foreground">{{ t('hints.resetHintsDesc') }}</p>
</div>
<Button @click="handleResetHints" variant="outline">
<RotateCcw class="mr-2 h-4 w-4" />
{{ t('settings.reset') }}
</Button>
</div>
</CardContent>
</Card>
<!-- 显示设置 -->
<Card>
<CardHeader>
<CardTitle>{{ t('settings.displaySettings') }}</CardTitle>
<CardDescription>{{ t('settings.displaySettingsDesc') }}</CardDescription>
</CardHeader>
<CardContent class="space-y-4">
<!-- 背景动画 -->
<div class="flex items-center justify-between p-4 border rounded-lg">
<div class="space-y-1">
<h3 class="font-medium">{{ t('settings.backgroundAnimation') }}</h3>
<p class="text-sm text-muted-foreground">{{ t('settings.backgroundAnimationDesc') }}</p>
</div>
<Switch :checked="gameStore.player.backgroundEnabled ?? false" @update:checked="updateBackgroundSetting" />
</div>
</CardContent>
</Card>
<!-- 关于 -->
<Card>
<CardHeader>
@@ -198,6 +252,11 @@
<div class="pt-2 border-t space-y-2">
<h3 class="text-sm font-medium">{{ t('settings.community') }}</h3>
<div class="flex flex-col gap-2">
<!-- Privacy Policy -->
<Button variant="outline" class="w-full justify-start" @click="openPrivacy">
<Shield class="mr-2 h-4 w-4" />
{{ t('settings.privacyPolicy') }}
</Button>
<!-- GitHub -->
<Button variant="outline" class="w-full justify-start" @click="openGithub">
<ExternalLink class="mr-2 h-4 w-4" />
@@ -234,6 +293,9 @@
<!-- 更新对话框 -->
<UpdateDialog v-model:open="showUpdateDialog" :version-info="updateInfo" />
<!-- 隐私协议弹窗 -->
<PrivacyDialog v-model:open="showPrivacyDialog" />
</div>
</template>
@@ -255,15 +317,31 @@
AlertDialogHeader,
AlertDialogTitle
} from '@/components/ui/alert-dialog'
import { Download, Upload, Trash2, ExternalLink, MessagesSquare, Play, Pause, RefreshCw, ChevronDown, ChevronUp } from 'lucide-vue-next'
import {
Download,
Upload,
Trash2,
ExternalLink,
MessagesSquare,
Play,
Pause,
RefreshCw,
ChevronDown,
ChevronUp,
RotateCcw,
Shield
} from 'lucide-vue-next'
import { saveAs } from 'file-saver'
import { toast } from 'vue-sonner'
import pkg from '../../package.json'
import { checkLatestVersion, canCheckVersion } from '@/utils/versionCheck'
import type { VersionInfo } from '@/utils/versionCheck'
import UpdateDialog from '@/components/UpdateDialog.vue'
import PrivacyDialog from '@/components/PrivacyDialog.vue'
import { useHints } from '@/composables/useHints'
const { t } = useI18n()
const { hintsEnabled, setHintsEnabled, resetHints } = useHints()
const gameStore = useGameStore()
const fileInputRef = ref<HTMLInputElement>()
@@ -278,7 +356,7 @@
const isTypesExpanded = ref(false)
// Ensure notification settings exist
// 确保通知设置存在
if (!gameStore.notificationSettings) {
gameStore.notificationSettings = {
browser: false,
@@ -293,7 +371,7 @@
return !s?.browser && !s?.inApp
})
// Auto-collapse if main switches are off
// 当主开关关闭时自动折叠
// watch(areMainSwitchesOff, (val) => {
// if (val) isTypesExpanded.value = false
// })
@@ -310,13 +388,13 @@
}
}
const updateTypeSetting = (key: string, val: boolean) => {
const updateTypeSetting = (key: 'construction' | 'research', val: boolean) => {
if (gameStore.notificationSettings) {
gameStore.notificationSettings.types[key] = val
}
}
const toggleType = (key: string) => {
const toggleType = (key: 'construction' | 'research') => {
if (gameStore.notificationSettings) {
const current = gameStore.notificationSettings.types[key]
gameStore.notificationSettings.types[key] = !current
@@ -385,6 +463,14 @@
window.open(`https://qm.qq.com/q/${pkg.id}`, '_blank')
}
// 隐私协议弹窗状态
const showPrivacyDialog = ref(false)
// 打开隐私协议弹窗
const openPrivacy = () => {
showPrivacyDialog.value = true
}
// 手动检查版本
const showUpdateDialog = ref(false)
const updateInfo = ref<VersionInfo | null>(null)
@@ -514,6 +600,7 @@
// 清除数据
const handleClearData = () => {
gameStore.isPaused = true
confirmTitle.value = t('settings.clearConfirmTitle')
confirmMessage.value = t('settings.clearConfirmMessage')
showConfirmDialog.value = true
@@ -583,4 +670,15 @@
fileInputRef.value.value = ''
}
}
// 重置提示
const handleResetHints = () => {
resetHints()
toast.success(t('hints.resetHints'))
}
// 更新背景设置
const updateBackgroundSetting = (val: boolean) => {
gameStore.player.backgroundEnabled = val
}
</script>