diff --git a/src/components.d.ts b/src/components.d.ts index 181d985..1507ddd 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -37,6 +37,7 @@ declare module 'vue' { ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] ElMenuItemGroup: typeof import('element-plus/es')['ElMenuItemGroup'] ElOption: typeof import('element-plus/es')['ElOption'] + ElProgress: typeof import('element-plus/es')['ElProgress'] ElRadio: typeof import('element-plus/es')['ElRadio'] ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] ElRow: typeof import('element-plus/es')['ElRow'] diff --git a/src/components/hazard_inspect/timeline/timeline.ts b/src/components/hazard_inspect/timeline/timeline.ts index b200381..163e7b2 100644 --- a/src/components/hazard_inspect/timeline/timeline.ts +++ b/src/components/hazard_inspect/timeline/timeline.ts @@ -1,8 +1,16 @@ -import type { Action } from 'element-plus' import type { ComputedRef, Ref } from 'vue' -import { ElMessage, ElMessageBox } from 'element-plus' +import { ElMessageBox } from 'element-plus' import { computed, nextTick, ref, watch } from 'vue' +function debounce void>(fn: T, delay: number): T { + let timeoutId: ReturnType | null = null + return ((...args: any[]) => { + if (timeoutId) + clearTimeout(timeoutId) + timeoutId = setTimeout(() => fn(...args), delay) + }) as T +} + export interface HazardData { ranges: number[] level: number @@ -48,13 +56,14 @@ export function useTimeline( emit: TimelineEmits, ) { const FPS = 30 - const pxPerFrame = ref(2) + const pxPerFrame = ref(1) const minPxPerFrame = ref(0.5) const maxPxPerFrame = ref(3) const timelineContainer: Ref = ref(null) const isDragging = ref(false) const isPlayheadDragging = ref(false) const isRulerDragging = ref(false) + const isMounted = ref(false) const dragStartX = ref(0) const scrollStartLeft = ref(0) const localCurrentFrame = ref(0) @@ -67,19 +76,27 @@ export function useTimeline( return Math.ceil(props.totalFrames / FPS) }) + /** + * 更新每帧像素值,根据容器宽度自动计算合适的缩放比例 + * 确保时间线能够完整显示所有帧,同时设置合理的缩放范围 + */ function updatePxPerFrame(): void { if (!timelineContainer.value || props.totalFrames === 0) return - const containerWidth = timelineContainer.value.clientWidth - 20 - const calculatedPxPerFrame = containerWidth / props.totalFrames - // 计算最小缩放倍数,确保能看到视频全长 + + const container = timelineContainer.value + if (!container.isConnected) + return + + const containerWidth = container.clientWidth - 20 + if (containerWidth <= 0) + return + const newMinPxPerFrame = Math.max(0.1, containerWidth / props.totalFrames) - // 只在必要时更新,避免无限循环 + if (Math.abs(newMinPxPerFrame - minPxPerFrame.value) > 0.01) { minPxPerFrame.value = newMinPxPerFrame } - // 最大值固定为3,不修改 - pxPerFrame.value = Math.max(minPxPerFrame.value, Math.min(calculatedPxPerFrame, maxPxPerFrame.value)) } function setZoom(value: number): void { @@ -336,16 +353,17 @@ export function useTimeline( }) }) + /** + * 组件挂载时的初始化函数 + * 设置时间线容器的初始状态并绑定事件监听器 + */ function initMounted(): void { + isMounted.value = true + const progressContainer = timelineContainer.value if (!progressContainer) return - // 使用 nextTick 确保容器已渲染 - nextTick(() => { - updatePxPerFrame() - }) - progressContainer.addEventListener('wheel', handleWheel, { passive: false }) progressContainer.addEventListener('mousedown', handleMouseDown) window.addEventListener('mousemove', handleMouseMove) @@ -353,34 +371,18 @@ export function useTimeline( window.addEventListener('mouseup', handleMouseUp) } + const debouncedUpdatePxPerFrame = debounce(() => { + updatePxPerFrame() + }, 150) + watch(() => props.totalFrames, () => { - // 使用 setTimeout 避免频繁更新 - setTimeout(() => { - updatePxPerFrame() - }, 100) + if (!isMounted.value) + return + debouncedUpdatePxPerFrame() }) - // 移除 resize 监听,避免无限循环 - // 监听容器宽度变化 - // let resizeObserver: ResizeObserver | null = null - - // function handleResize(): void { - // updatePxPerFrame() - // } - - // watch(() => timelineContainer.value, (newVal, oldVal) => { - // if (newVal && !oldVal) { - // // 使用 ResizeObserver 代替 window resize 监听 - // resizeObserver = new ResizeObserver(handleResize) - // resizeObserver.observe(newVal) - // } - // else if (!newVal && oldVal && resizeObserver) { - // resizeObserver.disconnect() - // resizeObserver = null - // } - // }) - function initUnmounted(): void { + isMounted.value = false window.removeEventListener('mousemove', handleMouseMove) window.removeEventListener('mousemove', handlePlayheadDrag) window.removeEventListener('mouseup', handleMouseUp) @@ -391,12 +393,6 @@ export function useTimeline( // if you want to disable its autofocus // autofocus: false, confirmButtonText: '确认', - // callback: (action: Action) => { - // ElMessage({ - // type: 'info', - // message: `action: ${action}`, - // }) - // }, }) } diff --git a/src/components/hazard_inspect/timeline/timeline.vue b/src/components/hazard_inspect/timeline/timeline.vue index 6950aaf..5b465bd 100644 --- a/src/components/hazard_inspect/timeline/timeline.vue +++ b/src/components/hazard_inspect/timeline/timeline.vue @@ -138,21 +138,6 @@ onUnmounted(() => { - diff --git a/src/pages/index.vue b/src/pages/index.vue index 41e82dc..65d4d36 100644 --- a/src/pages/index.vue +++ b/src/pages/index.vue @@ -26,6 +26,28 @@ const ruleForm = reactive({ ], }) +const timerDisplay = ref('00:00:00') +let timerInterval: ReturnType | null = null +let startTime: number = 0 + +function startTimer() { + startTime = Date.now() + timerInterval = setInterval(() => { + const elapsed = Date.now() - startTime + const hours = Math.floor(elapsed / 3600000) + const minutes = Math.floor((elapsed % 3600000) / 60000) + const seconds = Math.floor((elapsed % 60000) / 1000) + timerDisplay.value = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}` + }, 1000) +} + +function stopTimer() { + if (timerInterval) { + clearInterval(timerInterval) + timerInterval = null + } +} + const rules = reactive>({ vidPath: [ { @@ -50,6 +72,7 @@ async function runCheck(formEl: FormInstance | undefined) { }) if (valid) { isRunningCheck.value = true + startTimer() await runApi('/run', { vid_file: vidPaths.value[vidPaths.value.indexOf(ruleForm.vidPath)], run_sam3: ruleForm.function.includes('runSam3'), @@ -57,6 +80,7 @@ async function runCheck(formEl: FormInstance | undefined) { gen_report: ruleForm.function.includes('runGenerateReport'), }).then((res) => { isRunningCheck.value = false + stopTimer() // 判定是否成功运行 if (res !== 'error') { router.push({ @@ -109,51 +133,61 @@ onMounted(() => { - - - - - - - - - - 物体识别 - - - 隐患检查 - - - 生成报告 - - - 音频识别 - - - - - - 重置表单 - - - 查看结果 - - - 运行检查 - - - +
+ + + + + + + + + + 物体识别 + + + 隐患检查 + + + 生成报告 + + + 音频识别 + + + + + + 重置表单 + + + 查看结果 + + + 运行检查 + + + + + {{ timerDisplay }} + +