6f5491c0-6c94-4f8e-8ad3-136d04ab862f

This commit is contained in:
2026-06-23 13:04:15 +00:00
parent f73b96026c
commit 674775be0e
9 changed files with 1007 additions and 26 deletions
+34 -7
View File
@@ -9,6 +9,7 @@ import { TechTree } from "@/components/game/TechTree";
import { Codex } from "@/components/game/Codex";
import { PrestigeDialog } from "@/components/game/PrestigeDialog";
import { SettingsDialog } from "@/components/game/SettingsDialog";
import { ExpeditionPanel } from "@/components/game/ExpeditionPanel";
import { useGameLoop } from "@/hooks/useGameLoop";
import { useGameStore } from "@/store/gameStore";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
@@ -20,6 +21,7 @@ import {
Cpu,
BookOpen,
BarChart3,
Rocket,
Github,
} from "lucide-react";
import { TECH_TREE, FRAGMENTS, formatNum } from "@/lib/game/config";
@@ -37,6 +39,9 @@ export default function Page() {
const ownedTech = useGameStore((s) => s.tech);
const ownedFragments = useGameStore((s) => s.fragments);
const crystals = useGameStore((s) => s.crystals);
const crystalCap = useGameStore((s) => s.crystalCap);
const energy = useGameStore((s) => s.energy);
const hasActiveExpedition = useGameStore((s) => !!s.activeExpedition && !s.activeExpedition.finished);
// 挂载检测:避免持久化 store 在 SSR/CSR 间产生 hydration 不一致
useEffect(() => {
@@ -57,6 +62,9 @@ export default function Page() {
const ownedFragCount = Object.values(ownedFragments).filter(Boolean).length;
const canPrestige = contact >= 100;
// 仓库满仓警告
const warehouseFull = crystals >= crystalCap * 0.98;
// 目标提示
let goal = "点击中央晶体发起脉冲,累积记忆晶体";
if (totalDecoded === 0 && crystals >= 5) {
@@ -66,6 +74,12 @@ export default function Page() {
} else if (totalDecoded > 0 && ownedFragCount < FRAGMENTS.length) {
goal = `继续解码,拼凑记忆图谱(${ownedFragCount}/${FRAGMENTS.length}`;
}
if (energy >= 1 && totalDecoded >= 3) {
goal = "「探险」标签可深入遗迹,获取丰厚奖励";
}
if (warehouseFull) {
goal = "⚠ 仓库已满,产能浪费中!请解码晶体或升级仓库";
}
if (canPrestige) goal = "✦ 接触进度已满,可发起飞升进入新周目";
return (
@@ -133,10 +147,20 @@ export default function Page() {
<div className="glass rounded-2xl p-4 flex-1 min-h-[360px] max-h-[560px]">
<DecodePanel />
</div>
{/* 标签面板:技术 / 图谱 / 统计 */}
<div className="glass rounded-2xl p-3 min-h-[280px] max-h-[380px]">
<Tabs defaultValue="tech" className="h-full flex flex-col">
<TabsList className="grid grid-cols-3 h-8 bg-black/30">
{/* 标签面板:探险 / 技术 / 图谱 / 统计 */}
<div className="glass rounded-2xl p-3 min-h-[320px] max-h-[440px]">
<Tabs defaultValue={hasActiveExpedition ? "expedition" : "tech"} className="h-full flex flex-col">
<TabsList className="grid grid-cols-4 h-8 bg-black/30">
<TabsTrigger value="expedition" className="text-xs gap-1 relative">
<Rocket className="h-3 w-3" />
{energy >= 1 && !hasActiveExpedition && (
<span className="absolute -top-0.5 -right-0.5 h-1.5 w-1.5 rounded-full bg-amber-400 animate-pulse" />
)}
{hasActiveExpedition && (
<span className="absolute -top-0.5 -right-0.5 h-1.5 w-1.5 rounded-full bg-rose-400 animate-pulse" />
)}
</TabsTrigger>
<TabsTrigger value="tech" className="text-xs gap-1">
<Cpu className="h-3 w-3" />
@@ -150,6 +174,9 @@ export default function Page() {
</TabsTrigger>
</TabsList>
<TabsContent value="expedition" className="flex-1 mt-2 min-h-0 data-[state=active]:flex data-[state=active]:flex-col">
<ExpeditionPanel />
</TabsContent>
<TabsContent value="tech" className="flex-1 mt-2 min-h-0 data-[state=active]:flex data-[state=active]:flex-col">
<TechTree />
</TabsContent>
@@ -167,9 +194,9 @@ export default function Page() {
{/* 底部 Footer */}
<footer className="sticky bottom-0 z-20 mt-auto px-3 sm:px-5 pb-2 pt-1">
<div className="glass rounded-xl px-3 py-2 flex items-center gap-2 text-xs">
<span className="text-fuchsia-300"></span>
<span className="text-muted-foreground flex-1 truncate">{goal}</span>
<div className={`glass rounded-xl px-3 py-2 flex items-center gap-2 text-xs ${warehouseFull ? "border-amber-400/40 animate-pulse" : ""}`}>
<span className={warehouseFull ? "text-amber-400" : "text-fuchsia-300"}></span>
<span className={`flex-1 truncate ${warehouseFull ? "text-amber-200" : "text-muted-foreground"}`}>{goal}</span>
<span className="hidden sm:inline text-muted-foreground/60">|</span>
<span className="hidden sm:inline text-muted-foreground/70">
{formatNum(crystalsPerSec)}/s