perf(core): 全站资产改服务端懒加载,撤掉 bootstrap 全量 allAssets
不再在启动时一次性拉全部资产(数据多时随之变慢/串多页),各页按需服务端分页/过滤: - 生图(ImageWorkbench 模特库按 category=person、AssetFactory 按 ai 生成图)页面内懒加载。 - 商品库/项目向导封面图改用后端内嵌 cover_preview_url / images[].preview_url,不再反查全局 assets。 - 商品详情 AI 素材按 ?product 懒加载(上传/生成/采用三视图后刷新)。 - 工作台资产总数走轻量 /assets/summary/。 - App bootstrap 删除 api.allAssets() 与全局 assets state;loadData 仅留必要全局数据。 - 后端 ?product 过滤并集补上 ProductImage 关联资产,等价前端原 belongsToProduct。 - pipeline 用项目详情已内嵌的 *_url 显示,调用处传空 assets(不再依赖全局全量)。 闸验证:9 页功能正常、无分页瀑布、无接口报错;首屏不再含资产全量拉取。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,7 @@ import {
|
||||
X
|
||||
} from "lucide-react";
|
||||
import type { AITask, Asset, ModelConfig, Product } from "../types";
|
||||
import { api } from "../api";
|
||||
import { MediaLightbox, SuccessModal } from "../components/overlays";
|
||||
import { Pager } from "../components/pager";
|
||||
import type { Page } from "./route-config";
|
||||
@@ -80,8 +81,17 @@ async function downloadImage(url: string, filename: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export function AssetFactoryPage({ navigate, aiTasks, assets = [] }: { navigate: (page: Page) => void; aiTasks: AITask[]; assets?: Asset[] }) {
|
||||
// 任务 → 生成结果图:按 asset.origin_task 关联,取首张有预览 URL 的图片文件(脚本/视频任务无图则留占位)
|
||||
export function AssetFactoryPage({ navigate, aiTasks }: { navigate: (page: Page) => void; aiTasks: AITask[] }) {
|
||||
// 任务 → 生成结果图:按 asset.origin_task 关联取缩略图。服务端懒加载最近的 AI 生成图(不再吃全局 assets 全量),
|
||||
// 覆盖近 200 张生成图的任务卡缩略图;更早的任务卡留占位。
|
||||
const [assets, setAssets] = useState<Asset[]>([]);
|
||||
useEffect(() => {
|
||||
let alive = true;
|
||||
api.assetsPage({ asset_type: "image", source: "ai_generated", pageSize: 200 })
|
||||
.then((res) => { if (alive) setAssets(res.results); })
|
||||
.catch(() => { if (alive) setAssets([]); });
|
||||
return () => { alive = false; };
|
||||
}, []);
|
||||
const taskImage = useMemo(() => {
|
||||
const map: Record<string, string> = {};
|
||||
for (const asset of assets) {
|
||||
@@ -471,7 +481,6 @@ type GenBatch = {
|
||||
export function ImageWorkbenchPage({
|
||||
mode,
|
||||
products,
|
||||
assets,
|
||||
modelConfigs,
|
||||
onBack,
|
||||
navigate,
|
||||
@@ -481,7 +490,6 @@ export function ImageWorkbenchPage({
|
||||
}: {
|
||||
mode: WorkMode;
|
||||
products: Product[];
|
||||
assets: Asset[];
|
||||
modelConfigs: ModelConfig[];
|
||||
onBack: () => void;
|
||||
navigate?: (page: Page) => void;
|
||||
@@ -523,9 +531,16 @@ export function ImageWorkbenchPage({
|
||||
|
||||
const imageModels = modelConfigs.filter((model) => model.capability.includes("image"));
|
||||
|
||||
/* 模特卡数据来源:assets 中 category==='person' 的真资产(显真图 preview_url + name);
|
||||
/* 模特卡数据来源:服务端按 category=person 懒加载(不再吃全局 assets 全量数组);
|
||||
无则回退到基线占位模特卡 Ava/Luna/Mia/Zoe */
|
||||
const personAssets = useMemo(() => assets.filter((item) => item.category === "person"), [assets]);
|
||||
const [personAssets, setPersonAssets] = useState<Asset[]>([]);
|
||||
useEffect(() => {
|
||||
let alive = true;
|
||||
api.assetsPage({ category: "person", pageSize: 200 })
|
||||
.then((res) => { if (alive) setPersonAssets(res.results); })
|
||||
.catch(() => { if (alive) setPersonAssets([]); });
|
||||
return () => { alive = false; };
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (product) setPrompt(meta.promptTemplate(product.title));
|
||||
|
||||
Reference in New Issue
Block a user