feat: 解耦角色与模特并完善模特库
This commit is contained in:
@@ -29,7 +29,7 @@ import {
|
||||
} from "lucide-react";
|
||||
import type { AITask, Asset, ImageConversation, ImageConversationTask, ModelConfig, ModelEntity, Product, WorkbenchTask } from "../types";
|
||||
import { api } from "../api";
|
||||
import { ActorLibrary } from "../components/actor-library";
|
||||
import { ModelLibrary } from "../components/model-library";
|
||||
import { SkeletonRows } from "../components/loading";
|
||||
import { ConfirmModal, MediaLightbox } from "../components/overlays";
|
||||
import { Pager } from "../components/pager";
|
||||
@@ -664,8 +664,8 @@ export function ImageWorkbenchPage({
|
||||
const [count, setCount] = useState(mode === "image" ? "4" : "4");
|
||||
// 模特单选(长度恒 0/1);平台改回多选(P0③:可选多个平台,各出一组结果)
|
||||
const [pickedIds, setPickedIds] = useState<string[]>([]);
|
||||
// 模特库全屏弹窗(P0①:复用现成 ActorLibrary,mode=replace 选一个回填)
|
||||
const [actorLibOpen, setActorLibOpen] = useState(false);
|
||||
// 模特库全屏弹窗:选一个模特回填。
|
||||
const [modelLibOpen, setModelLibOpen] = useState(false);
|
||||
// 选中模特的展示名(从模特库/内联卡记下,导航头/CTA 显示用)
|
||||
const [pickedModelName, setPickedModelName] = useState("");
|
||||
// 商品库全屏选择器(P0④:pl-modal 多选 → 每个选中商品各起一批)
|
||||
@@ -749,7 +749,7 @@ export function ImageWorkbenchPage({
|
||||
|
||||
/* 模特卡数据来源:期2 改为「模特库」(顶级实体,引用其形象图当上身图参考)。
|
||||
映射 ModelEntity→Asset 形:id=形象图资产 id(选中即作 model_id 参考图),metadata 记 model_entity_id 溯源。
|
||||
无模特则回退到基线占位卡 Ava/Luna/Mia/Zoe。深埋 ActorLibrary 弹窗同源,逻辑不动。 */
|
||||
无模特则回退到基线占位卡 Ava/Luna/Mia/Zoe。深埋 ModelLibrary 弹窗同源。 */
|
||||
const [personAssets, setPersonAssets] = useState<Asset[]>([]);
|
||||
const loadModels = useCallback(() => {
|
||||
api.listModels({ pageSize: 200 })
|
||||
@@ -1854,7 +1854,7 @@ export function ImageWorkbenchPage({
|
||||
const name = selected?.name || pickedModelName;
|
||||
if (pickedIds[0]) {
|
||||
return (
|
||||
<button type="button" className="model-card selected" title="点击打开模特库更换模特" onClick={() => setActorLibOpen(true)}>
|
||||
<button type="button" className="model-card selected" title="点击打开模特库更换模特" onClick={() => setModelLibOpen(true)}>
|
||||
<div className="m-thumb">
|
||||
{url ? (
|
||||
<img className="m-thumb-img" src={url} alt={name} loading="lazy" />
|
||||
@@ -1869,7 +1869,7 @@ export function ImageWorkbenchPage({
|
||||
);
|
||||
}
|
||||
return (
|
||||
<button type="button" className="model-card model-card-empty" title="从模特库选择模特" onClick={() => setActorLibOpen(true)}>
|
||||
<button type="button" className="model-card model-card-empty" title="从模特库选择模特" onClick={() => setModelLibOpen(true)}>
|
||||
<div className="m-thumb m-thumb-empty">
|
||||
<Plus size={26} />
|
||||
<span className="m-empty-label">从模特库选择</span>
|
||||
@@ -2046,17 +2046,16 @@ export function ImageWorkbenchPage({
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{/* P0①:模特库全屏弹窗(复用现成 ActorLibrary,mode=replace 选一个回填到「选择模特」) */}
|
||||
<ActorLibrary
|
||||
open={actorLibOpen}
|
||||
{/* 模特库全屏弹窗:只选 Model,新增模特后刷新并回填。 */}
|
||||
<ModelLibrary
|
||||
open={modelLibOpen}
|
||||
mode="replace"
|
||||
assets={personAssets}
|
||||
onClose={() => setActorLibOpen(false)}
|
||||
onClose={() => setModelLibOpen(false)}
|
||||
onPick={(assetId, assetName) => {
|
||||
setPickedIds([assetId]);
|
||||
setPickedModelName(assetName || "");
|
||||
loadModels();
|
||||
setActorLibOpen(false);
|
||||
setModelLibOpen(false);
|
||||
}}
|
||||
onGenerate={(p) => onGenerate({ prompt: p, mode: "model", count: 1 })}
|
||||
onUpload={(file) => {
|
||||
@@ -2064,7 +2063,7 @@ export function ImageWorkbenchPage({
|
||||
fd.append("file", file);
|
||||
fd.append("name", file.name);
|
||||
fd.append("asset_type", "image");
|
||||
fd.append("category", "person");
|
||||
fd.append("category", "model_portrait");
|
||||
return api.uploadAsset(fd).catch(() => null);
|
||||
}}
|
||||
onRename={(assetId, name) => api.updateAsset(assetId, { name }).catch(() => null)}
|
||||
@@ -2218,7 +2217,7 @@ function ProductLibraryModal({
|
||||
onConfirm: () => void;
|
||||
navigate?: (page: Page) => void;
|
||||
}) {
|
||||
// Esc 关闭 + body 滚动锁(与 ActorLibrary 一致体感)
|
||||
// Esc 关闭 + body 滚动锁(与 ModelLibrary 一致体感)
|
||||
useEffect(() => {
|
||||
const onKey = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); };
|
||||
window.addEventListener("keydown", onKey);
|
||||
|
||||
Reference in New Issue
Block a user