From 24db76dd04b8962133878be795612e247fbd6740 Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Mon, 27 Jul 2026 07:29:12 +0800 Subject: [PATCH] =?UTF-8?q?refactor(editing-planner):=20=E6=8B=86=E5=88=86?= =?UTF-8?q?=20useEditPlanClips=20Hook=EF=BC=8C=E6=8C=89=E8=81=8C=E8=B4=A3?= =?UTF-8?q?=E6=8B=86=E4=B8=BA=E5=88=97=E8=A1=A8=20+=20CRUD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 主文件 233→95 行(-59%),拆分为: - useEditPlanClipList: 列表查询 + 选中状态 - useEditPlanClipMutations: 创建/更新/删除/批量删除/重排序/素材导入 - index: 组合入口 + 本地撤销重做 导入路径保持向后兼容 --- .../hooks/useEditPlanClips/index.ts | 83 ++++++++++++ .../useEditPlanClips/useEditPlanClipList.ts | 49 ++++++++ .../useEditPlanClipMutations.ts} | 118 +++++------------- 3 files changed, 166 insertions(+), 84 deletions(-) create mode 100644 apps/web/src/pages/editing-planner/hooks/useEditPlanClips/index.ts create mode 100644 apps/web/src/pages/editing-planner/hooks/useEditPlanClips/useEditPlanClipList.ts rename apps/web/src/pages/editing-planner/hooks/{useEditPlanClips.ts => useEditPlanClips/useEditPlanClipMutations.ts} (59%) diff --git a/apps/web/src/pages/editing-planner/hooks/useEditPlanClips/index.ts b/apps/web/src/pages/editing-planner/hooks/useEditPlanClips/index.ts new file mode 100644 index 000000000..23a09faaa --- /dev/null +++ b/apps/web/src/pages/editing-planner/hooks/useEditPlanClips/index.ts @@ -0,0 +1,83 @@ +import { useUndoRedo } from "../useUndoRedo" +import type { EditPlanClip } from "@/api/template-editor" +import { useEditPlanClipList } from "./useEditPlanClipList" +import { useEditPlanClipMutations } from "./useEditPlanClipMutations" + +/** + * 模板片段管理 Hook + * 对接后端 PR#389 片段 CRUD API + * + * 功能: + * - 加载/刷新片段列表 + * - 单个增删改查 + * - 批量删除 + * - 拖拽重排序 + * - 从素材批量导入 + * - 乐观更新 + 撤销重做 + */ +export function useEditPlanClips(planId: string | undefined) { + // 列表数据 + 选中状态 + const { + clips, + clipsTotal, + clipsLoading, + refetchClips, + selectedClipId, + setSelectedClipId, + selectedClip, + } = useEditPlanClipList(planId) + + // CRUD 操作 + const mutations = useEditPlanClipMutations({ + planId, + selectedClipId, + setSelectedClipId, + clipsLength: clips.length, + }) + + // 本地撤销重做(供拖拽等即时操作使用) + const { + state: localClips, + set: setLocalClips, + undo, + redo, + canUndo, + canRedo, + reset: resetLocalClips, + } = useUndoRedo([]) + + return { + // 数据 + clips, + clipsTotal, + clipsLoading, + selectedClipId, + selectedClip, + // 选中 + setSelectedClipId, + // 操作 + addClip: mutations.addClip, + updateClip: mutations.updateClip, + removeClip: mutations.removeClip, + batchRemoveClips: mutations.batchRemoveClips, + reorderClips: mutations.reorderClips, + importFromAssets: mutations.importFromAssets, + refetchClips, + // 状态 + isCreating: mutations.isCreating, + isUpdating: mutations.isUpdating, + isDeleting: mutations.isDeleting, + isReordering: mutations.isReordering, + isImporting: mutations.isImporting, + // 本地撤销重做 + localClips, + setLocalClips, + undo, + redo, + canUndo, + canRedo, + resetLocalClips, + } +} + +export default useEditPlanClips diff --git a/apps/web/src/pages/editing-planner/hooks/useEditPlanClips/useEditPlanClipList.ts b/apps/web/src/pages/editing-planner/hooks/useEditPlanClips/useEditPlanClipList.ts new file mode 100644 index 000000000..cfc5c6a09 --- /dev/null +++ b/apps/web/src/pages/editing-planner/hooks/useEditPlanClips/useEditPlanClipList.ts @@ -0,0 +1,49 @@ +import { useState } from "react" +import { useQuery } from "@tanstack/react-query" +import type { EditPlanClip } from "@/api/template-editor" +import { getEditPlanClips } from "@/api/template-editor" + +const QUERY_KEY = "editPlanClips" + +interface UseEditPlanClipListResult { + clips: EditPlanClip[] + clipsTotal: number + clipsLoading: boolean + refetchClips: () => void + selectedClipId: string | null + setSelectedClipId: (id: string | null) => void + selectedClip: EditPlanClip | null +} + +/** + * 编辑计划片段列表 Hook + * 封装片段列表查询、选中状态 + */ +export function useEditPlanClipList(planId: string | undefined): UseEditPlanClipListResult { + const { + data: clipListData, + isLoading: clipsLoading, + refetch: refetchClips, + } = useQuery({ + queryKey: [QUERY_KEY, planId], + queryFn: () => getEditPlanClips(planId!, { limit: 500 }), + enabled: !!planId, + staleTime: 30_000, + }) + + const clips: EditPlanClip[] = clipListData?.items ?? [] + const clipsTotal = clipListData?.total ?? 0 + + const [selectedClipId, setSelectedClipId] = useState(null) + const selectedClip = clips.find((c) => c.id === selectedClipId) ?? null + + return { + clips, + clipsTotal, + clipsLoading, + refetchClips, + selectedClipId, + setSelectedClipId, + selectedClip, + } +} diff --git a/apps/web/src/pages/editing-planner/hooks/useEditPlanClips.ts b/apps/web/src/pages/editing-planner/hooks/useEditPlanClips/useEditPlanClipMutations.ts similarity index 59% rename from apps/web/src/pages/editing-planner/hooks/useEditPlanClips.ts rename to apps/web/src/pages/editing-planner/hooks/useEditPlanClips/useEditPlanClipMutations.ts index ec198cd6e..a1551c3b3 100644 --- a/apps/web/src/pages/editing-planner/hooks/useEditPlanClips.ts +++ b/apps/web/src/pages/editing-planner/hooks/useEditPlanClips/useEditPlanClipMutations.ts @@ -1,26 +1,12 @@ -/** - * 模板片段管理 Hook - * 对接后端 PR#389 片段 CRUD API,替代原来的 config.segments 模式 - * - * 功能: - * - 加载/刷新片段列表 - * - 单个增删改查 - * - 批量删除 - * - 拖拽重排序 - * - 从素材批量导入 - * - 乐观更新 + 撤销重做 - */ -import { useCallback, useState } from "react" +import { useCallback } from "react" import { message } from "antd" -import { useQuery, useQueryClient, useMutation } from "@tanstack/react-query" +import { useQueryClient, useMutation } from "@tanstack/react-query" import type { - EditPlanClip, CreateEditPlanClipRequest, UpdateEditPlanClipRequest, ClipReorderItem, } from "@/api/template-editor" import { - getEditPlanClips, createEditPlanClip, updateEditPlanClip, deleteEditPlanClip, @@ -28,51 +14,37 @@ import { batchDeleteEditPlanClips, createClipsFromAssets, } from "@/api/template-editor" -import { useUndoRedo } from "./useUndoRedo" const QUERY_KEY = "editPlanClips" -export function useEditPlanClips(planId: string | undefined) { +interface UseEditPlanClipMutationsOptions { + planId: string | undefined + selectedClipId: string | null + setSelectedClipId: (id: string | null) => void + clipsLength: number +} + +/** + * 编辑计划片段 CRUD Hook + * 封装创建、更新、删除、批量删除、重排序、素材导入等操作 + */ +export function useEditPlanClipMutations({ + planId, + selectedClipId, + setSelectedClipId, + clipsLength, +}: UseEditPlanClipMutationsOptions) { const queryClient = useQueryClient() - /* ── 片段列表查询 ── */ - const { - data: clipListData, - isLoading: clipsLoading, - refetch: refetchClips, - } = useQuery({ - queryKey: [QUERY_KEY, planId], - queryFn: () => getEditPlanClips(planId!, { limit: 500 }), - enabled: !!planId, - staleTime: 30_000, - }) - - const clips: EditPlanClip[] = clipListData?.items ?? [] - const clipsTotal = clipListData?.total ?? 0 - - /* ── 选中片段 ── */ - const [selectedClipId, setSelectedClipId] = useState(null) - const selectedClip = clips.find((c) => c.id === selectedClipId) ?? null - - /* ── 本地撤销/重做(用于拖拽等即时操作的回退) ── */ - const { - state: localClips, - set: setLocalClips, - undo, - redo, - canUndo, - canRedo, - reset: resetLocalClips, - } = useUndoRedo([]) - - // 当服务端数据变化时同步本地 - // 注意:实际使用时以服务端为准,本地仅用于拖拽等临时操作 + const invalidate = () => { + queryClient.invalidateQueries({ queryKey: [QUERY_KEY, planId] }) + } /* ── 创建片段 ── */ const createMutation = useMutation({ mutationFn: (data: CreateEditPlanClipRequest) => createEditPlanClip(planId!, data), onSuccess: () => { - queryClient.invalidateQueries({ queryKey: [QUERY_KEY, planId] }) + invalidate() message.success("片段已添加") }, onError: () => { @@ -83,10 +55,10 @@ export function useEditPlanClips(planId: string | undefined) { const addClip = useCallback( (data: Omit & { order?: number }) => { if (!planId) return - const order = data.order ?? clips.length + const order = data.order ?? clipsLength createMutation.mutate({ ...data, order }) }, - [planId, clips.length, createMutation], + [planId, clipsLength, createMutation], ) /* ── 更新片段 ── */ @@ -94,7 +66,7 @@ export function useEditPlanClips(planId: string | undefined) { mutationFn: ({ clipId, data }: { clipId: string; data: UpdateEditPlanClipRequest }) => updateEditPlanClip(planId!, clipId, data), onSuccess: () => { - queryClient.invalidateQueries({ queryKey: [QUERY_KEY, planId] }) + invalidate() }, onError: () => { message.error("更新片段失败") @@ -113,7 +85,7 @@ export function useEditPlanClips(planId: string | undefined) { const deleteMutation = useMutation({ mutationFn: (clipId: string) => deleteEditPlanClip(planId!, clipId), onSuccess: () => { - queryClient.invalidateQueries({ queryKey: [QUERY_KEY, planId] }) + invalidate() message.success("片段已删除") }, onError: () => { @@ -129,14 +101,14 @@ export function useEditPlanClips(planId: string | undefined) { } deleteMutation.mutate(clipId) }, - [planId, selectedClipId, deleteMutation], + [planId, selectedClipId, setSelectedClipId, deleteMutation], ) /* ── 批量删除 ── */ const batchDeleteMutation = useMutation({ mutationFn: (clipIds: string[]) => batchDeleteEditPlanClips(planId!, clipIds), onSuccess: (res) => { - queryClient.invalidateQueries({ queryKey: [QUERY_KEY, planId] }) + invalidate() message.success(`已删除 ${res.deleted_count} 个片段`) }, onError: () => { @@ -152,19 +124,18 @@ export function useEditPlanClips(planId: string | undefined) { } batchDeleteMutation.mutate(clipIds) }, - [planId, selectedClipId, batchDeleteMutation], + [planId, selectedClipId, setSelectedClipId, batchDeleteMutation], ) - /* ── 重排序(拖拽结束后一次性提交) ── */ + /* ── 重排序 ── */ const reorderMutation = useMutation({ mutationFn: (items: ClipReorderItem[]) => reorderEditPlanClips(planId!, items), onSuccess: () => { - queryClient.invalidateQueries({ queryKey: [QUERY_KEY, planId] }) + invalidate() }, onError: () => { message.error("排序失败") - // 失败后刷新回服务端状态 - queryClient.invalidateQueries({ queryKey: [QUERY_KEY, planId] }) + invalidate() }, }) @@ -180,7 +151,7 @@ export function useEditPlanClips(planId: string | undefined) { const importFromAssetsMutation = useMutation({ mutationFn: (assetIds: string[]) => createClipsFromAssets(planId!, assetIds), onSuccess: (res) => { - queryClient.invalidateQueries({ queryKey: [QUERY_KEY, planId] }) + invalidate() message.success(`已导入 ${res.created_count} 个素材片段`) }, onError: () => { @@ -197,37 +168,16 @@ export function useEditPlanClips(planId: string | undefined) { ) return { - // 数据 - clips, - clipsTotal, - clipsLoading, - selectedClipId, - selectedClip, - // 选中 - setSelectedClipId, - // 操作 addClip, updateClip, removeClip, batchRemoveClips, reorderClips, importFromAssets, - refetchClips, - // 状态 isCreating: createMutation.isPending, isUpdating: updateMutation.isPending, isDeleting: deleteMutation.isPending, isReordering: reorderMutation.isPending, isImporting: importFromAssetsMutation.isPending, - // 本地撤销重做(供拖拽等场景使用) - localClips, - setLocalClips, - undo, - redo, - canUndo, - canRedo, - resetLocalClips, } } - -export default useEditPlanClips -- 2.54.0