perf(timer): perf

This commit is contained in:
2026-03-04 16:00:20 +08:00
parent b3cdaa960e
commit 2402d685d7
8 changed files with 146 additions and 69 deletions

View File

@@ -48,7 +48,11 @@ import { useDescToolsStore } from "./useDescToolsStore"
import { useKeyboardStore } from "./useKeyBoardStore" import { useKeyboardStore } from "./useKeyBoardStore"
import { usePaperStore } from "./usePaperStore" import { usePaperStore } from "./usePaperStore"
import { useRightToolsStore } from "./useRightToolsStore" import { useRightToolsStore } from "./useRightToolsStore"
import { useIntervalStore, useTimerStore } from "./useTimerStore" import {
useIntervalStore,
useLabelTimeStore,
useTimerStore,
} from "./useTimerStore"
import { useTopToolsStore } from "./useTopToolsStore" import { useTopToolsStore } from "./useTopToolsStore"
import { findGroupKey } from "./util" import { findGroupKey } from "./util"
import { safeClone } from "./utils/clone" import { safeClone } from "./utils/clone"
@@ -157,19 +161,20 @@ const LabelPage = ({
// Project.LabelSchemaList[] // Project.LabelSchemaList[]
// >([]); // >([]);
const topRef = useRef<any>(null) const topRef = useRef<any>(null)
const autoSaveTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
const autoSaveInProgressRef = useRef(false)
const paperContainerRef = useRef<any>(null) const paperContainerRef = useRef<any>(null)
const qaToolContainerRef = useRef<any>(null) const qaToolContainerRef = useRef<any>(null)
const [isConfirmSave, setIsConfirmSave] = useState(false) const [isConfirmSave, setIsConfirmSave] = useState(false)
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [sizes, setSizes] = useState<number[]>([]) const [sizes, setSizes] = useState<number[]>([])
const { const labelData = useLabelStore((state) => state.label)
label: labelData, const setLabel = useLabelStore((state) => state.setLabel)
setLabel, const setStateStack = useLabelStore((state) => state.setStateStack)
setStateStack, const setLabelTime = useLabelTimeStore((state) => state.setLabelTime)
setLabelTime, const isAutoSave = useIntervalStore((state) => state.isAutoSave)
} = useLabelStore() const autoSaveGap = useIntervalStore((state) => state.autoSaveGap)
const { isAutoSave, autoSaveGap } = useIntervalStore()
const { const {
editMode, editMode,
@@ -1310,17 +1315,40 @@ const LabelPage = ({
// auto save // auto save
useEffect(() => { useEffect(() => {
if (isAutoSave && !isView) { let cancelled = false
const minute = 60 * 1000 if (autoSaveTimerRef.current !== null) {
useIntervalStore.getState().startTimer(() => { clearTimeout(autoSaveTimerRef.current)
topRef.current.handleSave() autoSaveTimerRef.current = null
}, autoSaveGap * minute)
} else {
useIntervalStore.getState().clearTimer()
} }
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 () => { return () => {
useIntervalStore.getState().clearTimer() cancelled = true
if (autoSaveTimerRef.current !== null) {
clearTimeout(autoSaveTimerRef.current)
autoSaveTimerRef.current = null
}
} }
}, [autoSaveGap, isAutoSave, isView]) }, [autoSaveGap, isAutoSave, isView])

View File

@@ -29,6 +29,7 @@ import { useDescToolsStore } from "../useDescToolsStore"
import { useKeyboardStore } from "../useKeyBoardStore" import { useKeyboardStore } from "../useKeyBoardStore"
import { useOtherToolsStore } from "../useOtherToolsStore" import { useOtherToolsStore } from "../useOtherToolsStore"
import { usePaperStore } from "../usePaperStore" import { usePaperStore } from "../usePaperStore"
import { useLabelTimeStore } from "../useTimerStore"
import { useTopToolsStore } from "../useTopToolsStore" import { useTopToolsStore } from "../useTopToolsStore"
import { getSubAttrStatus } from "../util" import { getSubAttrStatus } from "../util"
import { safeClone } from "../utils/clone" import { safeClone } from "../utils/clone"
@@ -50,7 +51,9 @@ const BottomTools: React.FC<BottomToolsComponentProps> = (props) => {
const itemRefs = useRef<Record<string, HTMLDivElement | null>>({}) const itemRefs = useRef<Record<string, HTMLDivElement | null>>({})
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 { const {
inputNumber, inputNumber,
setInputNumber, setInputNumber,
@@ -521,17 +524,16 @@ const BottomTools: React.FC<BottomToolsComponentProps> = (props) => {
review2_uid: 0, review2_uid: 0,
} }
data.key_frame = true data.key_frame = true
const currentLabelTime =
useLabelTimeStore.getState().labelTime
if (taskDetail.label_status === 2) { if (taskDetail.label_status === 2) {
data.label1_ts = data.label1_ts = currentLabelTime.label
useLabelStore.getState().labelTime.label
data.label1_uid = usePermissionStore.getState().user_id data.label1_uid = usePermissionStore.getState().user_id
} else if (taskDetail.label_status === 4) { } else if (taskDetail.label_status === 4) {
data.review1_ts = data.review1_ts = currentLabelTime.review1
useLabelStore.getState().labelTime.review1
data.review1_uid = usePermissionStore.getState().user_id data.review1_uid = usePermissionStore.getState().user_id
} else if (taskDetail.label_status === 6) { } else if (taskDetail.label_status === 6) {
data.review2_ts = data.review2_ts = currentLabelTime.review2
useLabelStore.getState().labelTime.review2
data.review2_uid = usePermissionStore.getState().user_id data.review2_uid = usePermissionStore.getState().user_id
} }
setKeyFrameData(activeImage, data) setKeyFrameData(activeImage, data)

View File

@@ -159,12 +159,12 @@ const PaperContainer = (
const { activeImage } = useBottomToolsStore() const { activeImage } = useBottomToolsStore()
const { const storeLabel = useLabelStore((state) => state.label)
label: storeLabel, const deleteOperation = useLabelStore((state) => state.deleteOperation)
deleteOperation, const setOperation = useLabelStore((state) => state.setOperation)
setOperation, const getLabelDetailDataByKeys = useLabelStore(
getLabelDetailDataByKeys, (state) => state.getLabelDetailDataByKeys
} = useLabelStore() )
const { const {
objectOperations, objectOperations,

View File

@@ -49,7 +49,8 @@ const RightGroupTools: React.FC<RightGroupToolsComponentProps> = (props) => {
}, },
[projectDetail?.label_schema_list] [projectDetail?.label_schema_list]
) )
const { setLabel, pushStateStack } = useLabelStore() const setLabel = useLabelStore((state) => state.setLabel)
const pushStateStack = useLabelStore((state) => state.pushStateStack)
const { pathGroupMap, groupPathVisible, setGroupPathVisible } = const { pathGroupMap, groupPathVisible, setGroupPathVisible } =
useRightToolsStore() useRightToolsStore()
const { activeImage } = useBottomToolsStore() const { activeImage } = useBottomToolsStore()

View File

@@ -78,7 +78,7 @@ export const renderOperationIcon = (
const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => { const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
const { labelState, projectDetail, taskDetail } = props const { labelState, projectDetail, taskDetail } = props
const { label: storeLabel } = useLabelStore() const storeLabel = useLabelStore((state) => state.label)
const { activeImage } = useBottomToolsStore() const { activeImage } = useBottomToolsStore()

View File

@@ -89,7 +89,7 @@ import { useBottomToolsStore } from "../useBottomToolsStore"
import { useDescToolsStore } from "../useDescToolsStore" import { useDescToolsStore } from "../useDescToolsStore"
import { usePaperStore } from "../usePaperStore" import { usePaperStore } from "../usePaperStore"
import { useRightToolsStore } from "../useRightToolsStore" import { useRightToolsStore } from "../useRightToolsStore"
import { useIntervalStore } from "../useTimerStore" import { useIntervalStore, useLabelTimeStore } from "../useTimerStore"
import { useTopToolsStore } from "../useTopToolsStore" import { useTopToolsStore } from "../useTopToolsStore"
import { getSubAttrStatus } from "../util" import { getSubAttrStatus } from "../util"
import { labelTypeMap, TaskStatusEnum } from "../utils/constants" import { labelTypeMap, TaskStatusEnum } from "../utils/constants"
@@ -133,6 +133,17 @@ const initialWorkLoad: WorkLoad = {
question_size: {}, 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 = ( const TopTools = (
props: TopToolsComponentProps, props: TopToolsComponentProps,
ref: React.Ref<unknown> | undefined ref: React.Ref<unknown> | undefined
@@ -157,13 +168,10 @@ const TopTools = (
const [duplicateName, setDuplicateName] = useState("") const [duplicateName, setDuplicateName] = useState("")
const [recoverOpen, setRecoverOpen] = useState(false) const [recoverOpen, setRecoverOpen] = useState(false)
const [taskOpen, setTaskOpen] = useState(false) const [taskOpen, setTaskOpen] = useState(false)
const { const labelData = useLabelStore((state) => state.label)
label: labelData, const setLabel = useLabelStore((state) => state.setLabel)
setLabel, const pushStateStack = useLabelStore((state) => state.pushStateStack)
labelTime, const setLabelTime = useLabelTimeStore((state) => state.setLabelTime)
setLabelTime,
pushStateStack,
} = useLabelStore()
const { activeImage } = useBottomToolsStore() const { activeImage } = useBottomToolsStore()
const { const {
isView, isView,
@@ -212,8 +220,10 @@ const TopTools = (
} = useTopToolsStore() } = useTopToolsStore()
const { pathGroupMap } = useRightToolsStore() const { pathGroupMap } = useRightToolsStore()
const { isAutoSave, setIsAutoSave, autoSaveGap, setAutoSaveGap } = const isAutoSave = useIntervalStore((state) => state.isAutoSave)
useIntervalStore() const setIsAutoSave = useIntervalStore((state) => state.setIsAutoSave)
const autoSaveGap = useIntervalStore((state) => state.autoSaveGap)
const setAutoSaveGap = useIntervalStore((state) => state.setAutoSaveGap)
// 当前是否可以操作功能按键 // 当前是否可以操作功能按键
const currentEditAuth = useMemo(() => { const currentEditAuth = useMemo(() => {
@@ -723,10 +733,11 @@ const TopTools = (
} }
} }
const currentLabelTime = useLabelTimeStore.getState().labelTime
const workTime = { const workTime = {
label_work_time: labelTime.label, label_work_time: currentLabelTime.label,
first_review_work_time: labelTime.review1, first_review_work_time: currentLabelTime.review1,
second_review_work_time: labelTime.review2, second_review_work_time: currentLabelTime.review2,
} }
// 生成新workload // 生成新workload
let time = 0 let time = 0
@@ -851,9 +862,6 @@ const TopTools = (
} }
}, [ }, [
labelCategories, labelCategories,
labelTime.label,
labelTime.review1,
labelTime.review2,
oldWorkLoad, oldWorkLoad,
pathGroupMap, pathGroupMap,
projectDetail, projectDetail,
@@ -1257,22 +1265,23 @@ const TopTools = (
console.log(error) console.log(error)
return return
} }
const currentLabelTime = useLabelTimeStore.getState().labelTime
const handleAction = (current_status: number) => { const handleAction = (current_status: number) => {
switch (current_status) { switch (current_status) {
case 2: case 2:
return { return {
commit_action: key || 1, commit_action: key || 1,
work_time: labelTime.label, work_time: currentLabelTime.label,
} }
case 4: case 4:
return { return {
commit_action: key || 2, commit_action: key || 2,
work_time: labelTime.review1, work_time: currentLabelTime.review1,
} }
case 6: case 6:
return { return {
commit_action: key || 3, commit_action: key || 3,
work_time: labelTime.review2, work_time: currentLabelTime.review2,
} }
default: default:
return { return {
@@ -1345,15 +1354,15 @@ const TopTools = (
useEffect(() => { useEffect(() => {
let timer = setInterval(() => { let timer = setInterval(() => {
if (isView) return if (isView) return
setLabelTime({ setLabelTime((prev) => ({
...labelTime, ...prev,
[currentTimeKey]: labelTime[currentTimeKey] + 1, [currentTimeKey]: prev[currentTimeKey] + 1,
}) }))
}, 1000) }, 1000)
return () => { return () => {
clearInterval(timer) clearInterval(timer)
} }
}, [currentTimeKey, isView, labelTime, setLabelTime]) }, [currentTimeKey, isView, setLabelTime])
const form = useForm({ const form = useForm({
initialValues: { initialValues: {
@@ -1748,9 +1757,7 @@ const TopTools = (
{isLabelTask && ( {isLabelTask && (
<Flex justify="space-between" align="center" gap="md" px="md"> <Flex justify="space-between" align="center" gap="md" px="md">
<Timer style={{ width: 20, height: 20 }} /> <Timer style={{ width: 20, height: 20 }} />
{dayjs <TaskTimerDisplay currentTimeKey={currentTimeKey} />
.duration(labelTime[currentTimeKey], "seconds")
.format("HH:mm:ss")}
</Flex> </Flex>
)} )}
<Flex justify="flex-end" align="center" gap="md" px="0" flex={1}> <Flex justify="flex-end" align="center" gap="md" px="0" flex={1}>

View File

@@ -7,6 +7,11 @@ import { safeClone } from "./utils/clone"
type LabelData = Map<string, Map<string, [number, any[], any, any[]][]>> type LabelData = Map<string, Map<string, [number, any[], any, any[]][]>>
type LabelPathTuple = [number, any[], any, any[]] type LabelPathTuple = [number, any[], any, any[]]
type LabelTime = {
label: number
review1: number
review2: number
}
const isPaperPointLike = (value: any) => { const isPaperPointLike = (value: any) => {
return ( return (
@@ -53,11 +58,7 @@ const normalizeLabelPathTuple = (path: LabelPathTuple): LabelPathTuple => {
interface LabelState { interface LabelState {
// pathId pathPoints detail hollowPathPoints // pathId pathPoints detail hollowPathPoints
label: LabelData label: LabelData
labelTime: { labelTime: LabelTime
label: number
review1: number
review2: number
}
labelDefaultComments: Comment[] labelDefaultComments: Comment[]
setLabel: (label: LabelData) => void setLabel: (label: LabelData) => void
stateStack: LabelData[] stateStack: LabelData[]
@@ -83,11 +84,9 @@ interface LabelState {
[keys: string]: any [keys: string]: any
} }
) => void ) => void
setLabelTime: (labelTime: { setLabelTime: (
label: number labelTime: LabelTime | ((prev: LabelTime) => LabelTime)
review1: number ) => void
review2: number
}) => void
setLabelDefaultComments: (arr: Comment[]) => void setLabelDefaultComments: (arr: Comment[]) => void
setImage: ( setImage: (
imageId: string, imageId: string,
@@ -312,7 +311,10 @@ export const useLabelStore = create(
storeSet((state) => { storeSet((state) => {
return { return {
...state, ...state,
labelTime, labelTime:
typeof labelTime === "function"
? labelTime(state.labelTime)
: labelTime,
} }
}), }),
setLabelDefaultComments: (labelDefaultComments) => setLabelDefaultComments: (labelDefaultComments) =>

View File

@@ -65,3 +65,40 @@ export const useIntervalStore = create<IntervalStore>((set, get) => ({
set((state) => ({ ...state, autoSaveGap: value })) 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<LabelTimeStore>((set) => ({
labelTime: initialLabelTimeState,
setLabelTime: (labelTime) =>
set((state) => ({
...state,
labelTime:
typeof labelTime === "function"
? labelTime(state.labelTime)
: labelTime,
})),
resetLabelTime: () =>
set((state) => ({
...state,
labelTime: initialLabelTimeState,
})),
}))