feat(web): 用户工作台 - 产品化壳层、内容库布局与积分入口
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
f52702b70f
commit
969e2831b2
46 files changed
+5355
-944
No files matched your search
@@ -1,4 +1,5 @@
|
||||
import { PanelLeftClose, PanelLeftOpen } from "lucide-react";
|
||||
import { Coins, PanelLeftClose, PanelLeftOpen } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { Link, useLocation } from "react-router";
|
||||
|
||||
import { SystemAnnouncementCenter } from "@/components/layout/system-announcement-center";
|
||||
@@ -8,49 +9,59 @@ import { AnimatedThemeToggler } from "@/components/ui/animated-theme-toggler";
|
||||
import { useThemeStore } from "@/stores/use-theme-store";
|
||||
import { useUserStore } from "@/stores/use-user-store";
|
||||
import { useAppearanceStore } from "@/stores/use-appearance-store";
|
||||
import { useWalletBalance } from "@/hooks/use-wallet-balance";
|
||||
import { WorkspaceWalletModal } from "@/components/layout/workspace-wallet-modal";
|
||||
|
||||
const PAGE_TITLES: Record<string, string> = {
|
||||
home: "首页",
|
||||
home: "创作",
|
||||
create: "创作",
|
||||
projects: "短剧创作",
|
||||
canvas: "画布",
|
||||
tasks: "任务",
|
||||
assets: "素材",
|
||||
skills: "技能库",
|
||||
wallet: "积分中心",
|
||||
projects: "短剧 Agent",
|
||||
canvas: "自由画布",
|
||||
tasks: "创作历史",
|
||||
assets: "资产",
|
||||
skills: "技能",
|
||||
plugins: "插件",
|
||||
wallet: "积分超市",
|
||||
settings: "设置",
|
||||
};
|
||||
|
||||
export function WorkspaceTopBar({ sidebarOpen, onToggleSidebar }: { sidebarOpen: boolean; onToggleSidebar: () => void }) {
|
||||
const [walletOpen, setWalletOpen] = useState(false);
|
||||
const brandName = useAppearanceStore((state) => state.appearance.brandName);
|
||||
const theme = useThemeStore((state) => state.theme);
|
||||
const setTheme = useThemeStore((state) => state.setTheme);
|
||||
const user = useUserStore((state) => state.user);
|
||||
const creditsEnabled = useUserStore((state) => state.features.creditsEnabled);
|
||||
const { availableMicrocredits } = useWalletBalance(user?.id, creditsEnabled);
|
||||
const { pathname } = useLocation();
|
||||
const extension = useWorkspaceTopBarContent();
|
||||
|
||||
const slug = pathname.split("/").filter(Boolean)[0];
|
||||
const pageTitle = (slug && PAGE_TITLES[slug]) || brandName;
|
||||
const pageTitle = slug ? PAGE_TITLES[slug] || brandName : PAGE_TITLES.home;
|
||||
const balance = availableMicrocredits === null ? "--" : (availableMicrocredits / 1_000_000).toLocaleString("zh-CN", { maximumFractionDigits: 2 });
|
||||
|
||||
return (
|
||||
<header className={`app-workspace-topbar flex shrink-0 items-center justify-between gap-2 px-3 sm:px-4 ${extension ? "has-extension" : ""}`}>
|
||||
<div className="flex min-w-0 flex-1 items-center gap-2">
|
||||
<button type="button" className="app-workspace-topbar-icon-button" aria-label={sidebarOpen ? "收起侧栏" : "展开侧栏"} onClick={onToggleSidebar}>
|
||||
{sidebarOpen ? <PanelLeftClose className="size-4" /> : <PanelLeftOpen className="size-4" />}
|
||||
</button>
|
||||
<nav className={`${extension ? "hidden 2xl:flex" : "flex"} min-w-0 items-center gap-2 text-[var(--fs-caption)] text-foreground/50`} aria-label="当前位置">
|
||||
<Link to="/" className="shrink-0 font-medium text-foreground/70 transition-colors hover:text-foreground">{brandName}</Link>
|
||||
<span className="shrink-0 text-foreground/30">/</span>
|
||||
<span className="truncate font-medium text-foreground">{pageTitle}</span>
|
||||
</nav>
|
||||
{extension ? <div className="min-w-0 flex-1">{extension}</div> : null}
|
||||
</div>
|
||||
|
||||
<div className="flex shrink-0 items-center gap-1">
|
||||
<header className={`app-workspace-topbar ${extension ? "has-extension" : ""}`}>
|
||||
<button type="button" className="app-workspace-mobile-menu app-workspace-topbar-icon-button" aria-label={sidebarOpen ? "收起侧栏" : "展开侧栏"} onClick={onToggleSidebar}>
|
||||
{sidebarOpen ? <PanelLeftClose className="size-4" /> : <PanelLeftOpen className="size-4" />}
|
||||
</button>
|
||||
<nav className="app-workspace-topbar-breadcrumb" aria-label="当前位置">
|
||||
<Link to="/" className="font-medium text-foreground/65 transition-colors hover:text-foreground">{brandName}</Link>
|
||||
<span aria-hidden="true">/</span>
|
||||
<span className="truncate font-medium text-foreground">{pageTitle}</span>
|
||||
</nav>
|
||||
{extension ? <div className="app-workspace-topbar-extension">{extension}</div> : null}
|
||||
<div className="app-workspace-topbar-actions">
|
||||
{creditsEnabled ? <button type="button" className="app-workspace-topbar-credit-pill" aria-label={`打开积分中心,可用 ${balance} 积分`} onClick={() => setWalletOpen(true)}>
|
||||
<Coins aria-hidden="true" />
|
||||
<span>积分</span>
|
||||
<strong>{balance}</strong>
|
||||
</button> : null}
|
||||
{user ? <SystemAnnouncementCenter userId={user.id} className="app-workspace-topbar-icon-button" autoOpen /> : null}
|
||||
<AnimatedThemeToggler className="app-workspace-topbar-icon-button" theme={theme} onThemeChange={setTheme} aria-label="切换主题" />
|
||||
<WorkspaceAccountMenu />
|
||||
</div>
|
||||
{creditsEnabled ? <WorkspaceWalletModal open={walletOpen} onClose={() => setWalletOpen(false)} /> : null}
|
||||
</header>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user