Compare commits

...

2 Commits

Author SHA1 Message Date
xiaoxia 4a73d810d2 style: fix prettier formatting in assets.ts
CI Build & Deploy Pipeline / Build Staging API Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Staging Worker Image (pull_request) Has been skipped
Auto Approve CI PRs / Auto Approve on CI Green (pull_request) Failing after 0s
Auto Merge CI PRs / Auto Merge on CI Green + Approved (pull_request) Failing after 0s
CI Build & Deploy Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production API Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production Web Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI Build & Deploy Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI Build & Deploy Pipeline / Deploy Production (pull_request) Has been skipped
CI Build & Deploy Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 7s
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
AI Code Review / AI Code Review (pull_request) Successful in 45s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 44s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 12s
2026-07-18 15:18:38 +08:00
xiaoxia 8166f2c939 fix: 修复素材库批量删除后白屏崩溃,批量操作结果归一化 + 列表防御性检查
CI Build & Deploy Pipeline / Build Staging API Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production API Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production Web Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI Build & Deploy Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI Build & Deploy Pipeline / Deploy Production (pull_request) Has been skipped
CI Build & Deploy Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 27s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 52s
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Has been skipped
Auto Merge CI PRs / Auto Merge on CI Green + Approved (pull_request) Successful in 57s
Auto Approve CI PRs / Auto Approve on CI Green (pull_request) Successful in 1m1s
AI Code Review / AI Code Review (pull_request) Successful in 1m22s
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
根因:batchDeleteAssets 直接返回 response.data,当后端返回字段缺失或格式不一致时,
Drawer 中 operationResult.succeeded.length 会报 Cannot read properties of undefined (reading 'length'),
导致整个页面白屏崩溃。

修复:
1. API 层新增 normalizeBatchResult(),统一归一化4个批量操作返回值,succeeded/failed 缺失时默认空数组
2. AssetLibrary 组件层增加 Array.isArray 防御,apiAssets / apiLibraries 非数组时兜底为空数组
3. 覆盖场景:删除全部素材、删除部分素材、删除后再上传,均不会白屏
2026-07-18 14:26:43 +08:00
2 changed files with 31 additions and 6 deletions
Regular → Executable
+23 -4
View File
@@ -390,6 +390,25 @@ export interface BatchOperationResult {
failure_count: number;
}
/** 统一批量操作结果归一化,防御后端字段缺失或格式不一致 */
const normalizeBatchResult = (
raw: Record<string, unknown>,
): BatchOperationResult => {
const succeeded = Array.isArray(raw.succeeded)
? (raw.succeeded as string[])
: [];
const failed = Array.isArray(raw.failed) ? (raw.failed as string[]) : [];
const success_count =
typeof raw.success_count === "number"
? raw.success_count
: succeeded.length;
const failure_count =
typeof raw.failure_count === "number" ? raw.failure_count : failed.length;
const total =
typeof raw.total === "number" ? raw.total : success_count + failure_count;
return { succeeded, failed, total, success_count, failure_count };
};
/** 批量删除素材 */
export const batchDeleteAssets = async (
assetIds: string[],
@@ -397,7 +416,7 @@ export const batchDeleteAssets = async (
const response = await apiClient.post("/assets/batch-delete", {
asset_ids: assetIds,
});
return response.data;
return normalizeBatchResult((response.data || {}) as Record<string, unknown>);
};
/** 批量打标签 */
@@ -407,7 +426,7 @@ export const batchTagAssets = async (data: {
mode: "add" | "replace";
}): Promise<BatchOperationResult> => {
const response = await apiClient.post("/assets/batch-tag", data);
return response.data;
return normalizeBatchResult((response.data || {}) as Record<string, unknown>);
};
/** 批量改分类 */
@@ -416,7 +435,7 @@ export const batchClassifyAssets = async (data: {
category: string;
}): Promise<BatchOperationResult> => {
const response = await apiClient.post("/assets/batch-classify", data);
return response.data;
return normalizeBatchResult((response.data || {}) as Record<string, unknown>);
};
/** 批量智能标记 */
@@ -425,5 +444,5 @@ export const batchMarkAssets = async (data: {
smart_view: "recommended" | "caution" | "high_risk";
}): Promise<BatchOperationResult> => {
const response = await apiClient.post("/assets/batch-mark", data);
return response.data;
return normalizeBatchResult((response.data || {}) as Record<string, unknown>);
};
+8 -2
View File
@@ -355,7 +355,10 @@ const AssetLibrary: React.FC = () => {
staleTime: 60_000,
});
const libraries = useMemo(() => apiLibraries.map(mapLibrary), [apiLibraries]);
const libraries = useMemo(
() => (Array.isArray(apiLibraries) ? apiLibraries : []).map(mapLibrary),
[apiLibraries],
);
/* ── 当前选中的素材库 ── */
const [activeLibId, setActiveLibId] = useState<string>("");
@@ -377,7 +380,10 @@ const AssetLibrary: React.FC = () => {
staleTime: 30_000,
});
const assets = useMemo(() => apiAssets.map(mapAsset), [apiAssets]);
const assets = useMemo(
() => (Array.isArray(apiAssets) ? apiAssets : []).map(mapAsset),
[apiAssets],
);
/* ── Mutations ── */
const createLibMutation = useMutation({