feat(ui): P3 页面级应用(作品库卡片/空态/加载/设置/次级页)
- 作品库卡片重设计:书脊封面字块 + 衬线书名 + 一句话故事(缺失给占位) + 元信息行(题材徽章/StatusDot 待审/mono 时间),Card tone=card 浮起消塌陷 - EmptyState 中性化 + inline/card/bare 变体 + eyebrow(向后兼容) - loading 骨架化(Skeleton 拼作品库结构,motion-safe) - 设置页拆分 ProvidersSettings 489→198 + 5 个子组件,凭据点用 StatusDot - 次级页(向导/模板/codex/大纲)排版字阶与圆角 token 收敛
This commit is contained in:
@@ -1,9 +1,79 @@
|
||||
import { ThinkingIndicator } from "@/components/ThinkingIndicator";
|
||||
import { Card } from "@/components/ui/Card";
|
||||
import { PageContainer } from "@/components/ui/PageContainer";
|
||||
import { Skeleton } from "@/components/ui/Skeleton";
|
||||
|
||||
// 全站加载骨架:与作品库结构近似(标题条 + 工具条 + 卡片网格),
|
||||
// motion-safe 脉冲,尊重 prefers-reduced-motion。
|
||||
export default function Loading() {
|
||||
return (
|
||||
<main className="flex min-h-screen items-center justify-center bg-bg px-6 py-16">
|
||||
<ThinkingIndicator label="加载中…" className="text-ink-soft" />
|
||||
<main className="min-h-screen bg-bg">
|
||||
<PageContainer>
|
||||
<div role="status" aria-label="加载中" className="space-y-8">
|
||||
<span className="sr-only">加载中…</span>
|
||||
<HeaderSkeleton />
|
||||
<ToolbarSkeleton />
|
||||
<GridSkeleton />
|
||||
</div>
|
||||
</PageContainer>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
// 标题区:大标题条 + 说明条 + 右侧两枚按钮位。
|
||||
function HeaderSkeleton() {
|
||||
return (
|
||||
<div className="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
|
||||
<div className="space-y-3">
|
||||
<Skeleton className="h-8 w-48" />
|
||||
<Skeleton className="h-4 w-72 max-w-full" />
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Skeleton className="h-9 w-28" />
|
||||
<Skeleton className="h-9 w-24" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 工具条:搜索框 + 三枚控件位。
|
||||
function ToolbarSkeleton() {
|
||||
return (
|
||||
<Card flat className="grid gap-3 p-3 md:grid-cols-[1fr_auto_auto_auto]">
|
||||
<Skeleton className="h-9 w-full" />
|
||||
<Skeleton className="h-9 w-28" />
|
||||
<Skeleton className="h-9 w-28" />
|
||||
<Skeleton className="h-9 w-20" />
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
// 卡片网格:6 张与作品卡等高的占位卡。
|
||||
function GridSkeleton() {
|
||||
return (
|
||||
<div className="grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{Array.from({ length: 6 }).map((_, i) => (
|
||||
<CardSkeleton key={i} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 单张作品卡骨架:标题 + 徽章 + 三行简介 + 底部时间。
|
||||
function CardSkeleton() {
|
||||
return (
|
||||
<Card flat className="flex h-full min-h-[176px] flex-col p-6">
|
||||
<div className="mb-4 flex items-start justify-between gap-3">
|
||||
<div className="min-w-0 flex-1 space-y-3">
|
||||
<Skeleton className="h-6 w-3/4" />
|
||||
<Skeleton className="h-5 w-16 rounded-full" />
|
||||
</div>
|
||||
<Skeleton className="h-8 w-8 shrink-0" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Skeleton className="h-4 w-full" />
|
||||
<Skeleton className="h-4 w-5/6" />
|
||||
</div>
|
||||
<Skeleton className="mt-auto h-3 w-28" />
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user