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:
zyc
2026-06-17 18:25:05 +08:00
co-authored by Claude Opus 4.8
parent 59ab3c0404
commit 2f6d3f5823
7 changed files with 92 additions and 71 deletions
+10 -16
View File
@@ -102,7 +102,6 @@ export function App() {
const [team, setTeam] = useState<Team | null>(null);
const [products, setProducts] = useState<Product[]>([]);
const [projects, setProjects] = useState<Project[]>([]);
const [assets, setAssets] = useState<Asset[]>([]);
const [teamMembers, setTeamMembers] = useState<TeamMember[]>([]);
const [modelConfigs, setModelConfigs] = useState<ModelConfig[]>([]);
const [aiTasks, setAiTasks] = useState<AITask[]>([]);
@@ -139,11 +138,11 @@ export function App() {
);
const loadData = useCallback(async () => {
const [productData, projectData, assetData, billingData, ledgerData, trendData, memberData, modelData, taskData, notificationData] =
// 资产不再全局取全:各页按需服务端分页懒加载(library/生图/商品详情/dashboard 自取)。
const [productData, projectData, billingData, ledgerData, trendData, memberData, modelData, taskData, notificationData] =
await Promise.all([
api.products(),
api.projects(),
api.allAssets(),
api.billingSummary().catch(() => null),
api.ledgers(1, 10).catch(() => ({ count: 0, page: 1, page_size: 10, results: [] as Ledger[] })),
api.billingTrend().catch(() => null),
@@ -154,7 +153,6 @@ export function App() {
]);
setProducts(productData.results);
setProjects(projectData.results);
setAssets(assetData);
setTeamMembers(memberData);
setModelConfigs(modelData?.results || []);
setAiTasks(taskData?.results || []);
@@ -564,13 +562,12 @@ export function App() {
function renderPage() {
switch (page) {
case "dashboard":
return <Dashboard products={products} projects={projects} assets={assets} billing={billing} userName={currentUser.username} navigate={navigate} />;
return <Dashboard products={products} projects={projects} billing={billing} userName={currentUser.username} navigate={navigate} />;
case "products":
return (
<ProductsPage
products={products}
projects={projects}
assets={assets}
navigate={navigate}
openProduct={(productId, tab) => navigate("productDetail", { productId, hash: tab === "videos" ? "videos" : undefined })}
onCreate={(payload) => action(() => api.createProduct(payload), "")}
@@ -585,7 +582,6 @@ export function App() {
<ProductsPage
products={products}
projects={projects}
assets={assets}
navigate={navigate}
openProduct={(productId, tab) => navigate("productDetail", { productId, hash: tab === "videos" ? "videos" : undefined })}
onCreate={(payload) => action(() => api.createProduct(payload), "")}
@@ -595,12 +591,11 @@ export function App() {
/>
);
case "productDetail":
if (!activeProduct) return <ProductsPage products={products} assets={assets} navigate={navigate} openProduct={(productId, tab) => navigate("productDetail", { productId, hash: tab === "videos" ? "videos" : undefined })} onCreate={(payload) => action(() => api.createProduct(payload), "")} onDelete={(productId) => action(() => api.deleteProduct(productId), "商品已删除")} />;
if (!activeProduct) return <ProductsPage products={products} navigate={navigate} openProduct={(productId, tab) => navigate("productDetail", { productId, hash: tab === "videos" ? "videos" : undefined })} onCreate={(payload) => action(() => api.createProduct(payload), "")} onDelete={(productId) => action(() => api.deleteProduct(productId), "商品已删除")} />;
return (
<ProductDetailPage
product={activeProduct}
projects={projects.filter((project) => project.product === activeProduct.id)}
assets={assets}
initialTab={route.hash === "videos" ? "videos" : "assets"}
navigate={navigate}
onUpdate={(payload) => action(() => api.updateProduct(activeProduct.id, payload), "商品已更新")}
@@ -626,7 +621,6 @@ export function App() {
<ProjectWizardPage
products={products}
projects={projects}
assets={assets}
preselectProductId={activeProductId}
onBack={() => navigate("projects")}
onCreate={async (payload) => {
@@ -699,13 +693,13 @@ export function App() {
/>
);
case "assetFactory":
return <AssetFactoryPage navigate={navigate} aiTasks={aiTasks} assets={assets} />;
return <AssetFactoryPage navigate={navigate} aiTasks={aiTasks} />;
case "imageOptimize":
return <ImageWorkbenchPage mode="image" products={products} assets={assets} modelConfigs={modelConfigs} initialProductId={activeProductId} onBack={() => navigate("assetFactory")} navigate={navigate} onGenerate={generateImages} onResume={resumeImages} />;
return <ImageWorkbenchPage mode="image" products={products} modelConfigs={modelConfigs} initialProductId={activeProductId} onBack={() => navigate("assetFactory")} navigate={navigate} onGenerate={generateImages} onResume={resumeImages} />;
case "modelPhoto":
return <ImageWorkbenchPage mode="model" products={products} assets={assets} modelConfigs={modelConfigs} initialProductId={activeProductId} onBack={() => navigate("assetFactory")} navigate={navigate} onGenerate={generateImages} onResume={resumeImages} />;
return <ImageWorkbenchPage mode="model" products={products} modelConfigs={modelConfigs} initialProductId={activeProductId} onBack={() => navigate("assetFactory")} navigate={navigate} onGenerate={generateImages} onResume={resumeImages} />;
case "platformCover":
return <ImageWorkbenchPage mode="cover" products={products} assets={assets} modelConfigs={modelConfigs} initialProductId={activeProductId} onBack={() => navigate("assetFactory")} navigate={navigate} onGenerate={generateImages} onResume={resumeImages} />;
return <ImageWorkbenchPage mode="cover" products={products} modelConfigs={modelConfigs} initialProductId={activeProductId} onBack={() => navigate("assetFactory")} navigate={navigate} onGenerate={generateImages} onResume={resumeImages} />;
case "modelPhotoDemoA":
return <ModelPhotoDemoPage variant="A" products={products} onBack={() => navigate("modelPhoto")} navigate={navigate} />;
case "modelPhotoDemoB":
@@ -715,7 +709,7 @@ export function App() {
case "settingsNotify":
return <SettingsPage user={currentUser} team={currentTeam} initialSection="notify" preferences={preferences} sessions={sessions} onSavePreferences={savePreferences} onRevokeSession={revokeSession} onRevokeOthers={revokeOtherSessions} onSaveProfile={saveProfile} onChangePassword={changeOwnPassword} onUploadAvatar={uploadOwnAvatar} onResetAvatar={resetOwnAvatar} onNotify={(text) => setNotice({ type: "success", text })} onLogout={logout} />;
default:
return <Dashboard products={products} projects={projects} assets={assets} billing={billing} navigate={navigate} />;
return <Dashboard products={products} projects={projects} billing={billing} navigate={navigate} />;
}
}
@@ -740,7 +734,7 @@ export function App() {
team={currentTeam}
products={products}
projects={projects}
assets={assets}
assets={[]}
billing={billing}
notice={notice}
unreadCount={unreadCount}