import { useState, useCallback } from "react" import { useQueryClient } from "@tanstack/react-query" import { message } from "antd" import { deleteAsset, getAssetDiagnosis, batchDeleteAssets, batchTagAssets, batchClassifyAssets, batchMarkAssets, type BatchOperationResult, } from "@/api/assets" import type { AssetItem } from "../types" import type { SmartViewType } from "../components/BatchMarkModal" /** * 素材操作 Hook * 封装素材的诊断、删除、批量打标签、批量改分类、批量智能标记等操作, * 以及相关弹窗和结果展示的状态管理 */ interface UseAssetOperationsProps { selectedIds: Set setSelectedIds: (ids: Set) => void } export function useAssetOperations({ selectedIds, setSelectedIds }: UseAssetOperationsProps) { const queryClient = useQueryClient() /* ── 诊断状态 ── */ const [diagnosingId, setDiagnosingId] = useState(null) /* ── 批量操作弹窗状态 ── */ const [tagModalOpen, setTagModalOpen] = useState(false) const [classifyModalOpen, setClassifyModalOpen] = useState(false) const [markModalOpen, setMarkModalOpen] = useState(false) const [resultDrawerOpen, setResultDrawerOpen] = useState(false) /* ── 批量打标签表单 ── */ const [batchTagInput, setBatchTagInput] = useState("") const [batchTags, setBatchTags] = useState([]) const [tagMode, setTagMode] = useState<"add" | "replace">("add") /* ── 批量改分类表单 ── */ const [batchCategory, setBatchCategory] = useState("") /* ── 批量智能标记表单 ── */ const [batchSmartView, setBatchSmartView] = useState("recommended") /* ── 操作结果 ── */ const [operationResult, setOperationResult] = useState(null) const [operationTitle, setOperationTitle] = useState("") /* ── 批量操作 loading ── */ const [batchLoading, setBatchLoading] = useState(false) /* ── 刷新数据辅助函数 ── */ const invalidateAssets = useCallback(() => { queryClient.invalidateQueries({ queryKey: ["assets"] }) queryClient.invalidateQueries({ queryKey: ["asset-libraries"] }) }, [queryClient]) /* ── 诊断 ── */ const handleDiagnose = useCallback( async (asset: AssetItem) => { setDiagnosingId(asset.id) try { const result = await getAssetDiagnosis(asset.id) const score = result.readiness_score ?? "-" message.success(`"${asset.name}" 诊断完成,就绪分:${score}`) queryClient.invalidateQueries({ queryKey: ["assets"] }) } catch { message.error(`"${asset.name}" 诊断失败`) } finally { setDiagnosingId(null) } }, [queryClient], ) /* ── 单个素材删除 ── */ const handleSingleDelete = useCallback( async (assetId: string) => { try { await deleteAsset(assetId) invalidateAssets() // 从选中集合中移除 setSelectedIds( (() => { const next = new Set(selectedIds) next.delete(assetId) return next })(), ) message.success("素材已删除") } catch { message.error("删除失败,请重试") } }, [invalidateAssets, selectedIds, setSelectedIds], ) /* ── 显示操作结果 ── */ const showOperationResult = useCallback( (result: BatchOperationResult, title: string, clearSelection = true) => { setOperationResult(result) setOperationTitle(title) setResultDrawerOpen(true) if (clearSelection) setSelectedIds(new Set()) }, [setSelectedIds], ) /* ── 批量删除 ── */ const handleBatchDelete = useCallback(async () => { const ids = Array.from(selectedIds) setBatchLoading(true) try { const result = await batchDeleteAssets(ids) invalidateAssets() showOperationResult(result, "批量删除") if (result.failure_count === 0) { message.success(`成功删除 ${result.success_count} 个素材`) } else { message.warning( `删除完成:成功 ${result.success_count} 个,失败 ${result.failure_count} 个`, ) } } catch { message.error("批量删除失败,请重试") } finally { setBatchLoading(false) } }, [selectedIds, invalidateAssets, showOperationResult]) /* ── 批量打标签 ── */ const handleBatchTag = useCallback(async () => { if (batchTags.length === 0) { message.warning("请至少输入一个标签") return } const ids = Array.from(selectedIds) setBatchLoading(true) try { const result = await batchTagAssets({ asset_ids: ids, tags: batchTags, mode: tagMode, }) queryClient.invalidateQueries({ queryKey: ["assets"] }) showOperationResult(result, "批量打标签") setTagModalOpen(false) setBatchTags([]) setBatchTagInput("") setTagMode("add") if (result.failure_count === 0) { message.success(`成功为 ${result.success_count} 个素材打标签`) } else { message.warning( `打标签完成:成功 ${result.success_count} 个,失败 ${result.failure_count} 个`, ) } } catch { message.error("批量打标签失败,请重试") } finally { setBatchLoading(false) } }, [batchTags, selectedIds, tagMode, queryClient, showOperationResult]) /* ── 标签输入处理 ── */ const handleTagInputKeyDown = useCallback( (e: React.KeyboardEvent) => { if (e.key === "Enter" && batchTagInput.trim()) { e.preventDefault() const tag = batchTagInput.trim() if (!batchTags.includes(tag)) { setBatchTags([...batchTags, tag]) } setBatchTagInput("") } }, [batchTagInput, batchTags], ) const removeBatchTag = useCallback( (tag: string) => { setBatchTags(batchTags.filter((t) => t !== tag)) }, [batchTags], ) /* ── 批量改分类 ── */ const handleBatchClassify = useCallback(async () => { if (!batchCategory) { message.warning("请选择分类") return } const ids = Array.from(selectedIds) setBatchLoading(true) try { const result = await batchClassifyAssets({ asset_ids: ids, category: batchCategory, }) queryClient.invalidateQueries({ queryKey: ["assets"] }) showOperationResult(result, "批量改分类") setClassifyModalOpen(false) setBatchCategory("") if (result.failure_count === 0) { message.success(`成功将 ${result.success_count} 个素材改为「${batchCategory}」`) } else { message.warning( `改分类完成:成功 ${result.success_count} 个,失败 ${result.failure_count} 个`, ) } } catch { message.error("批量改分类失败,请重试") } finally { setBatchLoading(false) } }, [batchCategory, selectedIds, queryClient, showOperationResult]) /* ── 批量智能标记 ── */ const handleBatchMark = useCallback(async () => { const ids = Array.from(selectedIds) setBatchLoading(true) try { const result = await batchMarkAssets({ asset_ids: ids, smart_view: batchSmartView, }) queryClient.invalidateQueries({ queryKey: ["assets"] }) showOperationResult(result, "批量智能标记") setMarkModalOpen(false) const labelMap: Record = { recommended: "推荐", caution: "慎用", high_risk: "高风险", } if (result.failure_count === 0) { message.success( `成功将 ${result.success_count} 个素材标记为「${labelMap[batchSmartView]}」`, ) } else { message.warning( `智能标记完成:成功 ${result.success_count} 个,失败 ${result.failure_count} 个`, ) } } catch { message.error("批量智能标记失败,请重试") } finally { setBatchLoading(false) } }, [batchSmartView, selectedIds, queryClient, showOperationResult]) /* ── 关闭结果 Drawer ── */ const handleResultDrawerClose = useCallback(() => { setResultDrawerOpen(false) setOperationResult(null) }, []) return { // 诊断 diagnosingId, handleDiagnose, // 单个操作 handleSingleDelete, // 批量操作 loading batchLoading, // 批量打标签 tagModalOpen, setTagModalOpen, batchTagInput, setBatchTagInput, batchTags, setBatchTags, tagMode, setTagMode, handleBatchTag, handleTagInputKeyDown, removeBatchTag, // 批量改分类 classifyModalOpen, setClassifyModalOpen, batchCategory, setBatchCategory, handleBatchClassify, // 批量智能标记 markModalOpen, setMarkModalOpen, batchSmartView, setBatchSmartView, handleBatchMark, // 批量删除 handleBatchDelete, // 操作结果 resultDrawerOpen, operationResult, operationTitle, handleResultDrawerClose, } }