feat: redesign user workspace console
This commit is contained in:
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user