新增历史图片抓取和分析功能

dev_0.0.1_xq
刘政 1 day ago
parent 4e900df757
commit dc107cc26a

302
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -36,7 +36,6 @@
"screenfull": "^6.0.2", "screenfull": "^6.0.2",
"sortablejs": "^1.15.6", "sortablejs": "^1.15.6",
"typescript-eslint": "^8.48.0", "typescript-eslint": "^8.48.0",
"vite-plugin-svg-icons": "^2.0.1",
"vue": "^3.5.25", "vue": "^3.5.25",
"vue-eslint-parser": "^10.2.0", "vue-eslint-parser": "^10.2.0",
"vue-i18n": "^9.14.5", "vue-i18n": "^9.14.5",
@ -67,6 +66,7 @@
"vite": "6.3.4", "vite": "6.3.4",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-dts": "^4.5.0", "vite-plugin-dts": "^4.5.0",
"vite-plugin-svg-icons": "^2.0.1",
"vite-plugin-vue-devtools": "^7.7.9", "vite-plugin-vue-devtools": "^7.7.9",
"vue-tsc": "^2.2.12" "vue-tsc": "^2.2.12"
} }

@ -13,4 +13,10 @@ export const refresh=(params:{id: number,deviceType: number})=>{
*/ */
export const deviceLogin=(params:{id: number})=>{ export const deviceLogin=(params:{id: number})=>{
return http.get<Boolean>(ADMIN_MODULE+`/device/login`,params) return http.get<Boolean>(ADMIN_MODULE+`/device/login`,params)
}
/**
* NVR
*/
export const captureHistoryAndCompose= (params: { ids: number[] | undefined })=>{
return http.get<Boolean>(ADMIN_MODULE+`/device/history/analysis`,params)
} }

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="70.760376mm"
height="70.760376mm"
viewBox="0 0 70.760376 70.760376"
version="1.1"
id="svg1"
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
sodipodi:docname="Cam.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="1.5999719"
inkscape:cx="163.12786"
inkscape:cy="179.37815"
inkscape:window-width="1939"
inkscape:window-height="1158"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1" />
<g
inkscape:label="图层 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-24.149177,-57.838309)">
<circle
style="fill:#dfff8e;fill-opacity:1;stroke:#a8a8a8;stroke-width:4.02186;stroke-dasharray:none;stroke-opacity:1"
id="path1"
cx="59.529366"
cy="93.218498"
r="33.369259" />
<g
id="g8"
style="fill:#6bb6ff;fill-opacity:1;stroke:#000000;stroke-width:1.6;stroke-dasharray:none;stroke-opacity:1"
transform="translate(-15.653376,-30.811052)">
<path
sodipodi:type="star"
style="fill:#6bb6ff;fill-opacity:1;stroke:#000000;stroke-width:1.54655;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.2;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="path6"
inkscape:flatsided="false"
sodipodi:sides="3"
sodipodi:cx="103.60217"
sodipodi:cy="112.25517"
sodipodi:r1="12.027572"
sodipodi:r2="6.0137858"
sodipodi:arg1="1.0471976"
sodipodi:arg2="2.0943951"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 109.61596,122.67136 -9.02068,-5.2081 -9.020679,-5.20809 9.020679,-5.20809 9.02068,-5.20809 0,10.41618 z"
inkscape:transform-center-x="2.9948907"
transform="matrix(0.99600848,0,0,1.074601,-11.193569,3.4000286)"
inkscape:transform-center-y="2.2783516e-06" />
<rect
style="fill:#6bb6ff;fill-opacity:1;stroke:#000000;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="rect6"
width="31.699444"
height="21.877115"
x="52.321907"
y="113.091" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

@ -21,8 +21,8 @@ interface SvgProps {
const props = withDefaults(defineProps<SvgProps>(), { const props = withDefaults(defineProps<SvgProps>(), {
prefix: 'icon', prefix: 'icon',
width: 16, width: 480,
height: 16 height: 480
}); });
const symbolId = computed(() => `#${props.prefix}-${props.name}`); const symbolId = computed(() => `#${props.prefix}-${props.name}`);

@ -13,6 +13,7 @@ import 'element-plus/theme-chalk/dark/css-vars.css';
// element icons // element icons
import * as Icons from '@element-plus/icons-vue'; import * as Icons from '@element-plus/icons-vue';
// vue i18n // vue i18n
import I18n from '@/languages'; import I18n from '@/languages';

@ -32,6 +32,7 @@
</span> </span>
<div v-if="data.type===1" style="margin-left: 5px;"> <div v-if="data.type===1" style="margin-left: 5px;">
<el-button link type="primary" @click="showComputeRoom(data)"></el-button> <el-button link type="primary" @click="showComputeRoom(data)"></el-button>
<el-button link type="primary" @click="historicalDataAnalysis(data)"></el-button>
</div> </div>
</span> </span>
</template> </template>
@ -461,7 +462,7 @@ import type {NvrForm, NvrQuery, NvrRow} from "@/api/types/monitor/nvr";
import {type ElForm, ElMessage, ElMessageBox, type TreeInstance} from "element-plus"; import {type ElForm, ElMessage, ElMessageBox, type TreeInstance} from "element-plus";
import {getCameraListApi, removeCameraApi, updateCameraApi} from "@/api/modules/monitor/Camera"; import {getCameraListApi, removeCameraApi, updateCameraApi} from "@/api/modules/monitor/Camera";
import type {CameraForm, CameraQuery, CameraRow} from "@/api/types/monitor/camera"; import type {CameraForm, CameraQuery, CameraRow} from "@/api/types/monitor/camera";
import {deviceLogin, refresh} from "@/api/modules/monitor/device"; import {captureHistoryAndCompose, deviceLogin, refresh} from "@/api/modules/monitor/device";
import CameraControl from "@/views/sysmonitortree/sysMonitorTree/components/CameraControl.vue"; import CameraControl from "@/views/sysmonitortree/sysMonitorTree/components/CameraControl.vue";
import VideoPlayer from "@/views/sysmonitortree/sysMonitorTree/components/VideoPlayer.vue"; import VideoPlayer from "@/views/sysmonitortree/sysMonitorTree/components/VideoPlayer.vue";
import {useDictOptions} from "@/hooks/useDictOptions"; import {useDictOptions} from "@/hooks/useDictOptions";
@ -821,7 +822,22 @@ const handleSubmit=async ()=>{
// //
const handleEditClose=()=>{ const handleEditClose=()=>{
editCameraDialog.editVisible=false; editCameraDialog.editVisible=false;
}
//
const historicalDataAnalysis=async (data:TreeNode)=>{
// nvrid
const nvrIds=data!.children?.map(item=>item.id);
if(nvrIds?.length==0) {
ElMessage.warning("当前变电站没有NVR设备");
return;
}
const res=await captureHistoryAndCompose({
ids:nvrIds
});
if(res.code==='0000')
{
ElMessage.success('后台任务正在处理中,请稍后查看结果');
}
} }
const cameraIds=[] as number[]; const cameraIds=[] as number[];
const deleteBatchLoading = ref(false); const deleteBatchLoading = ref(false);
@ -830,7 +846,7 @@ const handleBatchChannel=(data: CameraRow[])=>{
ids.length=0; ids.length=0;
for(let i=0;i<data.length;i++) for(let i=0;i<data.length;i++)
{ {
cameraIds.push(data[i].id); cameraIds.push(data[i]?.id);
} }
} }
// //
@ -1255,7 +1271,6 @@ onBeforeUnmount(() => {
} }
.sidebar { .sidebar {
width: 320px;
padding: 10px; padding: 10px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;

@ -0,0 +1,37 @@
<template>
<svg aria-hidden="true" class="svg-icon">
<use :href="symbolId" :fill="color" />
</svg>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
name: {
type: String,
required: true,
},
prefix: {
type: String,
default: 'icon',
},
//
color: {
type: String,
default: 'currentColor',
},
})
const symbolId = computed(() => `#${props.prefix}-${props.name}`)
</script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
</style>

@ -1,8 +1,9 @@
<template> <template>
<div class="main-img" > <div class="main-img" >
<el-button type="primary" :icon="VideoCamera" class="full-size-btn" @click="handlePlay"/> <el-icon :size="50" color="#67C23A" >
<SvgIcon name="Cam" @click="handlePlay" />
</el-icon>
</div> </div>
<teleport to="body"> <teleport to="body">
<el-dialog <el-dialog
v-model="customDraggingVisible" v-model="customDraggingVisible"
@ -32,6 +33,7 @@ import { VideoCamera } from '@element-plus/icons-vue';
import {useCameraBindStore} from "@/stores/modules/cameraBind"; import {useCameraBindStore} from "@/stores/modules/cameraBind";
import VideoPlayer from "@/views/sysmonitortree/sysMonitorTree/components/VideoPlayer.vue"; import VideoPlayer from "@/views/sysmonitortree/sysMonitorTree/components/VideoPlayer.vue";
import {ElMessage} from "element-plus"; import {ElMessage} from "element-plus";
import SvgIcon from "@/components/SvgIcon/index.vue";
const customDraggingVisible = ref(false); const customDraggingVisible = ref(false);
const cameraBindStore=useCameraBindStore(); const cameraBindStore=useCameraBindStore();
const props = defineProps({ const props = defineProps({

@ -1,5 +1,6 @@
// vite.config.mts // vite.config.mts
import { resolve } from 'path'; import { resolve } from 'path';
import path from 'path'
import { fileURLToPath, URL } from 'node:url'; import { fileURLToPath, URL } from 'node:url';
import { defineConfig, ConfigEnv, UserConfig, loadEnv } from 'vite'; import { defineConfig, ConfigEnv, UserConfig, loadEnv } from 'vite';
import vue from '@vitejs/plugin-vue'; import vue from '@vitejs/plugin-vue';
@ -37,6 +38,12 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
// iconDirs: [resolve(process.cwd(), 'src/assets/icons')], // iconDirs: [resolve(process.cwd(), 'src/assets/icons')],
// symbolId: 'icon-[dir]-[name]' // symbolId: 'icon-[dir]-[name]'
// }), // }),
createSvgIconsPlugin({
// 指定需要缓存的图标文件夹
iconDirs: [path.resolve(process.cwd(), 'src/assets/svgs/icons')],
// 指定 symbolId 格式,也就是我们在使用时图标名称的格式
symbolId: 'icon-[dir]-[name]',
}),
UnoCSS({ UnoCSS({
// 在低版本浏览器上开发时会报错 Unexpected reserved word // 在低版本浏览器上开发时会报错 Unexpected reserved word
// 如果在开发环境需要兼容不支持顶级await的低版本浏览器例如Chrome(v87),就将下面的配置打开 // 如果在开发环境需要兼容不支持顶级await的低版本浏览器例如Chrome(v87),就将下面的配置打开

Loading…
Cancel
Save