优化路由结构,删除多余页面
This commit is contained in:
parent
d8a8b1c94f
commit
659c462d91
|
|
@ -1,17 +1,174 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormInstance, FormRules } from 'element-plus'
|
||||||
|
import { onMounted, reactive, ref } from 'vue'
|
||||||
|
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { runApi } from '~/composables/api'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const runCheckBtnRef = ref()
|
||||||
|
const isRunningCheck = ref(false)
|
||||||
|
const vidPaths = ref<string[]>([])
|
||||||
|
|
||||||
|
interface RuleForm {
|
||||||
|
vidPath: string
|
||||||
|
function: string[]
|
||||||
|
}
|
||||||
|
const ruleFormRef = ref<FormInstance>()
|
||||||
|
const ruleForm = reactive<RuleForm>({
|
||||||
|
vidPath: '',
|
||||||
|
function: [
|
||||||
|
// 'runSam3',
|
||||||
|
// 'runHazardCheck',
|
||||||
|
// 'runGenerateReport',
|
||||||
|
// 'runAudioRecognition',
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const rules = reactive<FormRules<RuleForm>>({
|
||||||
|
vidPath: [
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
message: '请选择视频',
|
||||||
|
trigger: 'change',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
async function runCheck(formEl: FormInstance | undefined) {
|
||||||
|
if (!formEl)
|
||||||
|
return
|
||||||
|
const valid = await formEl.validate((valid, fields) => {
|
||||||
|
if (valid) {
|
||||||
|
// console.log('submit!', ruleForm)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.error('error submit!', fields)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (valid) {
|
||||||
|
isRunningCheck.value = true
|
||||||
|
await runApi('/run', {
|
||||||
|
vid_file: vidPaths.value[vidPaths.value.indexOf(ruleForm.vidPath)],
|
||||||
|
run_sam3: ruleForm.function.includes('runSam3'),
|
||||||
|
run_inspection: ruleForm.function.includes('runHazardCheck'),
|
||||||
|
gen_report: ruleForm.function.includes('runGenerateReport'),
|
||||||
|
}).then((res) => {
|
||||||
|
isRunningCheck.value = false
|
||||||
|
// 判定是否成功运行
|
||||||
|
if (res !== 'error') {
|
||||||
|
router.push({
|
||||||
|
path: '/nav/hazardCheckResult',
|
||||||
|
query: { vid_file: vidPaths.value[vidPaths.value.indexOf(ruleForm.vidPath)] },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function reloadFiles() {
|
||||||
|
await runApi('/reload_files', {}).then((res) => {
|
||||||
|
for (const item of (res as any)[0].choices) {
|
||||||
|
vidPaths.value.push(item[0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetForm(formEl: FormInstance | undefined) {
|
||||||
|
if (!formEl)
|
||||||
|
return
|
||||||
|
formEl.resetFields()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function goToResult(formEl: FormInstance | undefined) {
|
||||||
|
if (!formEl)
|
||||||
|
return
|
||||||
|
await formEl.validate((valid, fields) => {
|
||||||
|
if (valid) {
|
||||||
|
// console.log('跳转结果页')
|
||||||
|
router.push({
|
||||||
|
path: '/nav/hazardCheckResult',
|
||||||
|
query: { vid_file: 'santai5.mp4' },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.error('error submit!', fields)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
reloadFiles()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<!-- <Logos my="4" />
|
<!-- 最外层容器:占满整个视口 -->
|
||||||
<HelloWorld msg="Hello Vue 3 + Element Plus + Vite" /> -->
|
<el-container style="margin: 0; height: 100%; flex-direction: column;">
|
||||||
<div class="container mx-auto">
|
<!-- 内层容器:自动填充剩余高度 -->
|
||||||
<div class="py-8 text-center text-4xl font-bold">
|
<el-main class="main" style="flex: 1; min-height: 0;">
|
||||||
安责险隐患检查
|
<el-form
|
||||||
</div>
|
ref="ruleFormRef"
|
||||||
<div class="grid grid-cols-3 gap-4">
|
style="max-width: 600px"
|
||||||
<el-button class="bg-gray-200 p-4" @click="$router.push('/nav/hazardCheck')">
|
:model="ruleForm"
|
||||||
本地视频隐患检查
|
:rules="rules"
|
||||||
</el-button>
|
label-width="auto"
|
||||||
<el-button class="bg-gray-200 p-4" @click="$router.push('/nav/VideoTrackDemo')">
|
>
|
||||||
物体跟踪 demo
|
<el-form-item label="视频" prop="vidPath">
|
||||||
</el-button>
|
<el-select v-model="ruleForm.vidPath" placeholder="视频">
|
||||||
</div>
|
<el-option v-for="item in vidPaths" :key="item" :label="item" :value="item" />
|
||||||
</div>
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="功能选择" prop="function">
|
||||||
|
<el-checkbox-group v-model="ruleForm.function" class="checkbox-group">
|
||||||
|
<el-checkbox value="runSam3" name="function">
|
||||||
|
物体识别
|
||||||
|
</el-checkbox>
|
||||||
|
<el-checkbox value="runHazardCheck" name="function">
|
||||||
|
隐患检查
|
||||||
|
</el-checkbox>
|
||||||
|
<el-checkbox value="runGenerateReport" name="function">
|
||||||
|
生成报告
|
||||||
|
</el-checkbox>
|
||||||
|
<el-checkbox value="runAudioRecognition" name="function">
|
||||||
|
音频识别
|
||||||
|
</el-checkbox>
|
||||||
|
</el-checkbox-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button @click="resetForm(ruleFormRef)">
|
||||||
|
重置表单
|
||||||
|
</el-button>
|
||||||
|
<el-button @click="goToResult(ruleFormRef)">
|
||||||
|
查看结果
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
ref="runCheckBtnRef"
|
||||||
|
type="primary"
|
||||||
|
:loading="isRunningCheck"
|
||||||
|
@click="runCheck(ruleFormRef)"
|
||||||
|
>
|
||||||
|
运行检查
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-main>
|
||||||
|
</el-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.main {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-group {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 16px;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -312,9 +312,6 @@ onMounted(() => {
|
||||||
<el-breadcrumb-item :to="{ path: '/' }">
|
<el-breadcrumb-item :to="{ path: '/' }">
|
||||||
主页
|
主页
|
||||||
</el-breadcrumb-item>
|
</el-breadcrumb-item>
|
||||||
<el-breadcrumb-item :to="{ path: '/nav/hazardCheck/' }">
|
|
||||||
隐患检查
|
|
||||||
</el-breadcrumb-item>
|
|
||||||
<el-breadcrumb-item>隐患检查结果</el-breadcrumb-item>
|
<el-breadcrumb-item>隐患检查结果</el-breadcrumb-item>
|
||||||
</el-breadcrumb>
|
</el-breadcrumb>
|
||||||
</el-header>
|
</el-header>
|
||||||
|
|
@ -1,156 +0,0 @@
|
||||||
<script setup>
|
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
const videoRef = ref(null)
|
|
||||||
const currentTime = ref(0)
|
|
||||||
const currentBoxes = ref([])
|
|
||||||
const selectedBox = ref(null)
|
|
||||||
|
|
||||||
// 内置跟踪数据
|
|
||||||
const trackData = ref([
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
label: '人物',
|
|
||||||
color: '#FF4D4F',
|
|
||||||
frames: [
|
|
||||||
{ time: 0, x: 60, y: 40, width: 160, height: 280 },
|
|
||||||
{ time: 1, x: 80, y: 40, width: 160, height: 280 },
|
|
||||||
{ time: 2, x: 100, y: 40, width: 160, height: 280 },
|
|
||||||
{ time: 3, x: 120, y: 40, width: 160, height: 280 },
|
|
||||||
{ time: 4, x: 140, y: 40, width: 160, height: 280 },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
label: '车辆',
|
|
||||||
color: '#1890FF',
|
|
||||||
frames: [
|
|
||||||
{ time: 0, x: 300, y: 150, width: 240, height: 140 },
|
|
||||||
{ time: 1, x: 320, y: 150, width: 240, height: 140 },
|
|
||||||
{ time: 2, x: 340, y: 150, width: 240, height: 140 },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
// 视频时间更新
|
|
||||||
function onTimeUpdate() {
|
|
||||||
const t = videoRef.value.currentTime
|
|
||||||
currentTime.value = t
|
|
||||||
|
|
||||||
const boxes = []
|
|
||||||
trackData.value.forEach((item) => {
|
|
||||||
const frame = item.frames.findLast(f => f.time <= t)
|
|
||||||
if (frame) {
|
|
||||||
boxes.push({
|
|
||||||
id: item.id,
|
|
||||||
label: item.label,
|
|
||||||
color: item.color,
|
|
||||||
...frame,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
currentBoxes.value = boxes
|
|
||||||
}
|
|
||||||
|
|
||||||
// 点击标注框
|
|
||||||
function handleBoxClick(box) {
|
|
||||||
selectedBox.value = box
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="demo-container" style="max-width: 1200px; margin: 20px auto">
|
|
||||||
<el-card header="视频实时物体跟踪(支持点击标注框)">
|
|
||||||
<div class="video-wrapper">
|
|
||||||
<video
|
|
||||||
ref="videoRef"
|
|
||||||
src="https://www.w3school.com.cn/i/movie.mp4"
|
|
||||||
controls
|
|
||||||
class="video"
|
|
||||||
@timeupdate="onTimeUpdate"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- 标注层 -->
|
|
||||||
<div class="annotations-layer">
|
|
||||||
<div
|
|
||||||
v-for="box in currentBoxes"
|
|
||||||
:key="box.id"
|
|
||||||
class="box"
|
|
||||||
:class="{ active: selectedBox?.id === box.id }"
|
|
||||||
:style="{
|
|
||||||
left: `${box.x}px`,
|
|
||||||
top: `${box.y}px`,
|
|
||||||
width: `${box.width}px`,
|
|
||||||
height: `${box.height}px`,
|
|
||||||
borderColor: box.color,
|
|
||||||
}"
|
|
||||||
@click.stop="handleBoxClick(box)"
|
|
||||||
>
|
|
||||||
<span class="label">{{ box.label }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="margin-top: 16px; display: flex; gap: 10px">
|
|
||||||
<el-tag type="primary">
|
|
||||||
当前时间:{{ currentTime.toFixed(2) }}s
|
|
||||||
</el-tag>
|
|
||||||
<el-tag v-if="selectedBox" type="warning">
|
|
||||||
已选中 ID:{{ selectedBox.id }} | 标签:{{ selectedBox.label }}
|
|
||||||
</el-tag>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.video-wrapper {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
background: #000;
|
|
||||||
border-radius: 8px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotations-layer {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.box {
|
|
||||||
position: absolute;
|
|
||||||
border: 2px solid;
|
|
||||||
background: rgba(0, 0, 0, 0.1);
|
|
||||||
pointer-events: auto;
|
|
||||||
cursor: pointer;
|
|
||||||
box-sizing: border-box;
|
|
||||||
transform-origin: center center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.box.active {
|
|
||||||
border-width: 4px;
|
|
||||||
background: rgba(255, 255, 0, 0.1);
|
|
||||||
transform: translate(-1px, -1px); /* 完美抵消边框变粗偏移 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
position: absolute;
|
|
||||||
top: -22px;
|
|
||||||
left: 0;
|
|
||||||
background: rgba(0, 0, 0, 0.7);
|
|
||||||
color: #fff;
|
|
||||||
font-size: 13px;
|
|
||||||
padding: 2px 6px;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,241 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
// =============== 1. 定义 TS 类型 ===============
|
|
||||||
/** 单个帧位置信息 */
|
|
||||||
interface TrackFrame {
|
|
||||||
time: number
|
|
||||||
x: number
|
|
||||||
y: number
|
|
||||||
width: number
|
|
||||||
height: number
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 单个跟踪目标 */
|
|
||||||
interface TrackItem {
|
|
||||||
id: number
|
|
||||||
label: string
|
|
||||||
color: string
|
|
||||||
frames: TrackFrame[]
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 渲染到页面的标注框 */
|
|
||||||
interface RenderBox extends TrackFrame {
|
|
||||||
id: number
|
|
||||||
label: string
|
|
||||||
color: string
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============== 2. 响应式数据 ===============
|
|
||||||
const 隐患列表 = ref<string[]>(['隐患1', '隐患2', '隐患3', '隐患4', '隐患5'])
|
|
||||||
const 物体列表 = ref<string[]>(['物体1', '物体2', '物体3', '物体4', '物体5'])
|
|
||||||
|
|
||||||
const videoRef = ref<HTMLVideoElement | null>(null)
|
|
||||||
const currentTime = ref<number>(0)
|
|
||||||
const currentBoxes = ref<RenderBox[]>([])
|
|
||||||
const selectedBox = ref<RenderBox | null>(null)
|
|
||||||
|
|
||||||
// 内置跟踪数据
|
|
||||||
const trackData = ref<TrackItem[]>([
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
label: '人物',
|
|
||||||
color: '#FF4D4F',
|
|
||||||
frames: [
|
|
||||||
{ time: 0, x: 60, y: 40, width: 160, height: 280 },
|
|
||||||
{ time: 1, x: 80, y: 40, width: 160, height: 280 },
|
|
||||||
{ time: 2, x: 100, y: 40, width: 160, height: 280 },
|
|
||||||
{ time: 3, x: 120, y: 40, width: 160, height: 280 },
|
|
||||||
{ time: 4, x: 140, y: 40, width: 160, height: 280 },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
label: '车辆',
|
|
||||||
color: '#1890FF',
|
|
||||||
frames: [
|
|
||||||
{ time: 0, x: 300, y: 150, width: 240, height: 140 },
|
|
||||||
{ time: 1, x: 320, y: 150, width: 240, height: 140 },
|
|
||||||
{ time: 2, x: 340, y: 150, width: 240, height: 140 },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
// =============== 3. 函数 ===============
|
|
||||||
/** 视频时间更新 */
|
|
||||||
function onTimeUpdate(): void {
|
|
||||||
if (!videoRef.value)
|
|
||||||
return
|
|
||||||
|
|
||||||
const t = videoRef.value.currentTime
|
|
||||||
currentTime.value = t
|
|
||||||
|
|
||||||
const boxes: RenderBox[] = []
|
|
||||||
trackData.value.forEach((item) => {
|
|
||||||
const frame = item.frames.findLast(f => f.time <= t)
|
|
||||||
if (frame) {
|
|
||||||
boxes.push({
|
|
||||||
id: item.id,
|
|
||||||
label: item.label,
|
|
||||||
color: item.color,
|
|
||||||
...frame,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
currentBoxes.value = boxes
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 点击标注框 */
|
|
||||||
function handleBoxClick(box: RenderBox): void {
|
|
||||||
selectedBox.value = box
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<!-- 最外层容器:占满整个视口 -->
|
|
||||||
<el-container style="margin: 0; height: 100%; flex-direction: column;">
|
|
||||||
<el-header style="height: 60px; display: flex; align-items: center; border-bottom: 1px solid var(--ep-border-color);">
|
|
||||||
<el-breadcrumb separator="/">
|
|
||||||
<el-breadcrumb-item :to="{ path: '/' }">
|
|
||||||
主页
|
|
||||||
</el-breadcrumb-item>
|
|
||||||
<el-breadcrumb-item>隐患检查</el-breadcrumb-item>
|
|
||||||
</el-breadcrumb>
|
|
||||||
</el-header>
|
|
||||||
|
|
||||||
<!-- 内层容器:自动填充剩余高度 -->
|
|
||||||
<el-container style="flex: 1; min-height: 0;">
|
|
||||||
<el-aside width="200px" style="border-right: 1px solid var(--ep-border-color);">
|
|
||||||
<el-row class="flex flex-col" style="border-bottom: 1px solid var(--ep-border-color);">
|
|
||||||
<el-row style="min-height: 20px;">
|
|
||||||
隐患列表
|
|
||||||
</el-row>
|
|
||||||
<el-row style="flex: 1; overflow-y: auto;">
|
|
||||||
<el-row v-for="item in 隐患列表" :key="item">
|
|
||||||
<el-button>
|
|
||||||
{{ item }}
|
|
||||||
</el-button>
|
|
||||||
</el-row>
|
|
||||||
</el-row>
|
|
||||||
</el-row>
|
|
||||||
<el-row>物体列表</el-row>
|
|
||||||
<el-row style="flex: 1; overflow-y: auto;">
|
|
||||||
<el-row v-for="item in 物体列表" :key="item">
|
|
||||||
<el-button>
|
|
||||||
{{ item }}
|
|
||||||
</el-button>
|
|
||||||
</el-row>
|
|
||||||
</el-row>
|
|
||||||
</el-aside>
|
|
||||||
<el-main style="border-right: 1px solid var(--ep-border-color); padding: 0;">
|
|
||||||
<div class="vid_box" style="border-bottom: 1px solid var(--ep-border-color);">
|
|
||||||
<div class="video-wrapper">
|
|
||||||
<video
|
|
||||||
ref="videoRef"
|
|
||||||
src="https://www.w3school.com.cn/i/movie.mp4"
|
|
||||||
controls
|
|
||||||
class="video"
|
|
||||||
@timeupdate="onTimeUpdate"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- 标注层 -->
|
|
||||||
<div class="annotations-layer">
|
|
||||||
<div
|
|
||||||
v-for="box in currentBoxes"
|
|
||||||
:key="box.id"
|
|
||||||
class="box"
|
|
||||||
:class="{ active: selectedBox?.id === box.id }"
|
|
||||||
:style="{
|
|
||||||
left: `${box.x}px`,
|
|
||||||
top: `${box.y}px`,
|
|
||||||
width: `${box.width}px`,
|
|
||||||
height: `${box.height}px`,
|
|
||||||
borderColor: box.color,
|
|
||||||
}"
|
|
||||||
@click.stop="handleBoxClick(box)"
|
|
||||||
>
|
|
||||||
<span class="label">{{ box.label }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="vid_track">
|
|
||||||
轨道
|
|
||||||
</div>
|
|
||||||
</el-main>
|
|
||||||
<el-aside width="300px">
|
|
||||||
<el-row style="border-bottom: 1px solid var(--ep-border-color);">
|
|
||||||
<el-col>
|
|
||||||
<el-row class="text-left">
|
|
||||||
隐患描述
|
|
||||||
</el-row>
|
|
||||||
<el-row class="text-left">
|
|
||||||
依据
|
|
||||||
</el-row>
|
|
||||||
<el-row class="text-left">
|
|
||||||
整改建议
|
|
||||||
</el-row>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-button>
|
|
||||||
查看报告
|
|
||||||
</el-button>
|
|
||||||
</el-row>
|
|
||||||
</el-aside>
|
|
||||||
</el-container>
|
|
||||||
</el-container>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.video-wrapper {
|
|
||||||
position: relative;
|
|
||||||
width: calc(100% - 8px);
|
|
||||||
background: #000;
|
|
||||||
border-radius: 8px;
|
|
||||||
overflow: hidden;
|
|
||||||
margin: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.annotations-layer {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.box {
|
|
||||||
position: absolute;
|
|
||||||
border: 2px solid;
|
|
||||||
background: rgba(0, 0, 0, 0.1);
|
|
||||||
pointer-events: auto;
|
|
||||||
cursor: pointer;
|
|
||||||
box-sizing: border-box;
|
|
||||||
transform-origin: center center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.box.active {
|
|
||||||
border-width: 4px;
|
|
||||||
background: rgba(255, 255, 0, 0.1);
|
|
||||||
transform: translate(-1px, -1px); /* 完美抵消边框变粗偏移 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
position: absolute;
|
|
||||||
top: -22px;
|
|
||||||
left: 0;
|
|
||||||
background: rgba(0, 0, 0, 0.7);
|
|
||||||
color: #fff;
|
|
||||||
font-size: 13px;
|
|
||||||
padding: 2px 6px;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,188 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import type { FormInstance, FormRules } from 'element-plus'
|
|
||||||
import { onMounted, reactive, ref } from 'vue'
|
|
||||||
|
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
import { runApi } from '~/composables/api'
|
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
const runCheckBtnRef = ref()
|
|
||||||
const isRunningCheck = ref(false)
|
|
||||||
const vidPaths = ref<string[]>([])
|
|
||||||
|
|
||||||
interface RuleForm {
|
|
||||||
vidPath: string
|
|
||||||
function: string[]
|
|
||||||
}
|
|
||||||
const ruleFormRef = ref<FormInstance>()
|
|
||||||
const ruleForm = reactive<RuleForm>({
|
|
||||||
vidPath: '',
|
|
||||||
function: [
|
|
||||||
// 'runSam3',
|
|
||||||
// 'runHazardCheck',
|
|
||||||
// 'runGenerateReport',
|
|
||||||
// 'runAudioRecognition',
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
const rules = reactive<FormRules<RuleForm>>({
|
|
||||||
vidPath: [
|
|
||||||
{
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
message: '请选择视频',
|
|
||||||
trigger: 'change',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
async function runCheck(formEl: FormInstance | undefined) {
|
|
||||||
if (!formEl)
|
|
||||||
return
|
|
||||||
const valid = await formEl.validate((valid, fields) => {
|
|
||||||
if (valid) {
|
|
||||||
// console.log('submit!', ruleForm)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.error('error submit!', fields)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (valid) {
|
|
||||||
isRunningCheck.value = true
|
|
||||||
await runApi('/run', {
|
|
||||||
vid_file: vidPaths.value[vidPaths.value.indexOf(ruleForm.vidPath)],
|
|
||||||
run_sam3: ruleForm.function.includes('runSam3'),
|
|
||||||
run_inspection: ruleForm.function.includes('runHazardCheck'),
|
|
||||||
gen_report: ruleForm.function.includes('runGenerateReport'),
|
|
||||||
}).then((res) => {
|
|
||||||
isRunningCheck.value = false
|
|
||||||
// 判定是否成功运行
|
|
||||||
if (res !== 'error') {
|
|
||||||
router.push({
|
|
||||||
path: '/nav/hazardCheck/hazardCheckResult',
|
|
||||||
query: { vid_file: vidPaths.value[vidPaths.value.indexOf(ruleForm.vidPath)] },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function reloadFiles() {
|
|
||||||
await runApi('/reload_files', {}).then((res) => {
|
|
||||||
for (const item of (res as any)[0].choices) {
|
|
||||||
vidPaths.value.push(item[0])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetForm(formEl: FormInstance | undefined) {
|
|
||||||
if (!formEl)
|
|
||||||
return
|
|
||||||
formEl.resetFields()
|
|
||||||
}
|
|
||||||
|
|
||||||
async function goToResult(formEl: FormInstance | undefined) {
|
|
||||||
if (!formEl)
|
|
||||||
return
|
|
||||||
const valid = await formEl.validate((valid, fields) => {
|
|
||||||
if (valid) {
|
|
||||||
// console.log('跳转结果页')
|
|
||||||
router.push('/nav/hazardCheck/hazardCheckResult')
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.error('error submit!', fields)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (valid) {
|
|
||||||
isRunningCheck.value = true
|
|
||||||
router.push({
|
|
||||||
path: '/nav/hazardCheck/hazardCheckResult',
|
|
||||||
query: { vid_file: 'santai5.mp4' },
|
|
||||||
})
|
|
||||||
// isRunningCheck.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
reloadFiles()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<!-- 最外层容器:占满整个视口 -->
|
|
||||||
<el-container style="margin: 0; height: 100%; flex-direction: column;">
|
|
||||||
<el-header style="height: 30px; display: flex; align-items: center; border-bottom: 1px solid var(--ep-border-color);">
|
|
||||||
<el-breadcrumb separator="/">
|
|
||||||
<el-breadcrumb-item :to="{ path: '/' }">
|
|
||||||
主页
|
|
||||||
</el-breadcrumb-item>
|
|
||||||
<el-breadcrumb-item>隐患检查</el-breadcrumb-item>
|
|
||||||
</el-breadcrumb>
|
|
||||||
</el-header>
|
|
||||||
|
|
||||||
<!-- 内层容器:自动填充剩余高度 -->
|
|
||||||
<el-main class="main" style="flex: 1; min-height: 0;">
|
|
||||||
<el-form
|
|
||||||
ref="ruleFormRef"
|
|
||||||
style="max-width: 600px"
|
|
||||||
:model="ruleForm"
|
|
||||||
:rules="rules"
|
|
||||||
label-width="auto"
|
|
||||||
>
|
|
||||||
<el-form-item label="视频" prop="vidPath">
|
|
||||||
<el-select v-model="ruleForm.vidPath" placeholder="视频">
|
|
||||||
<el-option v-for="item in vidPaths" :key="item" :label="item" :value="item" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="功能选择" prop="function">
|
|
||||||
<el-checkbox-group v-model="ruleForm.function" class="checkbox-group">
|
|
||||||
<el-checkbox value="runSam3" name="function">
|
|
||||||
物体识别
|
|
||||||
</el-checkbox>
|
|
||||||
<el-checkbox value="runHazardCheck" name="function">
|
|
||||||
隐患检查
|
|
||||||
</el-checkbox>
|
|
||||||
<el-checkbox value="runGenerateReport" name="function">
|
|
||||||
生成报告
|
|
||||||
</el-checkbox>
|
|
||||||
<el-checkbox value="runAudioRecognition" name="function">
|
|
||||||
音频识别
|
|
||||||
</el-checkbox>
|
|
||||||
</el-checkbox-group>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item>
|
|
||||||
<el-button @click="resetForm(ruleFormRef)">
|
|
||||||
重置表单
|
|
||||||
</el-button>
|
|
||||||
<el-button @click="goToResult(ruleFormRef)">
|
|
||||||
查看结果
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
ref="runCheckBtnRef"
|
|
||||||
type="primary"
|
|
||||||
:loading="isRunningCheck"
|
|
||||||
@click="runCheck(ruleFormRef)"
|
|
||||||
>
|
|
||||||
运行检查
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</el-main>
|
|
||||||
</el-container>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.main {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-group {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 16px;
|
|
||||||
justify-content: flex-start;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -19,9 +19,6 @@ declare module 'vue-router/auto-routes' {
|
||||||
*/
|
*/
|
||||||
export interface RouteNamedMap {
|
export interface RouteNamedMap {
|
||||||
'/': RouteRecordInfo<'/', '/', Record<never, never>, Record<never, never>>,
|
'/': RouteRecordInfo<'/', '/', Record<never, never>, Record<never, never>>,
|
||||||
'/nav/hazardCheck/': RouteRecordInfo<'/nav/hazardCheck/', '/nav/hazardCheck', Record<never, never>, Record<never, never>>,
|
'/nav/HazardCheckResult/': RouteRecordInfo<'/nav/HazardCheckResult/', '/nav/HazardCheckResult', Record<never, never>, Record<never, never>>,
|
||||||
'/nav/hazardCheck/1_隐患检查 copy': RouteRecordInfo<'/nav/hazardCheck/1_隐患检查 copy', '/nav/hazardCheck/1_隐患检查 copy', Record<never, never>, Record<never, never>>,
|
|
||||||
'/nav/hazardCheck/HazardCheckResult': RouteRecordInfo<'/nav/hazardCheck/HazardCheckResult', '/nav/hazardCheck/HazardCheckResult', Record<never, never>, Record<never, never>>,
|
|
||||||
'/nav/VideoTrackDemo': RouteRecordInfo<'/nav/VideoTrackDemo', '/nav/VideoTrackDemo', Record<never, never>, Record<never, never>>,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue