This commit is contained in:
@@ -592,15 +592,26 @@
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.project-placeholder {
|
||||
.project-placeholder,
|
||||
.project-preview {
|
||||
display: grid;
|
||||
height: 154px;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
overflow: hidden;
|
||||
border-bottom: 1px solid #a5a59f;
|
||||
background: #d8d8d3;
|
||||
}
|
||||
|
||||
.project-placeholder {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
|
||||
.project-preview img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.project-placeholder span {
|
||||
display: grid;
|
||||
place-items: end center;
|
||||
@@ -935,9 +946,9 @@
|
||||
font-size: 19px;
|
||||
}
|
||||
|
||||
.project-current > .project-placeholder {
|
||||
height: auto;
|
||||
min-height: 480px;
|
||||
.project-current > .project-placeholder,
|
||||
.project-current > .project-preview {
|
||||
height: 480px;
|
||||
border: 1px solid #73736d;
|
||||
}
|
||||
|
||||
@@ -945,6 +956,10 @@
|
||||
font-size: 80px;
|
||||
}
|
||||
|
||||
.project-current > .project-preview img {
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.project-actions {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
@@ -1013,7 +1028,8 @@
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.project-history li .project-placeholder {
|
||||
.project-history li .project-placeholder,
|
||||
.project-history li .project-preview {
|
||||
height: 88px;
|
||||
border: 0;
|
||||
}
|
||||
@@ -1439,8 +1455,9 @@
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.project-current > .project-placeholder {
|
||||
min-height: 360px;
|
||||
.project-current > .project-placeholder,
|
||||
.project-current > .project-preview {
|
||||
height: 360px;
|
||||
}
|
||||
|
||||
.local-only-footer {
|
||||
|
||||
@@ -231,6 +231,33 @@ function ProjectPlaceholder({ ratio, status }: { ratio: Ratio; status: ProjectSt
|
||||
);
|
||||
}
|
||||
|
||||
function ProjectPreview({ alt, imageId, loading = "lazy", projectId, ratio, status }: {
|
||||
alt: string;
|
||||
imageId: string | null;
|
||||
loading?: "eager" | "lazy";
|
||||
projectId: string;
|
||||
ratio: Ratio;
|
||||
status: ProjectStatus;
|
||||
}) {
|
||||
const [loadFailed, setLoadFailed] = useState(false);
|
||||
|
||||
useEffect(() => setLoadFailed(false), [imageId, projectId]);
|
||||
|
||||
if (!imageId || loadFailed) return <ProjectPlaceholder ratio={ratio} status={status} />;
|
||||
|
||||
return (
|
||||
<div className="project-preview" data-ratio={ratio} data-status={status}>
|
||||
<img
|
||||
alt={alt}
|
||||
decoding="async"
|
||||
loading={loading}
|
||||
onError={() => setLoadFailed(true)}
|
||||
src={`/api/v1/private-assets/projects/${encodeURIComponent(projectId)}/images/${encodeURIComponent(imageId)}`}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function WorkspacePage() {
|
||||
const promptId = useId();
|
||||
const [session, setSession] = useState<SessionPayload>();
|
||||
@@ -568,7 +595,13 @@ function ProjectCard({ activeLimitReached, busy, onPurge, onRestore, onSelect, o
|
||||
/>
|
||||
</label>
|
||||
) : null}
|
||||
<ProjectPlaceholder ratio={project.ratio} status={project.status} />
|
||||
<ProjectPreview
|
||||
alt={`${project.name}预览图`}
|
||||
imageId={project.current_image_id}
|
||||
projectId={project.project_id}
|
||||
ratio={project.ratio}
|
||||
status={project.status}
|
||||
/>
|
||||
<div className="project-card-body">
|
||||
<div><h3 title={project.name}>{project.name}</h3><span>{project.status === "failed_empty" ? "生成失败" : project.status === "trashed" ? "回收站" : "项目"}</span></div>
|
||||
<p>{project.successful_image_count} 张成功图 · {project.ratio}</p>
|
||||
@@ -953,7 +986,14 @@ export function ProjectDetailPage({ projectId }: { projectId: string }) {
|
||||
<div className="project-detail-grid">
|
||||
<section className="project-current" aria-labelledby="current-image-title">
|
||||
<header><h2 id="current-image-title">当前底图</h2><span>{project.pixel_width ?? 1080} × {project.pixel_height ?? 1440}</span></header>
|
||||
<ProjectPlaceholder ratio={project.ratio} status={project.status} />
|
||||
<ProjectPreview
|
||||
alt={`${project.name}当前底图`}
|
||||
imageId={project.current_image_id}
|
||||
loading="eager"
|
||||
projectId={project.project_id}
|
||||
ratio={project.ratio}
|
||||
status={project.status}
|
||||
/>
|
||||
<div className="project-actions">
|
||||
<button disabled={conflicted || atHistoryLimit} onClick={() => window.location.assign(`/app?continue=${project.project_id}`)} type="button">继续生成</button>
|
||||
{conflicted || !project.current_image_id ? <button disabled type="button">进入编辑器</button> : <a href={`/app/projects/${project.project_id}/editor`}>进入编辑器</a>}
|
||||
@@ -970,7 +1010,13 @@ export function ProjectDetailPage({ projectId }: { projectId: string }) {
|
||||
<ol>
|
||||
{project.images.toReversed().map((image, index) => (
|
||||
<li key={image.image_id} data-current={image.image_id === project.current_image_id}>
|
||||
<ProjectPlaceholder ratio={project.ratio} status="active" />
|
||||
<ProjectPreview
|
||||
alt={`生成结果 ${project.images.length - index}`}
|
||||
imageId={image.image_id}
|
||||
projectId={project.project_id}
|
||||
ratio={project.ratio}
|
||||
status="active"
|
||||
/>
|
||||
<div><strong>生成结果 {project.images.length - index}</strong><time dateTime={image.created_at}>{formatUpdatedAt(image.created_at)}</time><a href={`/api/v1/private-assets/projects/${project.project_id}/images/${image.image_id}`}>下载原始图</a></div>
|
||||
</li>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user