25
0
Fork 0

feat: 视频分析页开发

develop
JINGYJ 2 years ago
parent 402ca4719e
commit fff9fc175c

@ -57,6 +57,7 @@
"@commitlint/cli": "^17.6.5", "@commitlint/cli": "^17.6.5",
"@commitlint/config-conventional": "^17.6.5", "@commitlint/config-conventional": "^17.6.5",
"@iconify-icons/ep": "^1.2.11", "@iconify-icons/ep": "^1.2.11",
"@iconify-icons/icon-park-outline": "^1.2.11",
"@iconify-icons/ri": "^1.2.8", "@iconify-icons/ri": "^1.2.8",
"@iconify/vue": "^4.1.1", "@iconify/vue": "^4.1.1",
"@pureadmin/theme": "^3.1.0", "@pureadmin/theme": "^3.1.0",

File diff suppressed because it is too large Load Diff

@ -19,5 +19,5 @@
"CachingAsyncRoutes": false, "CachingAsyncRoutes": false,
"TooltipEffect": "light", "TooltipEffect": "light",
"ResponsiveStorageNameSpace": "responsive-", "ResponsiveStorageNameSpace": "responsive-",
"AdminHostUrl": "http://192.168.10.13:8000" "AdminHostUrl": "http://192.168.10.59:8000"
} }

@ -30,6 +30,12 @@ export const submitAnalyse = (data?: object) => {
}); });
}; };
export const getVideoList = (data?: object) => {
return http.request<Data>("post", baseUrlApi("tps/get_analyzed_videos/"), {
data
});
};
export const beforeUploadVideo = (params?: object) => { export const beforeUploadVideo = (params?: object) => {
return http.request<Data>("get", baseUrlApi("tps/upload_video/"), { return http.request<Data>("get", baseUrlApi("tps/upload_video/"), {
params params

@ -8,7 +8,9 @@ import { addIcon } from "@iconify/vue/dist/offline";
import HomeFilled from "@iconify-icons/ep/home-filled"; import HomeFilled from "@iconify-icons/ep/home-filled";
import InformationLine from "@iconify-icons/ri/information-line"; import InformationLine from "@iconify-icons/ri/information-line";
import Lollipop from "@iconify-icons/ep/lollipop"; import Lollipop from "@iconify-icons/ep/lollipop";
import VideoT from "@iconify-icons/icon-park-outline/video-two";
addIcon("homeFilled", HomeFilled); addIcon("homeFilled", HomeFilled);
addIcon("informationLine", InformationLine); addIcon("informationLine", InformationLine);
addIcon("lollipop", Lollipop); addIcon("lollipop", Lollipop);
addIcon("videoT", VideoT);

@ -2,17 +2,27 @@ export default {
path: "/videoParse", path: "/videoParse",
// redirect: "/error/403", // redirect: "/error/403",
meta: { meta: {
icon: "lollipop", icon: "videoT",
title: "视频分析", title: "视频分析",
showLink: true, showLink: true,
rank: 10 rank: 10
}, },
children: [ children: [
{
path: "/videoList/index",
name: "VideoListPage",
component: () => import("@/views/videoList/index.vue"),
meta: {
title: "视频分析"
}
},
{ {
path: "/videoParse/index", path: "/videoParse/index",
name: "VideoParsePage", name: "VideoParsePage",
component: () => import("@/views/videoParse/index.vue"), component: () => import("@/views/videoParse/index.vue"),
meta: { meta: {
showLink: false,
activePath: "/videoList/index",
title: "视频分析" title: "视频分析"
} }
} }

@ -0,0 +1,318 @@
<script setup lang="ts">
import { PureTable } from "@pureadmin/table";
import { type PaginationProps } from "@pureadmin/table";
import { onMounted, ref, reactive } from "vue";
import { useWindowSize } from "@vueuse/core";
import { getVideoList } from "@/api/videoParse";
import { useRouter } from "vue-router";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
defineOptions({
name: "VideoList"
});
const formInline = reactive({
date: "",
violation: ""
});
function pickerOptions(time) {
return time.getTime() > Date.now();
}
const now_date = new Date();
now_date.setMonth(now_date.getMonth() - 1);
const lastMonth = now_date;
const tableData = ref([]);
// const violationMap = ref({
// : "0",
// : "1",
// : "2",
// : "3",
// : "4"
// });
const options = [
{
value: "疑似静止",
label: "疑似静止"
},
{
value: "疑似遮挡",
label: "疑似遮挡"
},
{
value: "疑似抖动",
label: "疑似抖动"
},
{
value: "疑似递烟",
label: "疑似递烟"
},
{
value: "疑似递钱",
label: "疑似递钱"
}
];
const { height } = useWindowSize();
const tableSize = ref("default");
const columns: TableColumnList = [
{
label: "序号",
type: "index",
width: 70,
fixed: "left"
},
{
label: "视频名称",
// width: 200,
prop: "video_name"
},
{
label: "日期",
prop: "video_date",
// width: 140,
sortable: true
},
{
label: "异常数量",
// width: 100,
prop: "abnormal_count"
},
{
label: "异常表现",
prop: "abnormal_performance",
width: 370,
slot: "violation"
},
{
label: "文件地址",
prop: "video_path"
},
{
label: "操作",
fixed: "right",
// width: 160,
slot: "operation"
}
];
function tagStyle(type) {
switch (type) {
case "疑似遮挡":
return "span-occlusion";
case "疑似递烟":
return "span-smoke";
case "疑似递钱":
return "span-money";
case "疑似静止":
return "span-stillness";
default:
return "span-shake";
}
}
async function onSearch() {
const params = {
start_time: formInline.date === null ? undefined : formInline.date[0],
end_time: formInline.date === null ? undefined : formInline.date[1],
abnormal_behavior: formInline.violation || undefined,
page: pagination.currentPage || undefined,
page_size: pagination.pageSize || undefined
};
const { results, count } = await getVideoList(params);
pagination.total = count;
tableData.value = results;
setTimeout(() => {
loading.value = false;
}, 500);
}
function handleSearch() {
pagination.currentPage = 1;
onSearch();
}
function handleRefresh() {
Object.assign(formInline, { date: "", violation: "" });
onSearch();
}
function handleSizeChange(val) {
pagination.pageSize = val;
onSearch();
}
function handleCurrentChange(val) {
pagination.currentPage = val;
onSearch();
}
const router = useRouter();
function handleProductDetail(product) {
//
useMultiTagsStoreHook().handleTags("push", {
path: `/videoParse/index`,
name: "VideoParsePage",
query: product,
meta: {
title: `视频分析`,
showLink: false
}
});
router.push({ name: "VideoParsePage", query: product });
}
const loading = ref(true);
const pagination = reactive<PaginationProps>({
total: 0,
pageSize: 15,
currentPage: 1,
background: true
});
onMounted(() => {
onSearch();
});
</script>
<template>
<div class="main">
<h2 class="mb-4 text-xl">视频分析</h2>
<el-card shadow="never" :style="{ height: `calc(${height}px - 140px)` }">
<template #header>
<div class="card-header">
<span class="font-semibold">异常分析</span>
</div>
</template>
<el-form
:inline="true"
:model="formInline"
class="search-form bg-bg_color w-[99/100]"
>
<el-form-item style="margin-right: 10px">
<el-date-picker
v-model="formInline.date"
type="daterange"
unlink-panels
range-separator="~"
start-placeholder="起始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
:disabled-date="pickerOptions"
:default-value="lastMonth"
/>
</el-form-item>
<el-form-item style="margin-right: 12px">
<el-select
v-model="formInline.violation"
placeholder="异常表现"
multiple
collapse-tags
collapse-tags-tooltip
clearable
>
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch"></el-button>
<el-button @click="handleRefresh"> </el-button>
</el-form-item>
</el-form>
<!-- <PureTableBar title="" :columns="columns" @refresh="onSearch"> -->
<!-- <template v-slot="{ size, columns }"> -->
<pure-table
adaptive
align-whole="left"
table-layout="auto"
:loading="loading"
:data="tableData"
showOverflowTooltip
:columns="columns"
:pagination="pagination"
:paginationSmall="tableSize === 'small' ? true : false"
:header-cell-style="{
background: 'var(--el-table-row-hover-bg-color)',
color: 'var(--el-text-color-primary)'
}"
@page-size-change="handleSizeChange"
@page-current-change="handleCurrentChange"
>
<template #violation="{ row }">
<!-- {{ row.is_violation ? "是" : "否" }} -->
<span
:class="tagStyle(item)"
v-for="(item, index) in row.abnormal_performance"
:key="index"
>{{ item }}</span
>
</template>
<template #operation="{ row }">
<el-button
class="reset-margin"
link
type="primary"
@click="handleProductDetail(row)"
>
详情
</el-button>
</template>
</pure-table>
<!-- </template> -->
<!-- </PureTableBar> -->
</el-card>
</div>
</template>
<style lang="scss" scoped>
.span-occlusion,
.span-smoke,
.span-money,
.span-stillness,
.span-shake {
box-sizing: border-box;
display: inline-block;
width: 64px;
height: 24px;
margin-right: 10px;
font-size: 12px;
color: rgb(247 95 25 / 100%);
text-align: center;
background: rgb(247 112 49 / 10%);
border: 1px solid #f75f19;
border-radius: 2px;
}
.span-smoke {
color: rgb(2 36 215 / 100%);
background: rgb(2 36 215 / 10%);
border: 1px solid rgb(2 36 215 / 100%);
}
.span-money {
color: rgb(232 13 13 / 100%);
background: rgb(232 13 13 / 10%);
border: 1px solid rgb(232 13 13 / 100%);
}
.span-stillness {
color: rgb(68 174 245 / 100%);
background: rgb(68 174 245 / 10%);
border: 1px solid rgb(68 174 245 / 100%);
}
.span-shake {
color: rgb(246 148 0 / 100%);
background: rgb(246 148 0 / 10%);
border: 1px solid rgb(246 148 0 / 100%);
}
.reset-margin {
color: #e80d0d;
&:hover {
color: #e80d0d;
opacity: 0.5;
}
}
</style>

@ -1,17 +1,31 @@
import { addDialog } from "@/components/ReDialog"; import { addDialog, closeDialog } from "@/components/ReDialog";
// import { ref, h } from "vue"; // import { ref, h } from "vue";
import OurPlayer from "@/components/VideoPlayer/OurPlayer.vue"; import OurPlayer from "@/components/VideoPlayer/OurPlayer.vue";
// import dayjs from "dayjs";
// import { http } from "@/utils/http"; // import { http } from "@/utils/http";
export function videoUtil() { export function videoUtil() {
// const currentPlayingIndex = ref(-1); // const currentPlayingIndex = ref(-1);
function dataFormate(time) {
const milliseconds = time * 1000;
let sec = Math.floor(milliseconds / 1000);
let min = Math.floor(sec / 60);
const hour = Math.floor(min / 60);
sec %= 60;
min %= 60;
return `${hour.toString().padStart(2, "0")}:${min
.toString()
.padStart(2, "0")}:${sec.toString().padStart(2, "0")}`;
}
const columns: TableColumnList = [ const columns: TableColumnList = [
{ {
label: "视频内时间", label: "视频内时间",
prop: "relative_time", prop: "relative_time",
minWidth: 100, minWidth: 100,
sortable: true, sortable: true,
slot: "relative_time" formatter: ({ relative_time }) => dataFormate(relative_time)
}, },
{ {
label: "异常类型", label: "异常类型",
@ -60,6 +74,25 @@ export function videoUtil() {
draggable: true, draggable: true,
fullscreenIcon: true, fullscreenIcon: true,
closeOnClickModal: false, closeOnClickModal: false,
footerButtons: [
{
label: "取消",
bg: true,
btnClick: ({ dialog: { options, index }, button }) => {
console.log(options, index, button);
closeDialog(options, index);
}
},
{
label: "确认",
bg: true,
type: "primary",
btnClick: ({ dialog: { options, index }, button }) => {
console.log(options, index, button);
closeDialog(options, index);
}
}
],
contentRenderer: () => <OurPlayer></OurPlayer> contentRenderer: () => <OurPlayer></OurPlayer>
// return h( // return h(
// "video", // "video",

@ -1,121 +1,129 @@
<script setup lang="ts"> <script setup lang="ts">
import { useWindowSize } from "@vueuse/core"; import { useWindowSize } from "@vueuse/core";
import { Help } from "@element-plus/icons-vue"; // import { Help } from "@element-plus/icons-vue";
import { ref, reactive, onMounted } from "vue"; import { ref, reactive, onMounted } from "vue";
import { PureTableBar } from "@/components/RePureTableBar"; // import { PureTableBar } from "@/components/RePureTableBar";
import { PureTable } from "@pureadmin/table"; import { PureTable } from "@pureadmin/table";
import { type PaginationProps } from "@pureadmin/table"; import { type PaginationProps } from "@pureadmin/table";
import { getToken } from "@/utils/auth"; // import { getToken } from "@/utils/auth";
import { ElMessage, ElMessageBox, genFileId } from "element-plus"; import { ElMessage } from "element-plus";
import { videoUtil } from "./hook"; import { videoUtil } from "./hook";
import { baseUrlApi } from "@/api/utils"; // import { baseUrlApi } from "@/api/utils";
import { useRouter, useRoute } from "vue-router";
import { isEmpty } from "@pureadmin/utils";
import { Back } from "@element-plus/icons-vue";
import { import {
getPicList, getPicList,
getTPList, getTPList
submitAnalyse, // submitAnalyse,
beforeUploadVideo // beforeUploadVideo
} from "@/api/videoParse"; } from "@/api/videoParse";
// import { fa } from "element-plus/es/locale";
import type { // import type {
UploadProps, // UploadProps,
UploadUserFile, // UploadUserFile,
UploadInstance, // UploadInstance,
UploadRawFile // UploadRawFile
} from "element-plus"; // } from "element-plus";
defineOptions({ defineOptions({
name: "videoParse" name: "videoParse"
}); });
const { columns, openDialog } = videoUtil(); const { columns, openDialog } = videoUtil();
const fileList = ref<UploadUserFile[]>([]); // const fileList = ref<UploadUserFile[]>([]);
const uploadFile = ref<UploadInstance>(); // const uploadFile = ref<UploadInstance>();
const router = useRouter();
const submitUpload = () => { const route = useRoute();
uploadFile.value!.submit(); const getParameter = isEmpty(route.params) ? route.query : route.params;
}; // console.log(getParameter);
const handleRemove: UploadProps["onRemove"] = (file, uploadFiles) => { // const submitUpload = () => {
console.log("移除文件:", file, uploadFiles); // uploadFile.value!.submit();
}; // };
const handlePreview: UploadProps["onPreview"] = uploadFile => { // const handleRemove: UploadProps["onRemove"] = (file, uploadFiles) => {
console.log("预览文件:", uploadFile); // console.log("", file, uploadFiles);
}; // };
const handleExceed: UploadProps["onExceed"] = files => { // const handlePreview: UploadProps["onPreview"] = uploadFile => {
// ElMessage.warning( // console.log("", uploadFile);
// `${fileList.value[0].name}` // };
// );
ElMessageBox.confirm(`是否覆盖上传文件: ${fileList.value[0].name} ?`) // const handleExceed: UploadProps["onExceed"] = files => {
.then(() => { // // ElMessage.warning(
console.log("开始覆盖"); // // `${fileList.value[0].name}`
uploadFile.value!.clearFiles(); // // );
const file = files[0] as UploadRawFile; // ElMessageBox.confirm(` ${fileList.value[0].name} ?`)
file.uid = genFileId(); // .then(() => {
uploadFile.value!.handleStart(file); // console.log("");
}) // uploadFile.value!.clearFiles();
.catch(() => { // const file = files[0] as UploadRawFile;
console.log("取消覆盖"); // file.uid = genFileId();
submitUpload(); // uploadFile.value!.handleStart(file);
}); // })
}; // .catch(() => {
// console.log("");
const handleChange: UploadProps["onChange"] = files => { // submitUpload();
if (files.status === "ready") { // });
const fileName = files.name.substring(files.name.lastIndexOf(".") + 1); // };
if (
fileName === "mp4" || // const handleChange: UploadProps["onChange"] = files => {
fileName === "MP4" || // if (files.status === "ready") {
fileName === "avi" || // const fileName = files.name.substring(files.name.lastIndexOf(".") + 1);
fileName === "AVI" || // if (
fileName === "mkv" || // fileName === "mp4" ||
fileName === "MKV" || // fileName === "MP4" ||
fileName === "mov" || // fileName === "avi" ||
fileName === "MOV" // fileName === "AVI" ||
) { // fileName === "mkv" ||
const params = { video_name: files.name }; // fileName === "MKV" ||
beforeUploadVideo(params).then(response => { // fileName === "mov" ||
if (!response.success) { // fileName === "MOV"
if (!(response.code === 201)) { // ) {
fileList.value = []; // const params = { video_name: files.name };
ElMessage.warning(`上传文件失败,原因为:${response.msg}`); // beforeUploadVideo(params).then(response => {
return false; // if (!response.success) {
} else { // if (!(response.code === 201)) {
ElMessage.warning(`上传文件失败,原因为:${response.msg}`); // fileList.value = [];
} // ElMessage.warning(`${response.msg}`);
} else { // return false;
console.log("开始上传"); // } else {
submitUpload(); // ElMessage.warning(`${response.msg}`);
} // }
}); // } else {
} else { // console.log("");
ElMessage.warning(`上传文件格式不正确,当前文件格式为:${fileName}`); // submitUpload();
fileList.value = []; // }
return false; // });
} // } else {
} // ElMessage.warning(`${fileName}`);
}; // fileList.value = [];
// return false;
const successCallback: UploadProps["onSuccess"] = ( // }
response, // }
uploadFile, // };
uploadFiles
) => { // const successCallback: UploadProps["onSuccess"] = (
if (!response.success) { // response,
fileList.value = []; // uploadFile,
ElMessage.warning(`文件上传失败原因为:${response.msg},请稍后再试!`); // uploadFiles
handleRemove(uploadFile, uploadFiles); // ) => {
} // if (!response.success) {
}; // fileList.value = [];
// ElMessage.warning(`${response.msg}`);
const beforeRemove: UploadProps["beforeRemove"] = uploadFile => { // handleRemove(uploadFile, uploadFiles);
console.log("删除文件前:", uploadFile); // }
return ElMessageBox.confirm(`是否取消上传文件: ${uploadFile.name} ?`).then( // };
() => true,
() => false // const beforeRemove: UploadProps["beforeRemove"] = uploadFile => {
); // console.log("", uploadFile);
}; // return ElMessageBox.confirm(` ${uploadFile.name} ?`).then(
// () => true,
// () => false
// );
// };
const { height } = useWindowSize(); const { height } = useWindowSize();
const videoData = ref([]); const videoData = ref([]);
@ -148,11 +156,12 @@ function handleCurrentChange(val) {
currentPage.value = val; currentPage.value = val;
onSearch(); onSearch();
} }
const hightFlag = ref(false);
function onSearch() { function onSearch() {
let video_name = undefined; const video_name = getParameter.video_name || undefined;
if (fileList.value.length > 0) { // if (fileList.value.length > 0) {
video_name = fileList.value[0].name; // video_name = fileList.value[0].name;
} // }
const params = { const params = {
video_name: video_name, video_name: video_name,
page: currentPage.value || undefined, page: currentPage.value || undefined,
@ -186,6 +195,19 @@ function onSearch() {
carEvenList.value = dataList.data.car.filter( carEvenList.value = dataList.data.car.filter(
(item, index) => (index + 1) % 2 == 0 (item, index) => (index + 1) % 2 == 0
); );
if (dataList.data.person.length > dataList.data.car.length) {
if (dataList.data.person.length > 4) {
hightFlag.value = true;
} else {
hightFlag.value = false;
}
} else {
if (dataList.data.person.length > 4) {
hightFlag.value = true;
} else {
hightFlag.value = false;
}
}
setTimeout(() => { setTimeout(() => {
loading.value = false; loading.value = false;
}, 500); }, 500);
@ -206,6 +228,21 @@ function spanStyle(type) {
} }
} }
function tagStyle(type) {
switch (type) {
case "疑似遮挡":
return "span-occlusion";
case "疑似递烟":
return "span-smoke";
case "疑似递钱":
return "span-money";
case "疑似静止":
return "span-stillness";
default:
return "span-shake";
}
}
function getRandType(type) { function getRandType(type) {
// const type = Math.floor(Math.random() * 5) + 1; // const type = Math.floor(Math.random() * 5) + 1;
switch (type) { switch (type) {
@ -216,42 +253,28 @@ function getRandType(type) {
} }
} }
function startAnalyse() { // function startAnalyse() {
let video_name = undefined; // let video_name = undefined;
if (fileList.value.length > 0) { // if (fileList.value.length > 0) {
video_name = fileList.value[0].name; // video_name = fileList.value[0].name;
} // }
const params = { // const params = {
video_name: video_name // video_name: video_name
}; // };
submitAnalyse(params).then(response => { // submitAnalyse(params).then(response => {
if (!response.success) { // if (!response.success) {
ElMessage.warning(`提示:${response.msg},请稍后重试`); // ElMessage.warning(`${response.msg}`);
} else { // } else {
onSearch(); // onSearch();
} // }
}); // });
} // }
function dataFormate(time) {
const milliseconds = time * 1000;
let sec = Math.floor(milliseconds / 1000);
let min = Math.floor(sec / 60);
const hour = Math.floor(min / 60);
sec %= 60;
min %= 60;
return `${hour.toString().padStart(2, "0")}:${min
.toString()
.padStart(2, "0")}:${sec.toString().padStart(2, "0")}`;
}
onMounted(() => { onMounted(() => {
onSearch(); onSearch();
setInterval(() => { // setInterval(() => {
onSearch(); // onSearch();
}, 1000 * 60 * 5); // }, 1000 * 60 * 5);
}); });
</script> </script>
@ -259,11 +282,23 @@ onMounted(() => {
<div class="main"> <div class="main">
<el-row :gutter="24"> <el-row :gutter="24">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24"> <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<div class="w-full flex justify-between mb-4">
<el-button
:icon="Back"
key="视频详情"
link
text
class="btn-style"
@click="router.push('/videoList/index')"
>视频详情</el-button
>
</div>
<el-card <el-card
class="box-card" class="box-card"
:style="{ height: `calc(${height}px - 150px)` }" shadow="never"
:style="{ height: `calc(${height}px - 140px)` }"
> >
<template #header> <!-- <template #header>
<div class="card-header"> <div class="card-header">
<span>视频分析</span> <span>视频分析</span>
</div> </div>
@ -287,13 +322,13 @@ onMounted(() => {
accept=".mp4, .MP4, .avi, .AVI, .mkv, .MKV, .mov, .MOV" accept=".mp4, .MP4, .avi, .AVI, .mkv, .MKV, .mov, .MOV"
> >
<span>{{ "选择分析文件" }}</span <span>{{ "选择分析文件" }}</span
><el-button type="primary">选择文件</el-button> ><el-button type="primary">选择文件</el-button> -->
<!-- <template #tip> <!-- <template #tip>
<div class="el-upload__tip"> <div class="el-upload__tip">
jpg/png files with a size less than 500KB. jpg/png files with a size less than 500KB.
</div> </div>
</template> --> </template> -->
</el-upload> <!-- </el-upload>
</div> </div>
<el-button type="success" size="large" @click="startAnalyse"> <el-button type="success" size="large" @click="startAnalyse">
分析 分析
@ -302,23 +337,22 @@ onMounted(() => {
</el-icon> </el-icon>
</el-button> </el-button>
</div> </div>
<el-divider border-style="dashed" /> <el-divider border-style="dashed" /> -->
<!-- <el-empty description="暂无数据" v-show="videoData.length == 0" /> --> <!-- <el-empty description="暂无数据" v-show="videoData.length == 0" /> -->
<div class="analysis-content flex justify-between"> <div class="analysis-content flex justify-between">
<div <div
class="analysis-box" class="analysis-box"
:style="{ height: `calc(${height}px - 60vh - 150px)` }" :style="{ height: hightFlag ? `360px` : `270px` }"
> >
<span class="analysis-box-label">被执法人员推定</span> <span class="analysis-box-label">被执法人员推定</span>
<div <div class="analysis-table">
class="analysis-table"
:style="{ height: `calc(${height}px - 60vh - 240px)` }"
>
<div class="analysis-table-left"> <div class="analysis-table-left">
<div class="analysis-box-lable w-full flex justify-end"> <div class="analysis-box-lable w-full">
<span>目标清晰度</span> <span class="w-[40px]" />
<span class="w-[120px]" />
<span class="ml-6">目标清晰度</span>
</div> </div>
<el-scrollbar :height="`calc(${height}px - 60vh - 220px)`"> <el-scrollbar :height="hightFlag ? `270px` : `180px`">
<div <div
v-for="(item, index) in personOddList" v-for="(item, index) in personOddList"
:key="index" :key="index"
@ -327,27 +361,30 @@ onMounted(() => {
<span :class="spanStyle(item.id)">{{ <span :class="spanStyle(item.id)">{{
2 * index + 1 2 * index + 1
}}</span> }}</span>
<el-image <div class="defect-image">
style="height: 100px" <el-image
:src="item.pic_path" :src="item.pic_path"
:zoom-rate="1.2" :zoom-rate="1.2"
:preview-src-list="[item.pic_path]" :preview-src-list="[item.pic_path]"
fit="cover" fit="cover"
class="w-[100px]" class="w-[100%] h-[100%] rounded"
/> />
</div>
<span>{{ item.pic_quality }}</span> <span>{{ item.pic_quality }}</span>
</div> </div>
</el-scrollbar> </el-scrollbar>
</div> </div>
<el-divider <!-- <el-divider
direction="vertical" direction="vertical"
:style="{ height: `calc(${height}px - 60vh - 220px)` }" :style="{ height: `calc(${height}px - 60vh - 220px)` }"
/> /> -->
<div class="analysis-table-left"> <div class="analysis-table-left">
<div class="analysis-box-lable w-full flex justify-end"> <div class="analysis-box-lable w-full flex justify-end">
<span>目标清晰度</span> <span class="w-[40px]" />
<span class="w-[120px]" />
<span class="ml-1">目标清晰度</span>
</div> </div>
<el-scrollbar :height="`calc(${height}px - 60vh - 220px)`"> <el-scrollbar :height="hightFlag ? `270px` : `180px`">
<div <div
v-for="(item, index) in personEvenList" v-for="(item, index) in personEvenList"
:key="index" :key="index"
@ -356,14 +393,15 @@ onMounted(() => {
<span :class="spanStyle(item.id)">{{ <span :class="spanStyle(item.id)">{{
2 * index + 2 2 * index + 2
}}</span> }}</span>
<el-image <div class="defect-image">
style="height: 100px" <el-image
:src="item.pic_path" :src="item.pic_path"
:zoom-rate="1.2" :zoom-rate="1.2"
:preview-src-list="[item.pic_path]" :preview-src-list="[item.pic_path]"
fit="cover" fit="cover"
class="w-[100px]" class="w-[100%] h-[100%] rounded"
/> />
</div>
<span>{{ item.pic_quality }}</span> <span>{{ item.pic_quality }}</span>
</div> </div>
</el-scrollbar> </el-scrollbar>
@ -372,18 +410,17 @@ onMounted(() => {
</div> </div>
<div <div
class="analysis-box" class="analysis-box"
:style="{ height: `calc(${height}px - 60vh - 150px)` }" :style="{ height: hightFlag ? `360px` : `270px` }"
> >
<span class="analysis-box-label">被执法车辆推定</span> <span class="analysis-box-label">被执法车辆推定</span>
<div <div class="analysis-table">
class="analysis-table"
:style="{ height: `calc(${height}px - 60vh - 240px)` }"
>
<div class="analysis-table-left"> <div class="analysis-table-left">
<div class="analysis-box-lable w-full flex justify-end"> <div class="analysis-box-lable w-full flex justify-end">
<span>目标清晰度</span> <span class="w-[40px]" />
<span class="w-[120px]" />
<span class="ml-1">目标清晰度</span>
</div> </div>
<el-scrollbar :height="`calc(${height}px - 60vh - 220px)`"> <el-scrollbar :height="hightFlag ? `270px` : `180px`">
<div <div
v-for="(item, index) in carOddList" v-for="(item, index) in carOddList"
:key="index" :key="index"
@ -394,12 +431,11 @@ onMounted(() => {
}}</span> }}</span>
<div class="defect-image"> <div class="defect-image">
<el-image <el-image
style="height: 100px"
:src="item.pic_path" :src="item.pic_path"
:zoom-rate="1.2" :zoom-rate="1.2"
:preview-src-list="[item.pic_path]" :preview-src-list="[item.pic_path]"
fit="cover" fit="cover"
class="w-[100px]" class="w-[100%] h-[100%] rounded"
/> />
<span>{{ item.car_number }}</span> <span>{{ item.car_number }}</span>
</div> </div>
@ -407,15 +443,17 @@ onMounted(() => {
</div> </div>
</el-scrollbar> </el-scrollbar>
</div> </div>
<el-divider <!-- <el-divider
direction="vertical" direction="vertical"
:style="{ height: `calc(${height}px - 60vh - 240px)` }" :style="{ height: `calc(${height}px - 60vh - 240px)` }"
/> /> -->
<div class="analysis-table-left"> <div class="analysis-table-left">
<div class="analysis-box-lable w-full flex justify-end"> <div class="analysis-box-lable w-full flex justify-end">
<span>目标清晰度</span> <span class="w-[40px]" />
<span class="w-[120px]" />
<span class="ml-1">目标清晰度</span>
</div> </div>
<el-scrollbar :height="`calc(${height}px - 60vh - 220px)`"> <el-scrollbar :height="hightFlag ? `270px` : `180px`">
<div <div
v-for="(item, index) in carEvenList" v-for="(item, index) in carEvenList"
:key="index" :key="index"
@ -426,12 +464,11 @@ onMounted(() => {
}}</span> }}</span>
<div class="defect-image"> <div class="defect-image">
<el-image <el-image
style="height: 100px"
:src="item.pic_path" :src="item.pic_path"
:zoom-rate="1.2" :zoom-rate="1.2"
:preview-src-list="[item.pic_path]" :preview-src-list="[item.pic_path]"
fit="cover" fit="cover"
class="w-[100px]" class="w-[100%] h-[100%] rounded"
/> />
<span>{{ item.car_number }}</span> <span>{{ item.car_number }}</span>
</div> </div>
@ -442,7 +479,7 @@ onMounted(() => {
</div> </div>
</div> </div>
</div> </div>
<PureTableBar <!-- <PureTableBar
title="视频分析列表" title="视频分析列表"
:columns="columns" :columns="columns"
@refresh="onSearch" @refresh="onSearch"
@ -450,7 +487,7 @@ onMounted(() => {
<template v-slot="{ size, dynamicColumns }"> <template v-slot="{ size, dynamicColumns }">
<pure-table <pure-table
adaptive adaptive
align-whole="center" align-whole="left"
table-layout="auto" table-layout="auto"
:loading="loading" :loading="loading"
:size="size" :size="size"
@ -460,18 +497,16 @@ onMounted(() => {
:paginationSmall="size === 'small' ? true : false" :paginationSmall="size === 'small' ? true : false"
:header-cell-style="{ :header-cell-style="{
background: 'var(--el-table-row-hover-bg-color)', background: 'var(--el-table-row-hover-bg-color)',
backgroundColor: 'cornflowerblue',
color: 'var(--el-text-color-primary)' color: 'var(--el-text-color-primary)'
// color: 'blue' // color: 'blue'
}" }"
@page-size-change="handleSizeChange" @page-size-change="handleSizeChange"
@page-current-change="handleCurrentChange" @page-current-change="handleCurrentChange"
> >
<template #relative_time="{ row }">
<span>{{ dataFormate(row.relative_time) }}</span>
</template>
<template #behavior="{ row }"> <template #behavior="{ row }">
<span style="color: red">{{ row.abnormal_behavior }}</span> <span :class="tagStyle(row.abnormal_behavior)">{{
row.abnormal_behavior
}}</span>
</template> </template>
<template #image="{ row, index }"> <template #image="{ row, index }">
<el-image <el-image
@ -481,13 +516,20 @@ onMounted(() => {
:preview-src-list="[row.abnormal_pic]" :preview-src-list="[row.abnormal_pic]"
:initial-index="index" :initial-index="index"
fit="cover" fit="cover"
class="w-[100px] h-[100px]" class="h-[56px]"
/> />
</template> </template>
<template #exception_type="{ row }"> <template #exception_type="{ row }">
<el-button :type="getRandType(row.exception_type)"> <span
:class="
row.exception_type == '异常1'
? 'isbehavior'
: 'default-behavior'
"
:type="getRandType(row.exception_type)"
>
{{ row.exception_type }} {{ row.exception_type }}
</el-button> </span>
</template> </template>
<template #video="{ row }"> <template #video="{ row }">
<el-button <el-button
@ -502,7 +544,66 @@ onMounted(() => {
</template> </template>
</pure-table> </pure-table>
</template> </template>
</PureTableBar> </PureTableBar> -->
<div class="w-full h-[58px] flex items-center">
<h4>视频分析列表</h4>
</div>
<pure-table
adaptive
align-whole="left"
table-layout="auto"
:loading="loading"
:data="videoData"
:columns="columns"
:pagination="pagination"
:paginationSmall="false"
:header-cell-style="{
background: 'var(--el-table-row-hover-bg-color)',
color: 'var(--el-text-color-primary)'
// color: 'blue'
}"
@page-size-change="handleSizeChange"
@page-current-change="handleCurrentChange"
>
<template #behavior="{ row }">
<span :class="tagStyle(row.abnormal_behavior)">{{
row.abnormal_behavior
}}</span>
</template>
<template #image="{ row, index }">
<el-image
preview-teleported
loading="lazy"
:src="row.abnormal_pic"
:preview-src-list="[row.abnormal_pic]"
:initial-index="index"
fit="cover"
class="h-[56px] rounded"
/>
</template>
<template #exception_type="{ row }">
<span
:class="
row.exception_type == '异常1'
? 'isbehavior'
: 'default-behavior'
"
:type="getRandType(row.exception_type)"
>
{{ row.exception_type }}
</span>
</template>
<template #video="{ row }">
<el-button
class="reset-margin"
link
type="primary"
@click="handleUpdate(row)"
>
播放视频
</el-button>
</template>
</pure-table>
</el-card> </el-card>
</el-col> </el-col>
</el-row> </el-row>
@ -531,49 +632,78 @@ onMounted(() => {
.analysis-content { .analysis-content {
.analysis-box { .analysis-box {
width: 48%; width: 49.5%;
padding: 20px; // padding: 20px;
box-shadow: 5px 5px 5px rgb(0 0 0 / 2.5%), 5px -5px 5px rgb(0 0 0 / 2.5%), // box-shadow: 5px 5px 5px rgb(0 0 0 / 2.5%), 5px -5px 5px rgb(0 0 0 / 2.5%),
-5px 5px 5px rgb(0 0 0 / 2.5%), -5px -5px 5px rgb(0 0 0 / 2.5%); // -5px 5px 5px rgb(0 0 0 / 2.5%), -5px -5px 5px rgb(0 0 0 / 2.5%);
// background: skyblue; // background: skyblue;
// height: 400px; // height: 400px;
// border: 1px solid #eee;
// border-radius: 4px;
.analysis-box-label { .analysis-box-label {
font-size: 20px; display: block;
font-weight: bold; margin-bottom: 16px;
color: dodgerblue; font-size: 16px;
font-weight: 600;
color: rgb(51 51 51 / 100%);
} }
.analysis-table { .analysis-table {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
width: 100%; width: 100%;
border: 1px solid #eee;
border-radius: 4px;
.analysis-table-left { .analysis-table-left {
width: 45%; // width: 45%;
flex: 1;
.analysis-box-lable { .analysis-box-lable {
display: flex;
align-items: center;
justify-content: space-around;
// padding-left: 20px;
height: 48px;
padding-right: 10px; padding-right: 10px;
font-size: 14px;
color: #303133;
background: #f5f7fa;
} }
.scrollbar-item { .scrollbar-item {
box-sizing: border-box;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-around; justify-content: space-around;
margin-bottom: 20px; height: 90px;
padding: 10px 20px 10px 0;
font-size: 14px;
border-bottom: 1px solid #eee;
// margin-bottom: 20px;
.defect-image { .defect-image {
position: relative; position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 120px;
height: 70px;
background: #eee;
border-radius: 4px;
span { span {
position: absolute; position: absolute;
bottom: 7px; bottom: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 30px; height: 24px;
line-height: 30px; font-size: 12px;
line-height: 24px;
color: #fff; color: #fff;
text-align: center; text-align: center;
background-color: rgb(0 0 0 / 50%); background-color: rgb(0 0 0 / 60%);
border-radius: 0 0 4px 4px;
} }
} }
} }
@ -587,26 +717,117 @@ onMounted(() => {
} }
.span-first { .span-first {
font-size: 28px; box-sizing: border-box;
font-weight: bold; width: 24px;
color: #e02828; height: 24px;
font-size: 14px;
line-height: 20px;
color: #fff;
text-align: center;
background: rgb(224 40 40 / 100%);
border: 3px solid rgb(252 171 171 / 100%);
border-radius: 50%;
} }
.span-second { .span-second {
font-size: 28px; box-sizing: border-box;
font-weight: bold; width: 24px;
color: #e58b03; height: 24px;
font-size: 14px;
line-height: 20px;
color: #fff;
text-align: center;
background: rgb(229 139 3 / 100%);
border: 3px solid rgb(255 223 173 / 100%);
border-radius: 50%;
} }
.span-third { .span-third {
font-size: 28px; box-sizing: border-box;
font-weight: bold; width: 24px;
color: #516ee0; height: 24px;
font-size: 14px;
line-height: 20px;
color: #fff;
text-align: center;
background: rgb(81 110 224 / 100%);
border: 3px solid rgb(173 190 255 / 100%);
border-radius: 50%;
} }
.span-default { .span-default {
font-size: 28px; box-sizing: border-box;
font-weight: bold; width: 24px;
color: #555a64; height: 24px;
font-size: 14px;
line-height: 24px;
color: rgb(51 51 51 / 100%);
text-align: center;
background: rgb(28 13 130 / 15%);
border-radius: 50%;
}
.btn-style {
font-size: 20px;
font-weight: 600;
color: rgb(51 51 51 / 100%);
}
.isbehavior,
.default-behavior {
box-sizing: border-box;
display: inline-block;
width: 64px;
height: 24px;
font-size: 12px;
color: #fff;
text-align: center;
background: #f56c6c;
border-radius: 2px;
}
.default-behavior {
background: #448bf5;
}
.span-occlusion,
.span-smoke,
.span-money,
.span-stillness,
.span-shake {
box-sizing: border-box;
display: inline-block;
width: 64px;
height: 24px;
font-size: 12px;
color: rgb(247 95 25 / 100%);
text-align: center;
background: rgb(247 112 49 / 10%);
border: 1px solid #f75f19;
border-radius: 2px;
}
.span-smoke {
color: rgb(2 36 215 / 100%);
background: rgb(2 36 215 / 10%);
border: 1px solid rgb(2 36 215 / 100%);
}
.span-money {
color: rgb(232 13 13 / 100%);
background: rgb(232 13 13 / 10%);
border: 1px solid rgb(232 13 13 / 100%);
}
.span-stillness {
color: rgb(68 174 245 / 100%);
background: rgb(68 174 245 / 10%);
border: 1px solid rgb(68 174 245 / 100%);
}
.span-shake {
color: rgb(246 148 0 / 100%);
background: rgb(246 148 0 / 10%);
border: 1px solid rgb(246 148 0 / 100%);
} }
</style> </style>

Loading…
Cancel
Save