Match LibTV account and member panels
This commit is contained in:
parent
d7f50984b4
commit
c3b4e35997
@ -5,7 +5,6 @@ import {
|
||||
ChevronDown,
|
||||
Clock3,
|
||||
Copy,
|
||||
Crown,
|
||||
FileText,
|
||||
GitBranch,
|
||||
Headphones,
|
||||
@ -73,6 +72,7 @@ type ComposerMenu =
|
||||
| "settings"
|
||||
| "count"
|
||||
| null;
|
||||
type MemberStoreTab = "vip" | "model" | "credits";
|
||||
|
||||
type CapturedTool = {
|
||||
type?: string;
|
||||
@ -248,6 +248,90 @@ const AUDIO_REFERENCE_IMAGE_URL =
|
||||
"https://liblibai-online.liblib.cloud/web/ea95cb51f3d94addbb92dc003cd33b49/1342d344-2d95-41a1-a2aa-4d20869a318c.png";
|
||||
const PUBLISH_MODAL_BG_URL =
|
||||
"https://libtv-res.liblib.art/upload-images/4e7115e6ee6947b78d73e0203a85fc56/771d92a21c6dce69a97513d21ee3253fe0eedec9.png";
|
||||
const MEMBER_CARD_THEMES: Record<string, { bg: string; icon: string; tone: string }> = {
|
||||
"lib-nano": {
|
||||
bg: "https://liblibai-online.liblib.cloud/web/4e7115e6ee6947b78d73e0203a85fc56/3b7bcfd5-d1d3-4545-8089-53c04bad6ede.svg",
|
||||
icon: "https://liblibai-online.liblib.cloud/web/4e7115e6ee6947b78d73e0203a85fc56/8d5f4fb5-8132-4049-b902-7eca7db0de1d.svg",
|
||||
tone: "#2A2038",
|
||||
},
|
||||
"seedance-2": {
|
||||
bg: "https://liblibai-online.liblib.cloud/web/4e7115e6ee6947b78d73e0203a85fc56/a520a76c-8101-4be3-8c3b-30a37b2320d9.svg",
|
||||
icon: "https://liblibai-online.liblib.cloud/web/4e7115e6ee6947b78d73e0203a85fc56/9207509b-a5d5-47af-944b-1894b469735f.svg",
|
||||
tone: "#222A3E",
|
||||
},
|
||||
"seedance-2-1080p": {
|
||||
bg: "https://liblibai-online.liblib.cloud/web/4e7115e6ee6947b78d73e0203a85fc56/a32e399e-8353-4cdf-a44e-e7e08cca8c7f.svg",
|
||||
icon: "https://liblibai-online.liblib.cloud/web/4e7115e6ee6947b78d73e0203a85fc56/9207509b-a5d5-47af-944b-1894b469735f.svg",
|
||||
tone: "#222222",
|
||||
},
|
||||
};
|
||||
const MEMBER_MODEL_CARDS = [
|
||||
{
|
||||
title: "Seedance 2.0",
|
||||
label: "1080P 专享",
|
||||
power: "45,000",
|
||||
price: "2,999",
|
||||
corner: "首购加赠45000",
|
||||
desc: "大师版及以上会员可购买",
|
||||
group: "seedance-2-1080p",
|
||||
},
|
||||
{
|
||||
title: "Seedance 2.0",
|
||||
label: "1080P 专享",
|
||||
power: "60,000",
|
||||
price: "3,999",
|
||||
corner: "首购加赠60000",
|
||||
desc: "大师版及以上会员可购买",
|
||||
group: "seedance-2-1080p",
|
||||
},
|
||||
{
|
||||
title: "Lib Nano Pro",
|
||||
label: "模型积分卡",
|
||||
power: "20,000",
|
||||
price: "1,999",
|
||||
corner: "多生成571张图片",
|
||||
desc: "尊享版可购买",
|
||||
group: "lib-nano",
|
||||
},
|
||||
{
|
||||
title: "Lib Nano 2",
|
||||
label: "模型积分卡",
|
||||
power: "45,000",
|
||||
price: "2,999",
|
||||
corner: "多生成1500张图片",
|
||||
desc: "尊享版可购买",
|
||||
group: "lib-nano",
|
||||
},
|
||||
{
|
||||
title: "Seedance 2.0",
|
||||
label: "视频模型卡",
|
||||
power: "7,500",
|
||||
price: "599",
|
||||
corner: "多生成341秒视频",
|
||||
desc: "大师版及以上会员可购买",
|
||||
group: "seedance-2",
|
||||
},
|
||||
{
|
||||
title: "Seedance 2.0",
|
||||
label: "视频模型卡",
|
||||
power: "15,000",
|
||||
price: "999",
|
||||
corner: "多生成682秒视频",
|
||||
desc: "大师版及以上会员可购买",
|
||||
group: "seedance-2",
|
||||
},
|
||||
];
|
||||
const VIP_PLAN_CARDS = [
|
||||
{ title: "基础版VIP", power: "每月 8,000 点积分", price: "39", benefits: ["登录每日赠送20积分", "云端存储空间20GB", "会员专属模型"] },
|
||||
{ title: "大师版VIP", power: "每月 18,000 点积分", price: "89", benefits: ["无限并发任务", "会员专享无限次加速", "Seedance 2.0 活动权益"] },
|
||||
{ title: "尊享版VIP", power: "每月 34,000 点积分", price: "169", benefits: ["云端存储空间500GB", "智能分镜(Kling3.0)", "去除品牌水印"] },
|
||||
];
|
||||
const CREDIT_PACKS = [
|
||||
{ points: "1,000", price: "6", tag: "限时 6 折" },
|
||||
{ points: "5,000", price: "30", tag: "首充推荐" },
|
||||
{ points: "10,000", price: "60", tag: "最高赠送" },
|
||||
{ points: "30,000", price: "180", tag: "创作囤货" },
|
||||
];
|
||||
const STORY_SCRIPT_PROMPT = "根据我上传的剧本生成一个完整的故事脚本";
|
||||
const AUDIO_TO_VIDEO_PROMPT =
|
||||
"根据上传的音频生成对应场景画面,镜头语言、节奏、音乐匹配情绪变化,电影级质感。";
|
||||
@ -946,6 +1030,12 @@ export function LiblibVideoCanvasReplica({ canvasId, isLoading = false }: Liblib
|
||||
toast.success("分享链接已复制");
|
||||
}, [canvasId]);
|
||||
|
||||
const toggleTopPanel = useCallback((panel: Exclude<ActivePanel, null>) => {
|
||||
setCanvasCommand(null);
|
||||
setNodeContextMenu(null);
|
||||
setActivePanel((current) => (current === panel ? null : panel));
|
||||
}, []);
|
||||
|
||||
const isBusy = isLoading || isProjectLoading;
|
||||
|
||||
return (
|
||||
@ -1055,14 +1145,14 @@ export function LiblibVideoCanvasReplica({ canvasId, isLoading = false }: Liblib
|
||||
draftTitle={draftTitle}
|
||||
isSaving={isSaving}
|
||||
isTitleEditing={isTitleEditing}
|
||||
onAccount={() => setActivePanel("account")}
|
||||
onBell={() => setActivePanel("notifications")}
|
||||
onAccount={() => toggleTopPanel("account")}
|
||||
onBell={() => toggleTopPanel("notifications")}
|
||||
onCommitTitle={commitTitle}
|
||||
onDraftTitleChange={setDraftTitle}
|
||||
onMember={() => setActivePanel("member")}
|
||||
onMember={() => toggleTopPanel("member")}
|
||||
onSave={saveCanvas}
|
||||
onShare={() => setActivePanel("share")}
|
||||
onSkills={() => setActivePanel("skills")}
|
||||
onShare={() => toggleTopPanel("share")}
|
||||
onSkills={() => toggleTopPanel("skills")}
|
||||
onStartTitleEdit={() => setIsTitleEditing(true)}
|
||||
projectTitle={projectTitle}
|
||||
routerBack={() => router.push("/")}
|
||||
@ -1189,21 +1279,15 @@ export function LiblibVideoCanvasReplica({ canvasId, isLoading = false }: Liblib
|
||||
) : null}
|
||||
|
||||
{activePanel === "member" ? (
|
||||
<LibModal onClose={() => setActivePanel(null)} title="会员中心">
|
||||
<MemberCenterPanel />
|
||||
</LibModal>
|
||||
<MemberStoreModal onClose={() => setActivePanel(null)} />
|
||||
) : null}
|
||||
|
||||
{activePanel === "notifications" ? (
|
||||
<LibModal onClose={() => setActivePanel(null)} title="通知">
|
||||
<NotificationsPanel />
|
||||
</LibModal>
|
||||
<NotificationsPopover onClose={() => setActivePanel(null)} />
|
||||
) : null}
|
||||
|
||||
{activePanel === "account" ? (
|
||||
<LibModal onClose={() => setActivePanel(null)} title="账号">
|
||||
<AccountPanel />
|
||||
</LibModal>
|
||||
<AccountPopover onClose={() => setActivePanel(null)} />
|
||||
) : null}
|
||||
|
||||
{isBusy ? (
|
||||
@ -3021,141 +3105,419 @@ function SharePopover({
|
||||
onPublish: () => void;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className="fixed right-[312px] top-[66px] z-[75] w-[360px] rounded-[16px] border border-white/10 bg-[#202020]/98 p-2 text-white shadow-[0_24px_80px_rgba(0,0,0,0.52)] backdrop-blur-[16px]"
|
||||
data-canvas-overlay="true"
|
||||
>
|
||||
<div className="fixed inset-0 z-[70]" data-canvas-overlay="true">
|
||||
<button
|
||||
aria-label="关闭分享菜单"
|
||||
className="absolute inset-0 -z-10 cursor-default"
|
||||
className="absolute inset-0 cursor-default bg-transparent"
|
||||
onClick={onClose}
|
||||
type="button"
|
||||
/>
|
||||
<div className="flex flex-col gap-2">
|
||||
<span className="pl-2 text-sm font-medium text-white/60">发布与分享</span>
|
||||
<button
|
||||
className="flex cursor-pointer items-center gap-3 rounded-xl p-2 text-left transition-colors hover:bg-white/8"
|
||||
onClick={onPublish}
|
||||
type="button"
|
||||
>
|
||||
<span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-white/10 text-white/80">
|
||||
<StoreGlyph />
|
||||
</span>
|
||||
<span className="flex min-w-0 flex-col gap-1">
|
||||
<span className="text-sm font-medium text-white">在LibTV上发布</span>
|
||||
<span className="text-xs leading-snug text-white/70">发布你的作品和创作过程,让更多创作者看到。</span>
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
className="flex cursor-pointer items-center gap-3 rounded-xl p-2 text-left transition-colors hover:bg-white/8"
|
||||
onClick={onCopyLink}
|
||||
type="button"
|
||||
>
|
||||
<span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-white/10 text-white/80">
|
||||
<Copy className="h-5 w-5" />
|
||||
</span>
|
||||
<span className="flex min-w-0 flex-col gap-1">
|
||||
<span className="text-sm font-medium text-white">分享链接</span>
|
||||
<span className="text-xs leading-snug text-white/70">拥有此链接的人可以查看并复制你的画布。</span>
|
||||
</span>
|
||||
</button>
|
||||
<div className="fixed right-[312px] top-[66px] z-[75] w-[360px] rounded-[16px] border border-white/10 bg-[#202020]/98 p-2 text-white shadow-[0_24px_80px_rgba(0,0,0,0.52)] backdrop-blur-[16px]">
|
||||
<div className="flex flex-col gap-2">
|
||||
<span className="pl-2 text-sm font-medium text-white/60">发布与分享</span>
|
||||
<button
|
||||
className="flex cursor-pointer items-center gap-3 rounded-xl p-2 text-left transition-colors hover:bg-white/8"
|
||||
onClick={onPublish}
|
||||
type="button"
|
||||
>
|
||||
<span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-white/10 text-white/80">
|
||||
<StoreGlyph />
|
||||
</span>
|
||||
<span className="flex min-w-0 flex-col gap-1">
|
||||
<span className="text-sm font-medium text-white">在LibTV上发布</span>
|
||||
<span className="text-xs leading-snug text-white/70">发布你的作品和创作过程,让更多创作者看到。</span>
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
className="flex cursor-pointer items-center gap-3 rounded-xl p-2 text-left transition-colors hover:bg-white/8"
|
||||
onClick={onCopyLink}
|
||||
type="button"
|
||||
>
|
||||
<span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-white/10 text-white/80">
|
||||
<Copy className="h-5 w-5" />
|
||||
</span>
|
||||
<span className="flex min-w-0 flex-col gap-1">
|
||||
<span className="text-sm font-medium text-white">分享链接</span>
|
||||
<span className="text-xs leading-snug text-white/70">拥有此链接的人可以查看并复制你的画布。</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function MemberCenterPanel() {
|
||||
function MemberStoreModal({ onClose }: { onClose: () => void }) {
|
||||
const [activeTab, setActiveTab] = useState<MemberStoreTab>("model");
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="rounded-[8px] border border-[#ffc65d]/25 bg-[#2a2418] p-4">
|
||||
<div className="text-[12px] text-[#fad6a4]">限时 39 折</div>
|
||||
<div className="mt-1 text-[22px] font-bold text-white">会员特惠39折</div>
|
||||
<div className="mt-1 text-[13px] text-white/52">会员超市、模型权益、生成额度与积分套餐待接入。</div>
|
||||
<div className="fixed inset-0 z-[100] flex items-center justify-center bg-black/62 backdrop-blur-[3px]" data-canvas-overlay="true">
|
||||
<button aria-label="关闭会员超市" className="absolute inset-0 cursor-default" onClick={onClose} type="button" />
|
||||
<div className="relative flex max-h-[86dvh] w-[980px] max-w-[calc(100vw-32px)] overflow-hidden rounded-[16px] border border-white/12 bg-[#1f1f1f] text-white shadow-[0_30px_100px_rgba(0,0,0,0.62)]">
|
||||
<div className="w-[244px] shrink-0 border-white/8 border-r bg-[#171717] p-5">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="flex h-8 w-8 items-center justify-center rounded-[8px] bg-[#05dff6]/12 text-[#05dff6]">
|
||||
<StoreGlyph />
|
||||
</span>
|
||||
<div>
|
||||
<div className="text-[18px] font-semibold">会员超市</div>
|
||||
<div className="mt-0.5 text-[12px] text-white/42">超低价会员超市</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-5 rounded-[12px] border border-[#ffc65d]/20 bg-[#2a2418] p-3">
|
||||
<div className="text-[12px] text-[#fad6a4]">限时 39 折</div>
|
||||
<div className="mt-1 text-[22px] font-bold leading-tight">会员特惠39折</div>
|
||||
<div className="mt-2 text-[12px] leading-5 text-white/52">会员专属模型超市,顶级模型囤货必选。</div>
|
||||
</div>
|
||||
<div className="mt-4 space-y-1">
|
||||
{[
|
||||
["vip", "会员特惠", "每月积分与专属权益"],
|
||||
["model", "模型积分卡", "限时闪购 买一赠一"],
|
||||
["credits", "积分充值", "通用积分灵活补充"],
|
||||
].map(([id, title, desc]) => (
|
||||
<button
|
||||
className={cn(
|
||||
"w-full rounded-[10px] px-3 py-2 text-left transition-colors",
|
||||
activeTab === id ? "bg-white text-black" : "text-white/66 hover:bg-white/8 hover:text-white"
|
||||
)}
|
||||
key={id}
|
||||
onClick={() => setActiveTab(id as MemberStoreTab)}
|
||||
type="button"
|
||||
>
|
||||
<span className="block text-[13px] font-semibold">{title}</span>
|
||||
<span className={cn("mt-1 block text-[11px]", activeTab === id ? "text-black/55" : "text-white/36")}>{desc}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-5 rounded-[8px] border border-white/8 bg-white/5 p-3 text-[12px] leading-5 text-white/48">
|
||||
免费用户登录每日赠送20积分,每日赠2次5折视频;云端存储空间3GB。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex min-w-0 flex-1 flex-col">
|
||||
<div className="flex h-[64px] items-center justify-between border-white/8 border-b px-5">
|
||||
<div>
|
||||
<div className="text-[17px] font-semibold">
|
||||
{activeTab === "model" ? "模型积分卡" : activeTab === "vip" ? "会员特惠" : "积分充值"}
|
||||
</div>
|
||||
<div className="mt-1 text-[12px] text-white/42">
|
||||
{activeTab === "model" ? "模型超市内积分有效期为6个月,支付后不退不换。" : activeTab === "vip" ? "购买后立即生效,积分按月自动发放。" : "积分有效期2年,支付后不退不换。"}
|
||||
</div>
|
||||
</div>
|
||||
<button className="flex h-8 w-8 items-center justify-center rounded-[8px] text-white/50 hover:bg-white/8 hover:text-white" onClick={onClose} type="button">
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="overflow-y-auto p-5">
|
||||
{activeTab === "model" ? <ModelCardGrid /> : null}
|
||||
{activeTab === "vip" ? <VipPlanGrid /> : null}
|
||||
{activeTab === "credits" ? <CreditPackGrid /> : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ModelCardGrid() {
|
||||
return (
|
||||
<div>
|
||||
<div className="mb-4 flex items-center justify-between rounded-[10px] border border-[#ffc65d]/20 bg-[#2a2418] px-4 py-3">
|
||||
<div>
|
||||
<div className="text-[14px] font-semibold text-[#ffdca8]">限时闪购 买一赠一</div>
|
||||
<div className="mt-1 text-[12px] text-white/48">会员专属模型超市,顶级模型囤货必选</div>
|
||||
</div>
|
||||
<button
|
||||
className="mt-4 h-10 rounded-[8px] bg-[#ffc65d] px-4 text-[13px] font-bold text-black hover:bg-[#ffd282]"
|
||||
onClick={() => toast.info("会员购买接口待接入。")}
|
||||
className="h-9 rounded-[8px] bg-[#ffc65d] px-4 text-[13px] font-semibold text-black hover:bg-[#ffd282]"
|
||||
onClick={() => toast.info("模型积分卡购买接口待接入。")}
|
||||
type="button"
|
||||
>
|
||||
查看权益
|
||||
查看规则
|
||||
</button>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
{[
|
||||
["100", "可用积分"],
|
||||
["67", "模型能力"],
|
||||
["25", "工作流预设"],
|
||||
].map(([value, label]) => (
|
||||
<div className="rounded-[8px] border border-white/8 bg-white/5 p-3 text-center" key={label}>
|
||||
<div className="text-[20px] font-semibold text-white">{value}</div>
|
||||
<div className="mt-1 text-[12px] text-white/42">{label}</div>
|
||||
</div>
|
||||
{MEMBER_MODEL_CARDS.map((card) => (
|
||||
<MemberModelCard card={card} key={`${card.title}-${card.power}`} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function NotificationsPanel() {
|
||||
function MemberModelCard({ card }: { card: (typeof MEMBER_MODEL_CARDS)[number] }) {
|
||||
const theme = MEMBER_CARD_THEMES[card.group] || MEMBER_CARD_THEMES["seedance-2"];
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
{["全部", "生成任务", "系统通知", "会员活动"].map((item, index) => (
|
||||
<button
|
||||
className={cn(
|
||||
"mr-2 h-8 rounded-[8px] border border-white/8 px-3 text-[12px] text-white/56 hover:bg-white/8 hover:text-white",
|
||||
index === 0 && "bg-white text-black hover:bg-white hover:text-black"
|
||||
)}
|
||||
key={item}
|
||||
type="button"
|
||||
>
|
||||
{item}
|
||||
</button>
|
||||
))}
|
||||
{[
|
||||
["生成任务", "任务状态、进度轮询和失败原因接口待接入。"],
|
||||
["系统通知", "审核、账户和平台消息接口待接入。"],
|
||||
["会员活动", "会员超市与活动消息接口待接入。"],
|
||||
].map(([title, description]) => (
|
||||
<div className="rounded-[8px] border border-white/8 bg-white/5 p-3" key={title}>
|
||||
<div className="text-[13px] font-semibold text-white/82">{title}</div>
|
||||
<div className="mt-1 text-[12px] text-white/42">{description}</div>
|
||||
<button
|
||||
className="group relative min-h-[190px] overflow-hidden rounded-[12px] border border-white/8 p-4 text-left shadow-[0_14px_36px_rgba(0,0,0,0.24)] transition-colors hover:border-white/18"
|
||||
onClick={() => toast.info(`${card.title} 购买接口待接入。`)}
|
||||
style={{
|
||||
backgroundColor: theme.tone,
|
||||
backgroundImage: `linear-gradient(180deg, rgba(255,255,255,0.06), rgba(0,0,0,0.12)), url(${theme.bg})`,
|
||||
backgroundPosition: "right top",
|
||||
backgroundRepeat: "no-repeat",
|
||||
backgroundSize: "170px auto",
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<span className="absolute right-3 top-3 rounded-[8px] bg-[#ff4d4f] px-2 py-1 text-[10px] font-semibold text-white">{card.corner}</span>
|
||||
<span className="flex h-8 w-8 items-center justify-center rounded-[8px] bg-white/14">
|
||||
<img alt="" className="h-5 w-5" src={theme.icon} />
|
||||
</span>
|
||||
<span className="mt-5 block text-[16px] font-semibold text-white">{card.title}</span>
|
||||
<span className="mt-1 block text-[12px] text-white/55">{card.label}</span>
|
||||
<span className="mt-4 flex items-baseline gap-1">
|
||||
<span className="text-[24px] font-bold text-white">{card.power}</span>
|
||||
<span className="text-[12px] text-white/50">积分</span>
|
||||
</span>
|
||||
<span className="mt-3 flex items-center justify-between">
|
||||
<span className="text-[11px] text-white/42">{card.desc}</span>
|
||||
<span className="rounded-[8px] bg-white px-3 py-1.5 text-[12px] font-semibold text-black transition group-hover:bg-white/90">¥{card.price}</span>
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
function VipPlanGrid() {
|
||||
return (
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
{VIP_PLAN_CARDS.map((plan, index) => (
|
||||
<div className="rounded-[12px] border border-white/8 bg-white/[0.06] p-4" key={plan.title}>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-[16px] font-semibold">{plan.title}</span>
|
||||
{index === 1 ? <span className="rounded-full bg-[#ffc65d] px-2 py-0.5 text-[10px] font-semibold text-black">推荐</span> : null}
|
||||
</div>
|
||||
<div className="mt-2 text-[12px] text-white/48">{plan.power}</div>
|
||||
<div className="mt-5 flex items-end gap-1">
|
||||
<span className="text-[13px] text-[#ffc65d]">¥</span>
|
||||
<span className="text-[32px] font-bold text-[#ffc65d]">{plan.price}</span>
|
||||
<span className="pb-1 text-[12px] text-white/42">/月起</span>
|
||||
</div>
|
||||
<button
|
||||
className="mt-4 h-9 w-full rounded-[8px] bg-white text-[13px] font-semibold text-black hover:bg-white/90"
|
||||
onClick={() => toast.info(`${plan.title} 购买接口待接入。`)}
|
||||
type="button"
|
||||
>
|
||||
立即开通
|
||||
</button>
|
||||
<div className="mt-4 space-y-2">
|
||||
{plan.benefits.map((benefit) => (
|
||||
<div className="flex items-center gap-2 text-[12px] text-white/58" key={benefit}>
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-[#ffc65d]" />
|
||||
{benefit}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function AccountPanel() {
|
||||
function CreditPackGrid() {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-3 rounded-[8px] border border-white/8 bg-white/5 p-4">
|
||||
<img alt="avatar" className="h-12 w-12 rounded-full object-cover" src={TARGET_AVATAR} />
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="truncate text-[15px] font-semibold text-white">微信用户82a0d8</div>
|
||||
<div className="mt-1 text-[12px] text-white/42">账号中心、团队空间和额度设置待接入。</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-4 gap-3">
|
||||
{CREDIT_PACKS.map((pack) => (
|
||||
<button
|
||||
className="h-9 rounded-[8px] border border-white/8 px-3 text-[12px] text-white/62 hover:bg-white/8 hover:text-white"
|
||||
onClick={() => toast.info("账号设置接口待接入。")}
|
||||
className="rounded-[12px] border border-white/8 bg-white/[0.06] p-4 text-left transition hover:border-white/18 hover:bg-white/[0.08]"
|
||||
key={pack.points}
|
||||
onClick={() => toast.info(`${pack.points} 积分充值接口待接入。`)}
|
||||
type="button"
|
||||
>
|
||||
设置
|
||||
<span className="rounded-full bg-[#ff4d4f]/18 px-2 py-0.5 text-[10px] font-semibold text-[#ff8c8c]">{pack.tag}</span>
|
||||
<span className="mt-5 block text-[26px] font-bold">{pack.points}</span>
|
||||
<span className="mt-1 block text-[12px] text-white/45">通用积分</span>
|
||||
<span className="mt-5 block text-[15px] font-semibold text-[#ffc65d]">¥{pack.price}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
{[
|
||||
["100", "当前积分"],
|
||||
["个人空间", "当前空间"],
|
||||
].map(([value, label]) => (
|
||||
<div className="rounded-[8px] border border-white/8 bg-white/5 p-3" key={label}>
|
||||
<div className="text-[18px] font-semibold text-white">{value}</div>
|
||||
<div className="mt-1 text-[12px] text-white/42">{label}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function NotificationsPopover({ onClose }: { onClose: () => void }) {
|
||||
return (
|
||||
<div className="fixed inset-0 z-[70]" data-canvas-overlay="true">
|
||||
<button aria-label="关闭通知" className="absolute inset-0 cursor-default bg-transparent" onClick={onClose} type="button" />
|
||||
<div className="fixed right-[404px] top-[66px] z-[75] w-[360px] overflow-hidden rounded-[16px] border border-white/10 bg-[#202020]/98 text-white shadow-[0_24px_80px_rgba(0,0,0,0.52)] backdrop-blur-[16px]">
|
||||
<div className="flex items-center justify-between border-white/8 border-b px-4 py-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="relative flex h-8 w-8 items-center justify-center rounded-[8px] bg-white/8">
|
||||
<span className="absolute right-1 top-1 h-[5px] w-[5px] rounded-full bg-[#f53f3f]" />
|
||||
<Bell className="h-4 w-4 text-white/76" />
|
||||
</span>
|
||||
<div>
|
||||
<div className="text-[14px] font-semibold">通知</div>
|
||||
<div className="text-[11px] text-white/40">生成任务与系统消息</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<button className="rounded-[8px] p-1.5 text-white/48 hover:bg-white/8 hover:text-white" onClick={onClose} type="button">
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="p-3">
|
||||
<div className="mb-3 flex gap-2">
|
||||
{["全部", "生成任务", "系统通知", "会员活动"].map((item, index) => (
|
||||
<button
|
||||
className={cn(
|
||||
"h-7 rounded-[8px] px-3 text-[12px] transition-colors",
|
||||
index === 0 ? "bg-white text-black" : "bg-white/6 text-white/56 hover:bg-white/10 hover:text-white"
|
||||
)}
|
||||
key={item}
|
||||
type="button"
|
||||
>
|
||||
{item}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
{[
|
||||
["生成任务", "任务状态、进度轮询和失败原因接口待接入。"],
|
||||
["系统通知", "审核、账户和平台消息接口待接入。"],
|
||||
["会员活动", "限时 39 折,会员超市模型积分卡活动待接入。"],
|
||||
].map(([title, description]) => (
|
||||
<button
|
||||
className="mb-2 w-full rounded-[10px] border border-white/8 bg-white/5 p-3 text-left hover:bg-white/8"
|
||||
key={title}
|
||||
onClick={() => toast.info(`${title}接口待接入。`)}
|
||||
type="button"
|
||||
>
|
||||
<span className="block text-[13px] font-semibold text-white/82">{title}</span>
|
||||
<span className="mt-1 block text-[12px] leading-5 text-white/42">{description}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function AccountPopover({ onClose }: { onClose: () => void }) {
|
||||
return (
|
||||
<div className="fixed inset-0 z-[70]" data-canvas-overlay="true">
|
||||
<button aria-label="关闭账号菜单" className="absolute inset-0 cursor-default bg-transparent" onClick={onClose} type="button" />
|
||||
<div className="fixed right-4 top-[66px] z-[75] w-[388px] max-w-[calc(100vw-32px)] overflow-hidden rounded-[16px] border border-white/10 bg-[#202020]/98 text-white shadow-[0_24px_80px_rgba(0,0,0,0.54)] backdrop-blur-[16px]">
|
||||
<div className="px-4 pb-3 pt-5">
|
||||
<div className="flex items-start gap-3">
|
||||
<img alt="avatar" className="h-12 w-12 rounded-full object-cover" src={TARGET_AVATAR} />
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="truncate text-[14px] font-bold text-white/90">微信用户82a0d8</span>
|
||||
<span className="rounded-[7px] bg-[#09caf5]/12 px-2 py-0.5 text-[11px] text-[#09caf5]">个人</span>
|
||||
</div>
|
||||
<div className="mt-1 flex items-center gap-1 text-[12px] text-white/42">
|
||||
<span>UUID</span>
|
||||
<button
|
||||
className="rounded-[6px] px-1 text-white/42 hover:bg-white/8 hover:text-white"
|
||||
onClick={() => {
|
||||
void navigator.clipboard?.writeText("c9d954c4006648a2825b7240bf046f96");
|
||||
toast.success("ID 复制成功");
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<Copy className="h-3 w-3" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<ChevronDown className="mt-2 h-4 w-4 text-white/62" />
|
||||
</div>
|
||||
|
||||
<div className="mt-4 rounded-[12px] border border-white/8 bg-white/[0.04] p-2">
|
||||
<div className="flex items-center gap-1 px-2 py-1 text-[14px] font-medium text-white/45">
|
||||
<UserRound className="h-4 w-4" />
|
||||
个人空间
|
||||
</div>
|
||||
<button
|
||||
className="flex w-full items-center justify-between rounded-[10px] p-2 text-left hover:bg-white/8"
|
||||
onClick={() => toast.info("账号切换接口待接入。")}
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
<span className="block text-[14px] font-medium text-white/86">个人空间</span>
|
||||
<span className="mt-0.5 block text-[12px] text-white/48">个人免费版</span>
|
||||
</span>
|
||||
<span className="h-[14px] w-[14px] rounded-full border border-[#09caf5] bg-[#09caf5]/18" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 grid grid-cols-2 gap-2">
|
||||
<AccountMetricCard className="col-span-2" label="积分余额" value="100" suffix="点" accent />
|
||||
<AccountMetricCard label="加速余额" value="0" suffix="次" />
|
||||
<AccountMetricCard label="存储空间" value="0G" suffix="/ 3G" />
|
||||
</div>
|
||||
|
||||
<div className="mt-4 space-y-1">
|
||||
<AccountMenuButton icon={<UserRound className="h-[18px] w-[18px]" />} label="个人中心" onClick={() => toast.info("个人中心入口待接入。")} />
|
||||
<AccountMenuButton icon={<Plus className="h-[18px] w-[18px]" />} label="创建团队" onClick={() => toast.info("创建团队接口待接入。")} />
|
||||
<AccountMenuButton icon={<FileText className="h-[18px] w-[18px]" />} label="订阅与开发票" onClick={() => toast.info("订阅与开发票入口待接入。")} />
|
||||
<AccountMenuButton icon={<Settings2 className="h-[18px] w-[18px]" />} label="账号设置" onClick={() => toast.info("账号设置入口待接入。")} />
|
||||
<AccountMenuButton icon={<LogoutGlyph />} label="退出登录" onClick={() => toast.info("退出登录接口待接入。")} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function AccountMetricCard({
|
||||
accent = false,
|
||||
className,
|
||||
label,
|
||||
suffix,
|
||||
value,
|
||||
}: {
|
||||
accent?: boolean;
|
||||
className?: string;
|
||||
label: string;
|
||||
suffix: string;
|
||||
value: string;
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
className={cn(
|
||||
"rounded-[12px] border border-white/8 bg-white/[0.04] px-4 py-3 text-left hover:bg-white/[0.07]",
|
||||
className
|
||||
)}
|
||||
onClick={() => toast.info(`${label}接口待接入。`)}
|
||||
type="button"
|
||||
>
|
||||
<span className={cn("block text-[12px]", accent ? "text-[#09caf5]" : "text-white/45")}>{label}</span>
|
||||
<span className="mt-1 flex items-baseline gap-1">
|
||||
<span className={cn("font-['PlusJakartaSans'] text-[18px] font-bold", accent ? "text-[#09caf5]" : "text-white/88")}>{value}</span>
|
||||
<span className="text-[12px] text-white/44">{suffix}</span>
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
function AccountMenuButton({
|
||||
icon,
|
||||
label,
|
||||
onClick,
|
||||
}: {
|
||||
icon: React.ReactNode;
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
className="flex h-10 w-full cursor-pointer items-center gap-3 rounded-[10px] px-2 text-left text-[13px] text-white/82 transition-colors hover:bg-white/8"
|
||||
onClick={onClick}
|
||||
type="button"
|
||||
>
|
||||
<span className="flex h-6 w-6 shrink-0 items-center justify-center text-white/50">{icon}</span>
|
||||
<span className="flex-1">{label}</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
function LogoutGlyph() {
|
||||
return (
|
||||
<svg aria-hidden="true" className="h-[18px] w-[18px]" fill="none" viewBox="0 0 18 18">
|
||||
<path d="M9.75 2.925H4.5a.825.825 0 0 0-.825.825v10.5c0 .456.369.825.825.825h5.25v1.35H4.5a2.175 2.175 0 0 1-2.175-2.175V3.75c0-1.201.974-2.175 2.175-2.175h5.25zm5.658 5.482a.826.826 0 0 1 0 1.11l-3.658 4.025-.999-.908 2.722-2.996H5.25v-1.35h8.223L10.75 5.291l.999-.908z" fill="currentColor" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function SupportPanel() {
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user