148 lines
4.3 KiB
Vue
148 lines
4.3 KiB
Vue
<script setup lang="ts">
|
|
import type { Arrayable } from '@vueuse/core'
|
|
import type { TimelineEmits, TimelineProps } from './timeline'
|
|
import { InfoFilled } from '@element-plus/icons-vue'
|
|
import { ElScrollbar, ElSlider } from 'element-plus'
|
|
import { onMounted, onUnmounted } from 'vue'
|
|
import { useTimeline } from './timeline'
|
|
import './timeline.css'
|
|
|
|
const props = defineProps<TimelineProps>()
|
|
|
|
const emit = defineEmits<TimelineEmits>()
|
|
|
|
const {
|
|
timelineContainer,
|
|
trackWidth,
|
|
timeTicks,
|
|
secondTicks,
|
|
playheadPos,
|
|
trackRows,
|
|
pxPerFrame,
|
|
minPxPerFrame,
|
|
maxPxPerFrame,
|
|
setZoom,
|
|
getHazardStyle,
|
|
handleHazardClick,
|
|
handleMouseMove,
|
|
handleMouseUp,
|
|
handlePlayheadMouseDown,
|
|
handleRulerClick,
|
|
initMounted,
|
|
openMessageBox,
|
|
initUnmounted,
|
|
} = useTimeline(props, emit)
|
|
|
|
onMounted(() => {
|
|
initMounted()
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
initUnmounted()
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div class="timeline-wrapper">
|
|
<div class="timeline-toolbar">
|
|
<div class="timeline-title-container">
|
|
<span class="timeline-title">隐患时间线 (共 {{ Object.keys(hazardRanges).length }} 个隐患)</span>
|
|
<el-button
|
|
type="info" dashed :icon="InfoFilled" size="small"
|
|
@click="openMessageBox(
|
|
'拖动播放头 控制播放进度。滚轮/拖动 时间轴前后移动。点击隐患编号跳转对应隐患。shift键+鼠标滚轮 缩放时间轴。缩放默认以播放头位置为中心,如果播放头不在可视范围内,会以时间轴中心缩放。',
|
|
'操作说明',
|
|
)"
|
|
>
|
|
操作说明
|
|
</el-button>
|
|
</div>
|
|
<div class="timeline-zoom-controls">
|
|
<div class="timeline-zoom-label">
|
|
<el-text type="info" size="small">
|
|
缩放
|
|
</el-text>
|
|
</div>
|
|
<ElSlider
|
|
:model-value="pxPerFrame"
|
|
:min="minPxPerFrame"
|
|
:max="maxPxPerFrame"
|
|
:step="0.1"
|
|
:show-tooltip="true"
|
|
:format-tooltip="(val: number) => `缩放: ${val.toFixed(2)}`"
|
|
class="zoom-slider"
|
|
@update:model-value="(val: Arrayable<number>) => setZoom(Array.isArray(val) ? val[0] : val)"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
ref="timelineContainer"
|
|
class="timeline-container"
|
|
@mousemove="handleMouseMove"
|
|
@mouseup="handleMouseUp"
|
|
@mouseleave="handleMouseUp"
|
|
>
|
|
<div
|
|
id="playhead"
|
|
class="playhead"
|
|
:style="{ left: `${playheadPos}px` }"
|
|
@mousedown="handlePlayheadMouseDown"
|
|
>
|
|
<div class="playhead-indicator" />
|
|
</div>
|
|
|
|
<div class="timeline-content" :style="{ width: trackWidth }">
|
|
<div class="timeline-ruler">
|
|
<div
|
|
v-for="tick in secondTicks"
|
|
:key="tick.second"
|
|
class="time-tick"
|
|
:style="{ left: `${tick.position}px` }"
|
|
>
|
|
<div class="tick-mark minor" />
|
|
</div>
|
|
<div
|
|
v-for="tick in timeTicks"
|
|
:key="tick.second"
|
|
class="time-tick"
|
|
:style="{ left: `${tick.position}px` }"
|
|
>
|
|
<div class="tick-mark major" />
|
|
<span class="tick-label">{{ tick.label }}</span>
|
|
</div>
|
|
<div
|
|
class="timeline-ruler-click-area"
|
|
@click="handleRulerClick"
|
|
/>
|
|
</div>
|
|
|
|
<ElScrollbar :style="{ height: '100%' }">
|
|
<div id="tracks-list" class="tracks-list">
|
|
<div
|
|
v-for="(row, rowIndex) in trackRows"
|
|
:key="rowIndex"
|
|
class="track-row"
|
|
>
|
|
<div
|
|
v-for="hazard in row"
|
|
:key="hazard.id"
|
|
class="hazard-block"
|
|
:class="{ primary: hazard.level === 0, danger: hazard.level === 1 }"
|
|
:style="getHazardStyle(hazard.start, hazard.end)"
|
|
@click="handleHazardClick(hazard.id)"
|
|
>
|
|
<el-tooltip :content="hazard.tip" placement="top">
|
|
<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;">
|
|
{{ parseInt(hazard.id) + 1 }}
|
|
</div>
|
|
</el-tooltip>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</ElScrollbar>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|