parent
ec6508c262
commit
ee208d5b16
|
|
@ -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']
|
||||
|
|
|
|||
|
|
@ -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<T extends (...args: any[]) => void>(fn: T, delay: number): T {
|
||||
let timeoutId: ReturnType<typeof setTimeout> | 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<HTMLElement | null> = 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)
|
||||
}
|
||||
|
||||
watch(() => props.totalFrames, () => {
|
||||
// 使用 setTimeout 避免频繁更新
|
||||
setTimeout(() => {
|
||||
const debouncedUpdatePxPerFrame = debounce(() => {
|
||||
updatePxPerFrame()
|
||||
}, 100)
|
||||
}, 150)
|
||||
|
||||
watch(() => props.totalFrames, () => {
|
||||
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}`,
|
||||
// })
|
||||
// },
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -138,21 +138,6 @@ onUnmounted(() => {
|
|||
</div>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<!-- <div
|
||||
v-for="hazard in row"
|
||||
:key="hazard.id"
|
||||
>
|
||||
<el-tooltip :content="hazard.tip" placement="top">
|
||||
<div
|
||||
class="hazard-block"
|
||||
:class="{ primary: hazard.level === 0, danger: hazard.level === 1 }"
|
||||
:style="getHazardStyle(hazard.start, hazard.end)"
|
||||
@click="handleHazardClick(hazard.id)"
|
||||
>
|
||||
{{ parseInt(hazard.id) + 1 }}
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</ElScrollbar>
|
||||
|
|
|
|||
|
|
@ -26,6 +26,28 @@ const ruleForm = reactive<RuleForm>({
|
|||
],
|
||||
})
|
||||
|
||||
const timerDisplay = ref('00:00:00')
|
||||
let timerInterval: ReturnType<typeof setInterval> | 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<FormRules<RuleForm>>({
|
||||
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,6 +133,7 @@ onMounted(() => {
|
|||
<el-container style="margin: 0; height: 100%; flex-direction: column;">
|
||||
<!-- 内层容器:自动填充剩余高度 -->
|
||||
<el-main class="main" style="flex: 1; min-height: 0;">
|
||||
<div class="flex flex-col items-center justify-center">
|
||||
<el-form
|
||||
ref="ruleFormRef"
|
||||
style="max-width: 600px"
|
||||
|
|
@ -137,7 +162,7 @@ onMounted(() => {
|
|||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-form-item v-if="!isRunningCheck">
|
||||
<el-button @click="resetForm(ruleFormRef)">
|
||||
重置表单
|
||||
</el-button>
|
||||
|
|
@ -154,6 +179,15 @@ onMounted(() => {
|
|||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-progress
|
||||
v-if="isRunningCheck"
|
||||
:percentage="50"
|
||||
:indeterminate="true"
|
||||
style="width: 100%;"
|
||||
>
|
||||
<el-text>{{ timerDisplay }}</el-text>
|
||||
</el-progress>
|
||||
</div>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</template>
|
||||
|
|
|
|||
Loading…
Reference in New Issue