feat(core): 基础资产 Agent 化 — 商品三视图(image_edit 参考主图)+ 人物立绘/三视图异步生成 + 演员库/详情
- 商品三视图:有真实商品主图时走 image_edit,以主图为参考锁包装一致(品牌字/配色/外形/Logo) - 人物:据「某一版立绘」异步生成配套三视图(image_edit,worker 内跑),立绘/三视图各存版本可切换 - 基础资产出图改异步(run_base_asset_task / generate_triview_task),Web 层不被慢出图占住 - 前端:演员库浏览/新增、人物详情(立绘+三视图+版本切换/下载/查看大图)、按 busyKey 的单卡并发 loading(genBusy,替代全局 genBusyKey,各按钮互不阻塞) - dev:本地连云端 MySQL 复用连接(CONN_MAX_AGE/health check/connect_timeout),仅 development 生效 - 含 projects 测试补充 tsc + py_compile + 26 后端测试通过。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import type { CSSProperties, FormEvent } from "react";
|
||||
import type { Product, Project } from "../types";
|
||||
import type { Asset, Product, Project } from "../types";
|
||||
import type { Page } from "./route-config";
|
||||
import { ConfirmModal, Drawer, EmptyPanel } from "../components/overlays";
|
||||
import { Pager } from "../components/pager";
|
||||
@@ -28,9 +28,11 @@ type WizProductPayload = {
|
||||
selling_points?: Array<{ title: string; detail: string; sort_order: number }>;
|
||||
};
|
||||
|
||||
export function ProjectWizardPage({ products, projects = [], preselectProductId, onBack, onCreate, onCreateProduct }: {
|
||||
export function ProjectWizardPage({ products, projects = [], assets = [], preselectProductId, onBack, onCreate, onCreateProduct }: {
|
||||
products: Product[];
|
||||
projects?: Project[];
|
||||
// 团队 assets:商品的 cover_asset/images[].asset 是资产 id,需在此反查真 preview_url
|
||||
assets?: Asset[];
|
||||
// 从商品详情页「立即生成」进入时,预勾选该商品(而非默认第一个/耳机);id 不存在则回落默认。
|
||||
preselectProductId?: string;
|
||||
onBack: () => void;
|
||||
@@ -200,9 +202,18 @@ export function ProjectWizardPage({ products, projects = [], preselectProductId,
|
||||
}
|
||||
}
|
||||
|
||||
const productCover = (p: Product): CSSProperties | undefined => {
|
||||
const file = p.cover_asset || p.images?.find((img) => img.is_primary)?.asset || p.images?.[0]?.asset;
|
||||
return file ? ({ ["--mock-media-url"]: `url(${file})` } as CSSProperties) : undefined;
|
||||
// cover_asset / images[].asset 是资产 id,在团队 assets 里反查真 preview_url(对齐 products.tsx)
|
||||
const assetById = useMemo(() => {
|
||||
const map = new Map<string, Asset>();
|
||||
assets.forEach((a) => map.set(a.id, a));
|
||||
return map;
|
||||
}, [assets]);
|
||||
const productCoverUrl = (p: Product): string => {
|
||||
const firstImage = [...(p.images || [])].sort((a, b) => a.sort_order - b.sort_order)[0];
|
||||
const id = p.cover_asset || p.images?.find((img) => img.is_primary)?.asset || firstImage?.asset;
|
||||
if (!id) return "";
|
||||
const asset = assetById.get(id);
|
||||
return asset?.files?.find((f) => f.is_primary)?.preview_url || asset?.files?.[0]?.preview_url || "";
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -288,16 +299,23 @@ export function ProjectWizardPage({ products, projects = [], preselectProductId,
|
||||
{total === 0 ? (
|
||||
<div className="pp-empty">// NO MATCH<br />没有符合筛选条件的商品 <span className="reset" onClick={clearPickFilters}>[ 清空筛选 ]</span></div>
|
||||
) : (
|
||||
pageList.map((p) => (
|
||||
pageList.map((p) => {
|
||||
const coverUrl = productCoverUrl(p);
|
||||
return (
|
||||
<div className={`product-card${productId === p.id ? " selected" : ""}`} key={p.id} onClick={() => selectProduct(p.id)}>
|
||||
<div className={`placeholder product-thumb${productCover(p) ? " has-mock-media" : ""}`} style={productCover(p)}><span className="ph-frame">{p.title} · 1200×800</span></div>
|
||||
{coverUrl ? (
|
||||
<div className="product-thumb has-real-media"><img src={coverUrl} alt={p.title} loading="lazy" /></div>
|
||||
) : (
|
||||
<div className="placeholder product-thumb"><span className="ph-frame">{p.title} · 1200×800</span></div>
|
||||
)}
|
||||
<div className="product-body">
|
||||
<div className="product-name">{p.title}</div>
|
||||
<div className="product-cat">{p.category || "未分类"}</div>
|
||||
<div className="product-date">{(p.created_at || "").slice(0, 10)} 创建</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
);
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user