feat: redesign user workspace console

This commit is contained in:
2026-04-22 11:34:25 +08:00
parent 14b18d67fe
commit 745f6f07db
7 changed files with 2017 additions and 331 deletions

View File

@@ -3,6 +3,7 @@
import { useQuery } from "@tanstack/react-query";
import clsx from "clsx";
import {
Activity,
Coins,
FolderKanban,
ImagePlus,
@@ -20,16 +21,60 @@ import { api } from "@/lib/api";
import type { UserProfile } from "@/lib/types";
const navigation = [
{ href: "/workspace/create", label: "创建任务", icon: Sparkles },
{ href: "/workspace/tasks", label: "任务记录", icon: FolderKanban },
{ href: "/workspace/assets", label: "素材管理", icon: ImagePlus },
{ href: "/wallet", label: "钱包概览", icon: Coins },
{ href: "/wallet/recharge", label: "充值中心", icon: Coins },
{ href: "/wallet/redeem", label: "兑换密钥", icon: Ticket },
{ href: "/invite", label: "邀请中心", icon: Users },
{ href: "/profile", label: "个人资料", icon: UserRound },
{
href: "/workspace/create",
label: "工作台",
description: "生成参数、提示词与素材联动",
icon: Sparkles,
},
{
href: "/workspace/tasks",
label: "任务记录",
description: "查看队列、状态轮询与结果视频",
icon: FolderKanban,
},
{
href: "/workspace/assets",
label: "素材管理",
description: "上传图片、视频和音频素材",
icon: ImagePlus,
},
{
href: "/wallet",
label: "钱包概览",
description: "查看可用积分和冻结明细",
icon: Coins,
},
{
href: "/wallet/recharge",
label: "充值中心",
description: "采购积分,补足生产额度",
icon: Coins,
},
{
href: "/wallet/redeem",
label: "兑换密钥",
description: "使用兑换码快速充值",
icon: Ticket,
},
{
href: "/invite",
label: "邀请中心",
description: "管理关系链与推广奖励",
icon: Users,
},
{
href: "/profile",
label: "个人资料",
description: "维护账户信息与展示名",
icon: UserRound,
},
];
function matchesPath(pathname: string, href: string) {
return pathname === href || pathname.startsWith(`${href}/`);
}
export function SiteShell({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
const router = useRouter();
@@ -45,8 +90,10 @@ export function SiteShell({ children }: { children: React.ReactNode }) {
}, [error, router]);
const title = useMemo(() => {
const current = navigation.find((item) => pathname.startsWith(item.href));
return current?.label ?? "AIVideo";
const current = [...navigation]
.sort((left, right) => right.href.length - left.href.length)
.find((item) => matchesPath(pathname, item.href));
return current ?? navigation[0];
}, [pathname]);
if (isLoading || !data) {
@@ -61,9 +108,13 @@ export function SiteShell({ children }: { children: React.ReactNode }) {
<div className="shell-grid">
<aside className="shell-sidebar">
<div className="brand-block">
<span className="brand-kicker">AI VIDEO PLATFORM</span>
<span className="brand-kicker">AI VIDEO STUDIO</span>
<h1>AIVideo</h1>
<p></p>
<p></p>
<div className="brand-status">
<span className="status-dot" />
线
</div>
</div>
<nav className="sidebar-nav">
@@ -74,18 +125,26 @@ export function SiteShell({ children }: { children: React.ReactNode }) {
key={item.href}
href={item.href}
className={clsx("nav-item", {
active: pathname.startsWith(item.href),
active: title.href === item.href,
})}
>
<Icon size={18} />
<span>{item.label}</span>
<div>
<span>{item.label}</span>
<small>{item.description}</small>
</div>
</Link>
);
})}
</nav>
<div className="profile-card">
<div>
<div className="profile-card-main">
<div className="profile-avatar">
{(data.nickname || data.username || "AI")
.slice(0, 2)
.toUpperCase()}
</div>
<div className="profile-name">{data.nickname || data.username}</div>
<div className="profile-meta">{data.email}</div>
</div>
@@ -105,14 +164,20 @@ export function SiteShell({ children }: { children: React.ReactNode }) {
<main className="shell-main">
<header className="shell-header">
<div>
<div className="header-kicker">Creative Ops</div>
<h2>{title}</h2>
<div className="header-kicker">Creative Ops Console</div>
<h2>{title.label}</h2>
<p className="shell-header-copy">{title.description}</p>
</div>
<div className="shell-header-meta">
<div className="header-chip header-chip-success">
<Activity size={14} />
</div>
<div className="header-chip">{data.publicId}</div>
</div>
<div className="header-chip">{data.publicId}</div>
</header>
<section className="shell-content">{children}</section>
</main>
</div>
);
}