Commit 0aa3bd46 authored by yaoke.yk's avatar yaoke.yk

角色设定分镜ui调整

parent ea37c382
......@@ -303,6 +303,39 @@ export function CharacterGeneration() {
});
};
const handleGenerateFromModal = async () => {
if (!editingChar.name?.trim()) return;
const draft: Partial<Character> = {
...editingChar,
status: editingChar.status ?? "draft",
imagePrompt: editingChar.imagePrompt?.trim() || buildChineseCharacterPrompt(editingChar),
};
setSaving(true);
setGeneratingImageId(editingChar.id ? String(editingChar.id) : "draft");
try {
const saved = await saveCharacter.mutateAsync(draft);
const generated = await generateImage.mutateAsync(String(saved.id));
revokeImagePreviews(imagePreviews);
setEditingChar({
...generated,
imagePrompt: generated.imagePrompt ?? draft.imagePrompt,
});
setImagePreviews({
front: getCharacterViewUrl(generated, "front"),
side: getCharacterViewUrl(generated, "side"),
back: getCharacterViewUrl(generated, "back"),
});
setPendingFiles(emptyPendingFiles());
fileInputRefs.current.front && (fileInputRefs.current.front.value = "");
} catch (error) {
alert(`三视图生成失败: ${(error as Error).message}`);
} finally {
setSaving(false);
setGeneratingImageId(null);
}
};
const handleSaveState = async () => {
if (!editingCharacterId || !stateDraft.name.trim()) return;
setSavingState(true);
......@@ -679,15 +712,44 @@ export function CharacterGeneration() {
</div>
)}
</div>
{imagePreviews.front && (
{imagePreviews.front ? (
<div className="mt-2 flex items-center gap-2">
<button
type="button"
onClick={() => fileInputRefs.current.front?.click()}
className="mt-2 inline-flex items-center gap-1.5 rounded-md border border-border bg-card px-2.5 py-1 text-xs text-foreground hover:bg-muted"
className="inline-flex items-center gap-1.5 rounded-md border border-border bg-card px-2.5 py-1 text-xs text-foreground hover:bg-muted"
>
<Upload className="h-3.5 w-3.5" />
更换图片
</button>
<button
type="button"
onClick={handleGenerateFromModal}
disabled={!editingChar.name?.trim() || saving || uploadImage.isPending || generateImage.isPending}
className="inline-flex items-center gap-1.5 rounded-md border border-primary/30 bg-primary/5 px-2.5 py-1 text-xs text-primary hover:bg-primary/10 disabled:opacity-50"
>
{generatingImageId === (editingChar.id ? String(editingChar.id) : "draft") ? (
<Loader2 className="h-3.5 w-3.5 animate-spin" />
) : (
<Sparkles className="h-3.5 w-3.5" />
)}
AI重新生成
</button>
</div>
) : (
<button
type="button"
onClick={handleGenerateFromModal}
disabled={!editingChar.name?.trim() || saving || uploadImage.isPending || generateImage.isPending}
className="mt-2 inline-flex items-center gap-1.5 rounded-md border border-primary/30 bg-primary/5 px-2.5 py-1 text-xs text-primary hover:bg-primary/10 disabled:opacity-50"
>
{generatingImageId === (editingChar.id ? String(editingChar.id) : "draft") ? (
<Loader2 className="h-3.5 w-3.5 animate-spin" />
) : (
<Sparkles className="h-3.5 w-3.5" />
)}
{editingChar.id ? "AI生成三视图" : "保存并AI生成"}
</button>
)}
<input
ref={(node) => {
......
......@@ -116,6 +116,26 @@ export function SceneGeneration() {
});
};
const handleGenerateFromModal = async () => {
if (!editingScene.name?.trim()) return;
setSaving(true);
setGeneratingImageId(editingScene.id ? String(editingScene.id) : "draft");
try {
const saved = await saveScene.mutateAsync({ ...editingScene, status: editingScene.status ?? "draft" });
const generated = await generateImage.mutateAsync(String(saved.id));
revokeObjectPreview(imagePreview);
setEditingScene({ ...generated });
setImagePreview(generated.imageUrl ?? null);
setPendingFile(null);
if (fileInputRef.current) fileInputRef.current.value = "";
} catch (e) {
alert("图片生成失败: " + (e as Error).message);
} finally {
setSaving(false);
setGeneratingImageId(null);
}
};
const handleDelete = (scene: Scene) => {
if (!confirm(`确定要删除场景「${scene.name}」吗?`)) return;
deleteScene.mutate(scene.id, { onError: (e) => alert("删除失败: " + (e as Error).message) });
......@@ -404,15 +424,44 @@ export function SceneGeneration() {
</div>
)}
</div>
{imagePreview && (
{imagePreview ? (
<div className="mt-2 flex items-center gap-2">
<button
type="button"
onClick={() => fileInputRef.current?.click()}
className="mt-2 inline-flex items-center gap-1.5 rounded-md border border-border bg-card px-2.5 py-1 text-xs text-foreground hover:bg-muted"
className="inline-flex items-center gap-1.5 rounded-md border border-border bg-card px-2.5 py-1 text-xs text-foreground hover:bg-muted"
>
<Upload className="h-3.5 w-3.5" />
更换图片
</button>
<button
type="button"
onClick={handleGenerateFromModal}
disabled={!editingScene.name?.trim() || saving || uploadImage.isPending || generateImage.isPending}
className="inline-flex items-center gap-1.5 rounded-md border border-primary/30 bg-primary/5 px-2.5 py-1 text-xs text-primary hover:bg-primary/10 disabled:opacity-50"
>
{generatingImageId === (editingScene.id ? String(editingScene.id) : "draft") ? (
<Loader2 className="h-3.5 w-3.5 animate-spin" />
) : (
<Sparkles className="h-3.5 w-3.5" />
)}
AI重新生成
</button>
</div>
) : (
<button
type="button"
onClick={handleGenerateFromModal}
disabled={!editingScene.name?.trim() || saving || uploadImage.isPending || generateImage.isPending}
className="mt-2 inline-flex items-center gap-1.5 rounded-md border border-primary/30 bg-primary/5 px-2.5 py-1 text-xs text-primary hover:bg-primary/10 disabled:opacity-50"
>
{generatingImageId === (editingScene.id ? String(editingScene.id) : "draft") ? (
<Loader2 className="h-3.5 w-3.5 animate-spin" />
) : (
<Sparkles className="h-3.5 w-3.5" />
)}
{editingScene.id ? "AI生成场景图" : "保存并AI生成"}
</button>
)}
<input ref={fileInputRef} type="file" accept="image/*" className="hidden" onChange={handleFileSelect} />
</div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment