feat(project): init
This commit is contained in:
183
components/history-gallery.tsx
Normal file
183
components/history-gallery.tsx
Normal file
@@ -0,0 +1,183 @@
|
||||
import type { ImageHistoryItem, PublicUser } from "@/lib/types";
|
||||
|
||||
interface HistoryGalleryProps {
|
||||
currentUser: PublicUser;
|
||||
items: ImageHistoryItem[];
|
||||
}
|
||||
|
||||
const dateTimeFormatter = new Intl.DateTimeFormat("zh-CN", {
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
});
|
||||
|
||||
export default function HistoryGallery({
|
||||
currentUser,
|
||||
items,
|
||||
}: HistoryGalleryProps) {
|
||||
const totalUsers = new Set(items.map((item) => item.userId)).size;
|
||||
const latestItem = items[0] ?? null;
|
||||
|
||||
return (
|
||||
<div className="history-grid">
|
||||
<div className="history-overview-grid">
|
||||
<section className="card history-overview-card">
|
||||
<div className="section-heading">
|
||||
<div>
|
||||
<p className="section-kicker">Archive</p>
|
||||
<h3>
|
||||
{currentUser.role === "admin" ? "全站生成历史" : "我的生成历史"}
|
||||
</h3>
|
||||
</div>
|
||||
<span className="subtle-pill">{items.length} 张图片</span>
|
||||
</div>
|
||||
|
||||
<div className="history-stat-grid">
|
||||
<article className="history-stat-card">
|
||||
<span>图片总数</span>
|
||||
<strong>{items.length}</strong>
|
||||
</article>
|
||||
<article className="history-stat-card">
|
||||
<span>{currentUser.role === "admin" ? "涉及用户" : "当前身份"}</span>
|
||||
<strong>
|
||||
{currentUser.role === "admin" ? `${totalUsers} 位` : "普通用户"}
|
||||
</strong>
|
||||
</article>
|
||||
<article className="history-stat-card">
|
||||
<span>最近一张</span>
|
||||
<strong>
|
||||
{latestItem
|
||||
? dateTimeFormatter.format(new Date(latestItem.createdAt))
|
||||
: "暂无记录"}
|
||||
</strong>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="card history-overview-card history-overview-card-muted">
|
||||
<div className="section-heading">
|
||||
<div>
|
||||
<p className="section-kicker">Access</p>
|
||||
<h3>权限与查看范围</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="muted block-copy">
|
||||
{currentUser.role === "admin"
|
||||
? "管理员可以查看所有用户的历史记录、原始提示词和生成参数,页面会额外标注每张图所属的账号。"
|
||||
: "普通用户只能查看自己的历史记录和图片内容,接口会按当前登录账号做隔离。"}
|
||||
</p>
|
||||
|
||||
<div className="meta-pills history-info-pills">
|
||||
<span>Prompt 可回看</span>
|
||||
<span>按账号隔离</span>
|
||||
<span>原图可下载</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{items.length === 0 ? (
|
||||
<section className="card history-empty-card">
|
||||
<div className="placeholder-orb" />
|
||||
<h4>还没有历史图片</h4>
|
||||
<p>
|
||||
生成成功后的图片会自动保存到服务器,这里会展示图片、提示词和模型参数。
|
||||
</p>
|
||||
</section>
|
||||
) : (
|
||||
<section className="history-gallery-list">
|
||||
{items.map((item) => (
|
||||
<article key={item.id} className="card history-card">
|
||||
<div className="history-card-media">
|
||||
<img src={item.imageUrl} alt={item.fileName} loading="lazy" />
|
||||
</div>
|
||||
|
||||
<div className="history-card-header">
|
||||
<div>
|
||||
<h4>{item.fileName}</h4>
|
||||
<p className="muted">
|
||||
{dateTimeFormatter.format(new Date(item.createdAt))}
|
||||
</p>
|
||||
</div>
|
||||
{currentUser.role === "admin" ? (
|
||||
<span className="subtle-pill history-owner-pill">
|
||||
{item.username}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="meta-pills history-meta-pills">
|
||||
<span>{item.model ?? "未知模型"}</span>
|
||||
<span>{item.size ?? "未知尺寸"}</span>
|
||||
<span>{item.quality ?? "未知质量"}</span>
|
||||
</div>
|
||||
|
||||
<div className="history-meta-list">
|
||||
<div>
|
||||
<span>格式</span>
|
||||
<strong>{formatOutputFormatLabel(item)}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>背景</span>
|
||||
<strong>{item.background ?? "auto"}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>文件大小</span>
|
||||
<strong>{formatFileSize(item.fileSize)}</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<details className="history-disclosure" open>
|
||||
<summary>查看提示词</summary>
|
||||
<p>{item.prompt}</p>
|
||||
</details>
|
||||
|
||||
{item.revisedPrompt ? (
|
||||
<details className="history-disclosure">
|
||||
<summary>查看模型调整后的提示词</summary>
|
||||
<p>{item.revisedPrompt}</p>
|
||||
</details>
|
||||
) : null}
|
||||
|
||||
<div className="history-actions">
|
||||
<a
|
||||
className="ghost-button"
|
||||
href={item.imageUrl}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
查看原图
|
||||
</a>
|
||||
<a className="primary-button history-download-button" href={item.downloadUrl}>
|
||||
下载图片
|
||||
</a>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function formatFileSize(fileSize: number): string {
|
||||
if (fileSize < 1024) {
|
||||
return `${fileSize} B`;
|
||||
}
|
||||
|
||||
if (fileSize < 1024 * 1024) {
|
||||
return `${(fileSize / 1024).toFixed(1)} KB`;
|
||||
}
|
||||
|
||||
return `${(fileSize / (1024 * 1024)).toFixed(2)} MB`;
|
||||
}
|
||||
|
||||
function formatOutputFormatLabel(item: ImageHistoryItem): string {
|
||||
if (item.outputFormat) {
|
||||
return item.outputFormat.toUpperCase();
|
||||
}
|
||||
|
||||
return item.mimeType.replace(/^image\//, "").toUpperCase();
|
||||
}
|
||||
Reference in New Issue
Block a user