From 2402d685d7ee796a2d73a74c13507b0856221420 Mon Sep 17 00:00:00 2001 From: shay7sev Date: Wed, 4 Mar 2026 16:00:20 +0800 Subject: [PATCH] perf(timer): perf --- components/label/LabelNossr.tsx | 60 +++++++++++++----- components/label/components/BottomTools.tsx | 16 ++--- .../label/components/PaperContainer.tsx | 12 ++-- .../label/components/RightGroupTools.tsx | 3 +- .../label/components/RightObjectTools.tsx | 2 +- components/label/components/TopTools.tsx | 61 +++++++++++-------- components/label/store.ts | 24 ++++---- components/label/useTimerStore.ts | 37 +++++++++++ 8 files changed, 146 insertions(+), 69 deletions(-) diff --git a/components/label/LabelNossr.tsx b/components/label/LabelNossr.tsx index 72bd448..41f0736 100644 --- a/components/label/LabelNossr.tsx +++ b/components/label/LabelNossr.tsx @@ -48,7 +48,11 @@ import { useDescToolsStore } from "./useDescToolsStore" import { useKeyboardStore } from "./useKeyBoardStore" import { usePaperStore } from "./usePaperStore" import { useRightToolsStore } from "./useRightToolsStore" -import { useIntervalStore, useTimerStore } from "./useTimerStore" +import { + useIntervalStore, + useLabelTimeStore, + useTimerStore, +} from "./useTimerStore" import { useTopToolsStore } from "./useTopToolsStore" import { findGroupKey } from "./util" import { safeClone } from "./utils/clone" @@ -157,19 +161,20 @@ const LabelPage = ({ // Project.LabelSchemaList[] // >([]); const topRef = useRef(null) + const autoSaveTimerRef = useRef | null>(null) + const autoSaveInProgressRef = useRef(false) const paperContainerRef = useRef(null) const qaToolContainerRef = useRef(null) const [isConfirmSave, setIsConfirmSave] = useState(false) const [loading, setLoading] = useState(false) const [sizes, setSizes] = useState([]) - const { - label: labelData, - setLabel, - setStateStack, - setLabelTime, - } = useLabelStore() - const { isAutoSave, autoSaveGap } = useIntervalStore() + const labelData = useLabelStore((state) => state.label) + const setLabel = useLabelStore((state) => state.setLabel) + const setStateStack = useLabelStore((state) => state.setStateStack) + const setLabelTime = useLabelTimeStore((state) => state.setLabelTime) + const isAutoSave = useIntervalStore((state) => state.isAutoSave) + const autoSaveGap = useIntervalStore((state) => state.autoSaveGap) const { editMode, @@ -1310,17 +1315,40 @@ const LabelPage = ({ // auto save useEffect(() => { - if (isAutoSave && !isView) { - const minute = 60 * 1000 - useIntervalStore.getState().startTimer(() => { - topRef.current.handleSave() - }, autoSaveGap * minute) - } else { - useIntervalStore.getState().clearTimer() + let cancelled = false + if (autoSaveTimerRef.current !== null) { + clearTimeout(autoSaveTimerRef.current) + autoSaveTimerRef.current = null } + if (!isAutoSave || isView) return + const minute = 60 * 1000 + const delay = autoSaveGap * minute + const scheduleNext = () => { + autoSaveTimerRef.current = setTimeout(async () => { + if (cancelled) return + if (autoSaveInProgressRef.current) { + scheduleNext() + return + } + autoSaveInProgressRef.current = true + try { + await topRef.current?.handleSave?.() + } catch (error) { + console.error("[LabelNossr] auto save failed", error) + } finally { + autoSaveInProgressRef.current = false + if (!cancelled) scheduleNext() + } + }, delay) + } + scheduleNext() return () => { - useIntervalStore.getState().clearTimer() + cancelled = true + if (autoSaveTimerRef.current !== null) { + clearTimeout(autoSaveTimerRef.current) + autoSaveTimerRef.current = null + } } }, [autoSaveGap, isAutoSave, isView]) diff --git a/components/label/components/BottomTools.tsx b/components/label/components/BottomTools.tsx index 265b36a..682cdf2 100644 --- a/components/label/components/BottomTools.tsx +++ b/components/label/components/BottomTools.tsx @@ -29,6 +29,7 @@ import { useDescToolsStore } from "../useDescToolsStore" import { useKeyboardStore } from "../useKeyBoardStore" import { useOtherToolsStore } from "../useOtherToolsStore" import { usePaperStore } from "../usePaperStore" +import { useLabelTimeStore } from "../useTimerStore" import { useTopToolsStore } from "../useTopToolsStore" import { getSubAttrStatus } from "../util" import { safeClone } from "../utils/clone" @@ -50,7 +51,9 @@ const BottomTools: React.FC = (props) => { const itemRefs = useRef>({}) - const { label: storeLabel, setLabel, pushStateStack } = useLabelStore() + const storeLabel = useLabelStore((state) => state.label) + const setLabel = useLabelStore((state) => state.setLabel) + const pushStateStack = useLabelStore((state) => state.pushStateStack) const { inputNumber, setInputNumber, @@ -521,17 +524,16 @@ const BottomTools: React.FC = (props) => { review2_uid: 0, } data.key_frame = true + const currentLabelTime = + useLabelTimeStore.getState().labelTime if (taskDetail.label_status === 2) { - data.label1_ts = - useLabelStore.getState().labelTime.label + data.label1_ts = currentLabelTime.label data.label1_uid = usePermissionStore.getState().user_id } else if (taskDetail.label_status === 4) { - data.review1_ts = - useLabelStore.getState().labelTime.review1 + data.review1_ts = currentLabelTime.review1 data.review1_uid = usePermissionStore.getState().user_id } else if (taskDetail.label_status === 6) { - data.review2_ts = - useLabelStore.getState().labelTime.review2 + data.review2_ts = currentLabelTime.review2 data.review2_uid = usePermissionStore.getState().user_id } setKeyFrameData(activeImage, data) diff --git a/components/label/components/PaperContainer.tsx b/components/label/components/PaperContainer.tsx index 6c36864..6ee87be 100644 --- a/components/label/components/PaperContainer.tsx +++ b/components/label/components/PaperContainer.tsx @@ -159,12 +159,12 @@ const PaperContainer = ( const { activeImage } = useBottomToolsStore() - const { - label: storeLabel, - deleteOperation, - setOperation, - getLabelDetailDataByKeys, - } = useLabelStore() + const storeLabel = useLabelStore((state) => state.label) + const deleteOperation = useLabelStore((state) => state.deleteOperation) + const setOperation = useLabelStore((state) => state.setOperation) + const getLabelDetailDataByKeys = useLabelStore( + (state) => state.getLabelDetailDataByKeys + ) const { objectOperations, diff --git a/components/label/components/RightGroupTools.tsx b/components/label/components/RightGroupTools.tsx index 3265bad..e602285 100644 --- a/components/label/components/RightGroupTools.tsx +++ b/components/label/components/RightGroupTools.tsx @@ -49,7 +49,8 @@ const RightGroupTools: React.FC = (props) => { }, [projectDetail?.label_schema_list] ) - const { setLabel, pushStateStack } = useLabelStore() + const setLabel = useLabelStore((state) => state.setLabel) + const pushStateStack = useLabelStore((state) => state.pushStateStack) const { pathGroupMap, groupPathVisible, setGroupPathVisible } = useRightToolsStore() const { activeImage } = useBottomToolsStore() diff --git a/components/label/components/RightObjectTools.tsx b/components/label/components/RightObjectTools.tsx index a0dfe15..315101a 100644 --- a/components/label/components/RightObjectTools.tsx +++ b/components/label/components/RightObjectTools.tsx @@ -78,7 +78,7 @@ export const renderOperationIcon = ( const RightObjectTools: React.FC = (props) => { const { labelState, projectDetail, taskDetail } = props - const { label: storeLabel } = useLabelStore() + const storeLabel = useLabelStore((state) => state.label) const { activeImage } = useBottomToolsStore() diff --git a/components/label/components/TopTools.tsx b/components/label/components/TopTools.tsx index f60774c..630c02e 100644 --- a/components/label/components/TopTools.tsx +++ b/components/label/components/TopTools.tsx @@ -89,7 +89,7 @@ import { useBottomToolsStore } from "../useBottomToolsStore" import { useDescToolsStore } from "../useDescToolsStore" import { usePaperStore } from "../usePaperStore" import { useRightToolsStore } from "../useRightToolsStore" -import { useIntervalStore } from "../useTimerStore" +import { useIntervalStore, useLabelTimeStore } from "../useTimerStore" import { useTopToolsStore } from "../useTopToolsStore" import { getSubAttrStatus } from "../util" import { labelTypeMap, TaskStatusEnum } from "../utils/constants" @@ -133,6 +133,17 @@ const initialWorkLoad: WorkLoad = { question_size: {}, } +const TaskTimerDisplay = ({ + currentTimeKey, +}: { + currentTimeKey: "label" | "review1" | "review2" +}) => { + const currentTime = useLabelTimeStore( + (state) => state.labelTime[currentTimeKey] + ) + return <>{dayjs.duration(currentTime, "seconds").format("HH:mm:ss")} +} + const TopTools = ( props: TopToolsComponentProps, ref: React.Ref | undefined @@ -157,13 +168,10 @@ const TopTools = ( const [duplicateName, setDuplicateName] = useState("") const [recoverOpen, setRecoverOpen] = useState(false) const [taskOpen, setTaskOpen] = useState(false) - const { - label: labelData, - setLabel, - labelTime, - setLabelTime, - pushStateStack, - } = useLabelStore() + const labelData = useLabelStore((state) => state.label) + const setLabel = useLabelStore((state) => state.setLabel) + const pushStateStack = useLabelStore((state) => state.pushStateStack) + const setLabelTime = useLabelTimeStore((state) => state.setLabelTime) const { activeImage } = useBottomToolsStore() const { isView, @@ -212,8 +220,10 @@ const TopTools = ( } = useTopToolsStore() const { pathGroupMap } = useRightToolsStore() - const { isAutoSave, setIsAutoSave, autoSaveGap, setAutoSaveGap } = - useIntervalStore() + const isAutoSave = useIntervalStore((state) => state.isAutoSave) + const setIsAutoSave = useIntervalStore((state) => state.setIsAutoSave) + const autoSaveGap = useIntervalStore((state) => state.autoSaveGap) + const setAutoSaveGap = useIntervalStore((state) => state.setAutoSaveGap) // 当前是否可以操作功能按键 const currentEditAuth = useMemo(() => { @@ -723,10 +733,11 @@ const TopTools = ( } } + const currentLabelTime = useLabelTimeStore.getState().labelTime const workTime = { - label_work_time: labelTime.label, - first_review_work_time: labelTime.review1, - second_review_work_time: labelTime.review2, + label_work_time: currentLabelTime.label, + first_review_work_time: currentLabelTime.review1, + second_review_work_time: currentLabelTime.review2, } // 生成新workload let time = 0 @@ -851,9 +862,6 @@ const TopTools = ( } }, [ labelCategories, - labelTime.label, - labelTime.review1, - labelTime.review2, oldWorkLoad, pathGroupMap, projectDetail, @@ -1257,22 +1265,23 @@ const TopTools = ( console.log(error) return } + const currentLabelTime = useLabelTimeStore.getState().labelTime const handleAction = (current_status: number) => { switch (current_status) { case 2: return { commit_action: key || 1, - work_time: labelTime.label, + work_time: currentLabelTime.label, } case 4: return { commit_action: key || 2, - work_time: labelTime.review1, + work_time: currentLabelTime.review1, } case 6: return { commit_action: key || 3, - work_time: labelTime.review2, + work_time: currentLabelTime.review2, } default: return { @@ -1345,15 +1354,15 @@ const TopTools = ( useEffect(() => { let timer = setInterval(() => { if (isView) return - setLabelTime({ - ...labelTime, - [currentTimeKey]: labelTime[currentTimeKey] + 1, - }) + setLabelTime((prev) => ({ + ...prev, + [currentTimeKey]: prev[currentTimeKey] + 1, + })) }, 1000) return () => { clearInterval(timer) } - }, [currentTimeKey, isView, labelTime, setLabelTime]) + }, [currentTimeKey, isView, setLabelTime]) const form = useForm({ initialValues: { @@ -1748,9 +1757,7 @@ const TopTools = ( {isLabelTask && ( - {dayjs - .duration(labelTime[currentTimeKey], "seconds") - .format("HH:mm:ss")} + )} diff --git a/components/label/store.ts b/components/label/store.ts index 9bc1118..a479563 100644 --- a/components/label/store.ts +++ b/components/label/store.ts @@ -7,6 +7,11 @@ import { safeClone } from "./utils/clone" type LabelData = Map> type LabelPathTuple = [number, any[], any, any[]] +type LabelTime = { + label: number + review1: number + review2: number +} const isPaperPointLike = (value: any) => { return ( @@ -53,11 +58,7 @@ const normalizeLabelPathTuple = (path: LabelPathTuple): LabelPathTuple => { interface LabelState { // pathId pathPoints detail hollowPathPoints label: LabelData - labelTime: { - label: number - review1: number - review2: number - } + labelTime: LabelTime labelDefaultComments: Comment[] setLabel: (label: LabelData) => void stateStack: LabelData[] @@ -83,11 +84,9 @@ interface LabelState { [keys: string]: any } ) => void - setLabelTime: (labelTime: { - label: number - review1: number - review2: number - }) => void + setLabelTime: ( + labelTime: LabelTime | ((prev: LabelTime) => LabelTime) + ) => void setLabelDefaultComments: (arr: Comment[]) => void setImage: ( imageId: string, @@ -312,7 +311,10 @@ export const useLabelStore = create( storeSet((state) => { return { ...state, - labelTime, + labelTime: + typeof labelTime === "function" + ? labelTime(state.labelTime) + : labelTime, } }), setLabelDefaultComments: (labelDefaultComments) => diff --git a/components/label/useTimerStore.ts b/components/label/useTimerStore.ts index 52faf03..0471920 100644 --- a/components/label/useTimerStore.ts +++ b/components/label/useTimerStore.ts @@ -65,3 +65,40 @@ export const useIntervalStore = create((set, get) => ({ set((state) => ({ ...state, autoSaveGap: value })) }, })) + +export interface LabelTimeState { + label: number + review1: number + review2: number +} + +interface LabelTimeStore { + labelTime: LabelTimeState + setLabelTime: ( + labelTime: LabelTimeState | ((prev: LabelTimeState) => LabelTimeState) + ) => void + resetLabelTime: () => void +} + +const initialLabelTimeState: LabelTimeState = { + label: 0, + review1: 0, + review2: 0, +} + +export const useLabelTimeStore = create((set) => ({ + labelTime: initialLabelTimeState, + setLabelTime: (labelTime) => + set((state) => ({ + ...state, + labelTime: + typeof labelTime === "function" + ? labelTime(state.labelTime) + : labelTime, + })), + resetLabelTime: () => + set((state) => ({ + ...state, + labelTime: initialLabelTimeState, + })), +}))