auto-save 2026-05-18 23:55 (+5, ~9)
This commit is contained in:
110
src/app/page.tsx
110
src/app/page.tsx
@@ -5,13 +5,25 @@ import PromptPanel from '@/components/PromptPanel';
|
||||
import ResultGrid from '@/components/ResultGrid';
|
||||
import Sidebar from '@/components/Sidebar';
|
||||
import PackPanel from '@/components/PackPanel';
|
||||
import type { GenImage, GenSession, GeneratePackResponse, GenerateResponse, PackKind } from '@/lib/types';
|
||||
import type {
|
||||
GenImage,
|
||||
GenSession,
|
||||
GenerateAllPacksResponse,
|
||||
GeneratePackResponse,
|
||||
GenerateResponse,
|
||||
LockCharacterResponse,
|
||||
PackKind,
|
||||
VideoGenerationResponse,
|
||||
} from '@/lib/types';
|
||||
|
||||
export default function Home() {
|
||||
const [sessions, setSessions] = useState<GenSession[]>([]);
|
||||
const [current, setCurrent] = useState<GenSession | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [loadingKind, setLoadingKind] = useState<PackKind | null>(null);
|
||||
const [allLoading, setAllLoading] = useState(false);
|
||||
const [characterLoading, setCharacterLoading] = useState(false);
|
||||
const [videoLoading, setVideoLoading] = useState(false);
|
||||
const [provider, setProvider] = useState<string>('?');
|
||||
const [sidebarOpen, setSidebarOpen] = useState(true);
|
||||
|
||||
@@ -85,6 +97,84 @@ export default function Home() {
|
||||
}
|
||||
}
|
||||
|
||||
async function reloadCurrent(sessionId: string) {
|
||||
const all = await refreshSessions();
|
||||
const updated = all.find(x => x.id === sessionId) ?? null;
|
||||
setCurrent(updated);
|
||||
return updated;
|
||||
}
|
||||
|
||||
async function handleLockCharacter(image: GenImage) {
|
||||
if (!current || characterLoading) return;
|
||||
setCharacterLoading(true);
|
||||
try {
|
||||
const r = await fetch('/api/character/lock', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ sessionId: current.id, imageId: image.id, force: true }),
|
||||
});
|
||||
if (!r.ok) {
|
||||
alert('角色锁定失败:' + (await r.text()));
|
||||
return;
|
||||
}
|
||||
const d: LockCharacterResponse = await r.json();
|
||||
setProvider(d.provider);
|
||||
await reloadCurrent(current.id);
|
||||
} finally {
|
||||
setCharacterLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleGenerateAll(image: GenImage) {
|
||||
if (!current || allLoading) return;
|
||||
setAllLoading(true);
|
||||
try {
|
||||
const r = await fetch('/api/packs/generate-all', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ sessionId: current.id, imageId: image.id }),
|
||||
});
|
||||
if (!r.ok) {
|
||||
alert('完整三包生成失败:' + (await r.text()));
|
||||
return;
|
||||
}
|
||||
const d: GenerateAllPacksResponse = await r.json();
|
||||
setProvider(d.provider);
|
||||
await reloadCurrent(current.id);
|
||||
} finally {
|
||||
setAllLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleGenerateVideo(image: GenImage, promptTemplate: string) {
|
||||
if (!current || videoLoading) return;
|
||||
setVideoLoading(true);
|
||||
try {
|
||||
const character = current.characterSpec
|
||||
? `${current.characterSpec.name},${current.characterSpec.oneLiner}`
|
||||
: current.prompt;
|
||||
const r = await fetch('/api/video/generate', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
prompt: promptTemplate.replace('{character}', character),
|
||||
imageUrl: image.url,
|
||||
duration: 6,
|
||||
ratio: '16:9',
|
||||
resolution: '1080p',
|
||||
}),
|
||||
});
|
||||
if (!r.ok) {
|
||||
alert('Seedance 视频提交失败:' + (await r.text()));
|
||||
return;
|
||||
}
|
||||
const d: VideoGenerationResponse = await r.json();
|
||||
alert(`Seedance 任务已提交:${d.taskId ?? d.status}`);
|
||||
} finally {
|
||||
setVideoLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex h-screen bg-[#FAFAFA]">
|
||||
<Sidebar
|
||||
@@ -107,9 +197,9 @@ export default function Home() {
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 pt-1">
|
||||
<span className={provider === 'poe' ? 'chip chip-live' : 'chip chip-mock'}>
|
||||
<span className={`w-1.5 h-1.5 rounded-full ${provider === 'poe' ? 'bg-emerald-500' : 'bg-amber-500'}`} />
|
||||
{provider === 'poe' ? 'Poe · 实时生图' : provider === 'mock' ? 'Mock · 占位图' : provider}
|
||||
<span className={provider === 'gpt' ? 'chip chip-live' : 'chip chip-mock'}>
|
||||
<span className={`w-1.5 h-1.5 rounded-full ${provider === 'gpt' ? 'bg-emerald-500' : 'bg-amber-500'}`} />
|
||||
{provider === 'gpt' ? 'GPT · 最高规格' : provider === 'mock' ? 'Mock · 占位图' : provider}
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
@@ -128,7 +218,17 @@ export default function Home() {
|
||||
<code className="text-[11px] text-zinc-400 font-mono">{current.id}</code>
|
||||
</div>
|
||||
<ResultGrid images={current.images} onAction={handleAction} />
|
||||
<PackPanel session={current} loadingKind={loadingKind} onGenerate={handleGeneratePack} />
|
||||
<PackPanel
|
||||
session={current}
|
||||
loadingKind={loadingKind}
|
||||
allLoading={allLoading}
|
||||
characterLoading={characterLoading}
|
||||
videoLoading={videoLoading}
|
||||
onGenerate={handleGeneratePack}
|
||||
onGenerateAll={handleGenerateAll}
|
||||
onLockCharacter={handleLockCharacter}
|
||||
onGenerateVideo={handleGenerateVideo}
|
||||
/>
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user