From aed8e13b3cbca5c83fbd0d8a57a3e0d9e6e8c561 Mon Sep 17 00:00:00 2001 From: houzhongjian <houzhongyi@126.com> Date: 星期五, 15 十一月 2024 16:49:11 +0800 Subject: [PATCH] 1、大华和海康摄像头图像采集功能开发 --- src/views/data/video/nvr/index.vue | 187 ++++++------ /dev/null | 28 -- src/views/Login/components/LoginForm.vue | 32 +- src/views/data/video/nvr/NvrCamera.vue | 174 ++++++----- src/views/data/video/camera/index.vue | 197 +++++++------ types/env.d.ts | 1 src/utils/hostMap.ts | 2 src/views/bpm/model/index.vue | 17 - src/api/data/video/image/index.ts | 50 +++ src/views/Home/Index.vue | 29 + src/utils/is.ts | 3 src/views/data/video/camera/CameraImage.vue | 114 ++++++++ 12 files changed, 498 insertions(+), 336 deletions(-) diff --git a/src/api/data/video/image/index.ts b/src/api/data/video/image/index.ts new file mode 100644 index 0000000..d2d5f7a --- /dev/null +++ b/src/api/data/video/image/index.ts @@ -0,0 +1,50 @@ +import request from '@/config/axios' + +export interface ImageVO { + id: undefined, + cameraId: string, + imagePath: string, + imageUrl: string, + createDate: undefined, +} + +// 查询列表 +export const getImagePage = (params: PageParam) => { + return request.get({url: '/data/video/image/page', params}) +} + +// 获得 +export const getImage = (id: number) => { + return request.get({url: '/data/video/image/get?id=' + id}) +} + +// 查询应用列表 +export const getImageList = () => { + return request.get({url: '/data/video/image/list'}) +} + +// 新增 +export const createImage = (data: ImageVO) => { + return request.post({url: '/data/video/image/create', data}) +} + +// 修改 +export const updateImage = (data: ImageVO) => { + return request.put({url: '/data/video/image/update', data}) +} + +// 删除 +export const deleteImage = (id: number) => { + return request.delete({url: '/data/video/image/delete?id=' + id}) +} + +// 导出 +export const exportImage = (params: ImageVO) => { + return request.download({url: '/data/video/image/export-excel', params}) +} + +//预览摄像头截图 +export const getPreviewUrl = (url: string) => { + const host = import.meta.env.VITE_VIDEO_CAMERA_DOMAIN ? import.meta.env.VITE_VIDEO_CAMERA_DOMAIN : window.location.host + return `http://${host}:8899` + url +} diff --git a/src/utils/hostMap.ts b/src/utils/hostMap.ts index 81df37d..c2904af 100644 --- a/src/utils/hostMap.ts +++ b/src/utils/hostMap.ts @@ -1,6 +1,6 @@ const map = { "//localhost:7200/": "//wujie-micro.github.io/demo-vue2/", - "//localhost:90/": "//localhost:90/", + "//localhost:9000/": "//localhost:9000/", "//localhost:8000/": "//wujie-micro.github.io/demo-main-vue/", }; diff --git a/src/utils/is.ts b/src/utils/is.ts index eec86a9..39812b6 100644 --- a/src/utils/is.ts +++ b/src/utils/is.ts @@ -98,8 +98,9 @@ export const isClient = !isServer export const isUrl = (path: string): boolean => { + // fix:修复hash路由无法跳转的问题 const reg = - /(((^https?:(?:\/\/)?)(?:[-:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&%@.\w_]*)#?(?:[\w]*))?)$/ + /(((^https?:(?:\/\/)?)(?:[-:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%#\/.\w-_]*)?\??(?:[-\+=&%@.\w_]*)#?(?:[\w]*))?)$/ return reg.test(path) } diff --git a/src/views/Home/Index.vue b/src/views/Home/Index.vue index aa64a85..822f800 100644 --- a/src/views/Home/Index.vue +++ b/src/views/Home/Index.vue @@ -5,9 +5,9 @@ <el-skeleton :loading="loading" animated> <div id="app" v-for="(item, index) in appList" :key="`dynamics-${index}`"> <div class="card" @click="gotoApp(item)"> - <img :src="item.icon" style="width: 100px; height: 100px" /> + <img :src="item.icon" style="width: 100px; height: 100px"/> <div> - {{item.appName}} + {{ item.appName }} </div> </div> </div> @@ -19,12 +19,11 @@ import * as AppApi from '@/api/system/app' import {Apps} from "@/views/Home/types"; import {CACHE_KEY, useCache} from "@/hooks/web/useCache"; -import * as authUtil from "@/utils/auth"; -defineOptions({ name: 'Home' }) +defineOptions({name: 'Home'}) -const { wsCache } = useCache() +const {wsCache} = useCache() const loading = ref(true) @@ -35,7 +34,7 @@ appList = Object.assign(appList, data) } -const getAppMenuList = async (id) => { +const getAppMenuList = async (id, appCode) => { const data = await AppApi.getAppMenuList(id) let userInfo = wsCache.get(CACHE_KEY.USER) userInfo.menus = data @@ -55,32 +54,38 @@ // 进入应用 const gotoApp = async (item) => { + let path = window.location.pathname + let appName = path.split("/")[0] + console.log(appName) let id = item.id let type = item.type - if(type === 0) { - getAppMenuList(id) + let appCode = item.appCode + if (type === 0) { + await getAppMenuList(id, appCode) } else { const data = await AppApi.getAppMenuList(id) let userInfo = wsCache.get(CACHE_KEY.USER) userInfo.menus = data wsCache.set(CACHE_KEY.USER, userInfo) wsCache.set(CACHE_KEY.ROLE_ROUTERS, data) - // await OAuth2Login(formData.value) - // window.open(item.appDomain + '/login?appid=' + item.id + "&username=" + authUtil.getLoginForm().username, '_blank') window.open(item.appDomain + '/index', '_blank') + // window.open('/plat/shasteel', '_blank') + // window.location.href = '/plat/shasteel' + // window.location.href = `/plat/shasteel?key=energy&url=http://localhost:9000&energy=/energy/demo` } } </script> <style lang="scss" scoped> -#app{ +#app { width: 300px; height: 200px; display: inline-block; background: transparent; } -.card{ + +.card { border: thin dashed gainsboro; width: 150px; height: 120px; diff --git a/src/views/Login/components/LoginForm.vue b/src/views/Login/components/LoginForm.vue index 22a80b2..bc3a32a 100644 --- a/src/views/Login/components/LoginForm.vue +++ b/src/views/Login/components/LoginForm.vue @@ -12,7 +12,7 @@ <el-row style="margin-right: -10px; margin-left: -10px"> <el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-form-item> - <LoginFormTitle style="width: 100%" /> + <LoginFormTitle style="width: 100%"/> </el-form-item> </el-col> <el-col :span="24" style="padding-right: 10px; padding-left: 10px"> @@ -86,27 +86,27 @@ </el-form> </template> <script lang="ts" setup> -import { ElLoading } from 'element-plus' +import {ElLoading} from 'element-plus' import LoginFormTitle from './LoginFormTitle.vue' -import type { RouteLocationNormalizedLoaded } from 'vue-router' +import type {RouteLocationNormalizedLoaded} from 'vue-router' -import { useIcon } from '@/hooks/web/useIcon' +import {useIcon} from '@/hooks/web/useIcon' import * as authUtil from '@/utils/auth' -import { usePermissionStore } from '@/store/modules/permission' +import {usePermissionStore} from '@/store/modules/permission' import * as LoginApi from '@/api/login' -import { LoginStateEnum, useFormValid, useLoginState } from './useLogin' +import {LoginStateEnum, useFormValid, useLoginState} from './useLogin' -defineOptions({ name: 'LoginForm' }) +defineOptions({name: 'LoginForm'}) -const { t } = useI18n() -const iconHouse = useIcon({ icon: 'ep:house' }) -const iconAvatar = useIcon({ icon: 'ep:avatar' }) -const iconLock = useIcon({ icon: 'ep:lock' }) +const {t} = useI18n() +const iconHouse = useIcon({icon: 'ep:house'}) +const iconAvatar = useIcon({icon: 'ep:avatar'}) +const iconLock = useIcon({icon: 'ep:lock'}) const formLogin = ref() -const { validForm } = useFormValid(formLogin) -const { getLoginState } = useLoginState() -const { currentRoute, push } = useRouter() +const {validForm} = useFormValid(formLogin) +const {getLoginState} = useLoginState() +const {currentRoute, push} = useRouter() const permissionStore = usePermissionStore() const redirect = ref<string>('') const loginLoading = ref(false) @@ -199,14 +199,14 @@ authUtil.removeLoginForm() } authUtil.setToken(res) - if (!redirect.value) { + if (!redirect.value || redirect.value == "/") { redirect.value = '/index' } // 判断是否为SSO登录 if (redirect.value.indexOf('sso') !== -1) { window.location.href = window.location.href.replace('/login?redirect=', '') } else { - push({ path: redirect.value || permissionStore.addRouters[0].path }) + push({path: redirect.value || permissionStore.addRouters[0].path}) } } finally { loginLoading.value = false diff --git a/src/views/bpm/model/index.vue b/src/views/bpm/model/index.vue index f8b0b75..a20ea4e 100644 --- a/src/views/bpm/model/index.vue +++ b/src/views/bpm/model/index.vue @@ -163,14 +163,6 @@ <el-button link type="primary" - @click="handleSimpleDesign(scope.row.id)" - v-hasPermi="['bpm:model:update']" - > - 仿钉钉设计流程 - </el-button> - <el-button - link - type="primary" @click="handleDeploy(scope.row)" v-hasPermi="['bpm:model:deploy']" > @@ -328,15 +320,6 @@ const handleDesign = (row) => { push({ name: 'BpmModelEditor', - query: { - modelId: row.id - } - }) -} - -const handleSimpleDesign = (row) => { - push({ - name: 'SimpleWorkflowDesignEditor', query: { modelId: row.id } diff --git a/src/views/bpm/simpleWorkflow/index.vue b/src/views/bpm/simpleWorkflow/index.vue deleted file mode 100644 index 144615e..0000000 --- a/src/views/bpm/simpleWorkflow/index.vue +++ /dev/null @@ -1,28 +0,0 @@ -<template> - <div> - <section class="dingflow-design"> - <div class="box-scale"> - <nodeWrap v-model:nodeConfig="nodeConfig" /> - <div class="end-node"> - <div class="end-node-circle"></div> - <div class="end-node-text">流程结束</div> - </div> - </div> - </section> - </div> -</template> -<script lang="ts" setup> -import nodeWrap from '@/components/SimpleProcessDesigner/src/nodeWrap.vue' -defineOptions({ name: 'SimpleWorkflowDesignEditor' }) -let nodeConfig = ref({ - nodeName: '发起人', - type: 0, - id: 'root', - formPerms: {}, - nodeUserList: [], - childNode: {} -}) -</script> -<style> -@import url('@/components/SimpleProcessDesigner/theme/workflow.css'); -</style> \ No newline at end of file diff --git a/src/views/data/video/camera/CameraImage.vue b/src/views/data/video/camera/CameraImage.vue new file mode 100644 index 0000000..335983b --- /dev/null +++ b/src/views/data/video/camera/CameraImage.vue @@ -0,0 +1,114 @@ +<template> + <el-dialog v-model="dialogVisible" :title="dialogTitle" :close="handleClose" + style="width: 50%; margin-top: 20px; overflow: auto; z-index: 1"> + <!-- 列表 --> + <el-table v-loading="loading" :data="list"> + <el-table-column + label="截图时间" + align="center" + prop="createDate" + :formatter="dateFormatter" + width="200" + /> + <el-table-column label="图片路径" align="center" prop="imagePath" width="500" + :show-overflow-tooltip="true"/> + <el-table-column label="图片预览" align="center" prop="imageUrl"> + <template #default="scope"> + <el-image style="height: 50px; z-index: 1" + :src="getPreviewUrl(scope.row.imageUrl)" + :preview-src-list="getPreviewSrcList(scope.row.imageUrl)" fit="cover" + preview-teleported/> + </template> + </el-table-column> + <el-table-column label="操作" align="center" min-width="60" fixed="right"> + <template #default="scope"> + <el-button + link + type="danger" + style="z-index: 1" + @click="handleDelete(scope.row.id)" + v-hasPermi="['video:camera:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="open" + /> + </el-dialog> +</template> +<script lang="ts" setup> +import * as ImageApi from '@/api/data/video/image' +import {getPreviewUrl} from "@/api/data/video/image"; +import {dateFormatter} from "@/utils/formatTime"; + +defineOptions({name: 'CameraImage'}) + +const message = useMessage() // 消息弹窗 +const {t} = useI18n() // 国际化 +const dialogTitle = ref('截图列表') +const loading = ref(true) // 列表的加载中 +const total = ref(0) // 列表的总页数 +const list = ref([]) // 列表的数据 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + id: undefined, + brand: undefined, + code: undefined, + device: undefined, + cameraId: '', + imagePath: undefined, + imageUrl: undefined, + createDate: undefined, +}) +const cameraId = ref('') +const dialogVisible = ref(false) + +/** 查询列表 */ +const open = async (camera_id: string) => { + dialogVisible.value = true + cameraId.value = camera_id + queryParams.cameraId = camera_id + loading.value = true + try { + const data = await ImageApi.getImagePage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +defineExpose({open}) // 提供 open 方法,用于打开弹窗 + +const handleClose = async () => { + dialogVisible.value = false +} + +const getPreviewSrcList = (imageUrl: string) => { + let previewSrcList: string[] = [] + previewSrcList.push(getPreviewUrl(imageUrl)) + return previewSrcList +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await ImageApi.deleteImage(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await open(cameraId.value) + } catch { + } +} +</script> diff --git a/src/views/data/video/camera/index.vue b/src/views/data/video/camera/index.vue index d0fbbe6..add49c5 100644 --- a/src/views/data/video/camera/index.vue +++ b/src/views/data/video/camera/index.vue @@ -61,11 +61,11 @@ </el-form-item> <el-form-item> <el-button @click="handleQuery"> - <Icon icon="ep:search" class="mr-5px" /> + <Icon icon="ep:search" class="mr-5px"/> 搜索 </el-button> <el-button @click="resetQuery"> - <Icon icon="ep:refresh" class="mr-5px" /> + <Icon icon="ep:refresh" class="mr-5px"/> 重置 </el-button> <el-button @@ -74,7 +74,7 @@ @click="openForm('create')" v-hasPermi="['video:camera:save']" > - <Icon icon="ep:plus" class="mr-5px" /> + <Icon icon="ep:plus" class="mr-5px"/> 新增 </el-button> <el-button @@ -84,7 +84,7 @@ :loading="exportLoading" v-hasPermi="['video:camera:export']" > - <Icon icon="ep:download" class="mr-5px" /> + <Icon icon="ep:download" class="mr-5px"/> 导出 </el-button> </el-form-item> @@ -96,20 +96,21 @@ <el-table v-loading="loading" :data="list"> <el-table-column label="品牌" align="center" prop="brand" width="80"> <template #default="scope"> - <dict-tag :type="DICT_TYPE.CAMERA_BRAND" :value="scope.row.brand" /> + <dict-tag :type="DICT_TYPE.CAMERA_BRAND" :value="scope.row.brand"/> </template> </el-table-column> <el-table-column label="设备类型" align="center" prop="device" width="200"/> <el-table-column label="编码" align="center" prop="code" width="200"/> - <el-table-column label="IP" align="center" prop="ip" /> - <el-table-column label="端口" align="center" prop="port" width="100"/> + <el-table-column label="IP" align="center" prop="ip"/> + <el-table-column label="端口" align="center" prop="port" width="80"/> + <el-table-column label="通道" align="center" prop="channel" width="80"/> <el-table-column label="用户名" align="center" prop="username" width="100"/> <el-table-column label="状态" prop="status" width="80"> <template #default="scope"> - <dict-tag :type="DICT_TYPE.NVR_ONLINE_STATUS" :value="scope.row.status" /> + <dict-tag :type="DICT_TYPE.NVR_ONLINE_STATUS" :value="scope.row.status"/> </template> </el-table-column> - <el-table-column label="位置" align="center" prop="location" /> + <el-table-column label="位置" align="center" prop="location"/> <el-table-column label="备注" align="center" prop="remark" width="150"/> <el-table-column label="操作" align="center" min-width="110" fixed="right"> <template #default="scope"> @@ -129,6 +130,14 @@ > 删除 </el-button> + <el-button + link + type="success" + @click="imageHandle(scope.row.id)" + v-hasPermi="['video:image:query']" + > + 查看截图 + </el-button> </template> </el-table-column> </el-table> @@ -142,99 +151,109 @@ </ContentWrap> <!-- 表单弹窗:添加/修改 --> - <CameraForm ref="formRef" @success="getList" /> + <CameraForm ref="formRef" @success="getList"/> + <CameraImage ref="imageFormRef"/> </template> <script lang="ts" setup> import {DICT_TYPE, getIntDictOptions} from '@/utils/dict' - import download from '@/utils/download' - import * as CameraApi from '@/api/data/video/camera' - import CameraForm from './CameraForm.vue' +import download from '@/utils/download' +import * as CameraApi from '@/api/data/video/camera' +import CameraForm from './CameraForm.vue' +import CameraImage from './CameraImage.vue' - defineOptions({name: 'Camera'}) - const message = useMessage() // 消息弹窗 - const {t} = useI18n() // 国际化 +defineOptions({name: 'Camera'}) - const loading = ref(true) // 列表的加载中 - const total = ref(0) // 列表的总页数 - const list = ref([]) // 列表的数据 - const queryParams = reactive({ - pageNo: 1, - pageSize: 10, - type: 1, - brand: undefined, - ip: undefined, - code: undefined, - device: undefined, - location: undefined, - status: undefined - }) - const queryFormRef = ref() // 搜索的表单 - const exportLoading = ref(false) // 导出的加载中 +const message = useMessage() // 消息弹窗 +const {t} = useI18n() // 国际化 - /** 查询列表 */ - const getList = async () => { - loading.value = true - try { - const data = await CameraApi.getCameraPage(queryParams) - list.value = data.list - total.value = data.total - } finally { - loading.value = false - } +const loading = ref(true) // 列表的加载中 +const total = ref(0) // 列表的总页数 +const list = ref([]) // 列表的数据 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + type: 1, + brand: undefined, + ip: undefined, + code: undefined, + device: undefined, + location: undefined, + status: undefined +}) +const queryFormRef = ref() // 搜索的表单 +const exportLoading = ref(false) // 导出的加载中 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await CameraApi.getCameraPage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false } +} - /** 搜索按钮操作 */ - const handleQuery = () => { - queryParams.pageNo = 1 - getList() - } +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} - /** 重置按钮操作 */ - const resetQuery = () => { - queryFormRef.value.resetFields() - handleQuery() - } +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + queryParams.brand = undefined + handleQuery() +} - /** 添加/修改操作 */ - const formRef = ref() - const openForm = (type: string, id?: number) => { - formRef.value.open(type, id) - } +/** 添加/修改操作 */ +const formRef = ref() +const openForm = (type: string, id?: number) => { + formRef.value.open(type, id) +} - /** 删除按钮操作 */ - const handleDelete = async (id: number) => { - try { - // 删除的二次确认 - await message.delConfirm() - // 发起删除 - await CameraApi.deleteCamera(id) - message.success(t('common.delSuccess')) - // 刷新列表 - await getList() - } catch { - } - } - - /** 导出按钮操作 */ - const handleExport = async () => { - try { - // 导出的二次确认 - await message.exportConfirm() - // 发起导出 - exportLoading.value = true - const data = await CameraApi.exportCamera(queryParams) - download.excel(data, '录像机列表.xls') - } catch { - } finally { - exportLoading.value = false - } - } - - /** 初始化 **/ - onMounted(async () => { +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await CameraApi.deleteCamera(id) + message.success(t('common.delSuccess')) + // 刷新列表 await getList() - }) + } catch { + } +} + +/** 查看截图 */ +const imageFormRef = ref() +const imageHandle = (id: string) => { + imageFormRef.value.open(id) +} + +/** 导出按钮操作 */ +const handleExport = async () => { + try { + // 导出的二次确认 + await message.exportConfirm() + // 发起导出 + exportLoading.value = true + const data = await CameraApi.exportCamera(queryParams) + download.excel(data, '录像机列表.xls') + } catch { + } finally { + exportLoading.value = false + } +} + +/** 初始化 **/ +onMounted(async () => { + await getList() +}) </script> diff --git a/src/views/data/video/nvr/NvrCamera.vue b/src/views/data/video/nvr/NvrCamera.vue index 0ff49b3..212ff57 100644 --- a/src/views/data/video/nvr/NvrCamera.vue +++ b/src/views/data/video/nvr/NvrCamera.vue @@ -6,95 +6,104 @@ @close="handleClose" size="60%"> <div class="mod-dev__camera" style="padding: 10px;"> - <el-form - class="-mb-15px" - :model="queryParams" - ref="queryFormRef" - :inline="true" - label-width="68px" - > - <el-form-item label="监控区域" prop="code"> - <el-input - v-model="queryParams.location" - placeholder="请输入监控区域" - clearable - @keyup.enter="handleQuery" - class="!w-240px" - /> - </el-form-item> - <el-form-item> - <el-button @click="handleQuery"> - <Icon icon="ep:search" class="mr-5px" /> - 搜索 - </el-button> - <el-button @click="resetQuery"> - <Icon icon="ep:refresh" class="mr-5px" /> - 重置 - </el-button> - <el-button - type="primary" - plain - @click="openForm('create')" - > - <Icon icon="ep:plus" class="mr-5px" /> - 新增 - </el-button> - <el-button - type="success" - plain - @click="handleExport" - :loading="exportLoading" - v-hasPermi="['dev:camera:export']" - > - <Icon icon="ep:download" class="mr-5px" /> - 导出 - </el-button> - </el-form-item> - </el-form> - - <!-- 列表 --> - <el-table v-loading="loading" :data="list"> - <el-table-column label="编码" align="center" prop="code" width="80" /> - <el-table-column label="抓图方式" align="center" prop="captureType" width="80"> - <template #default="scope"> - <dict-tag :type="DICT_TYPE.CAPTURE_TYPE" :value="scope.row.captureType" /> - </template> - </el-table-column> - <el-table-column label="通道" align="center" prop="channel" width="80" /> - <el-table-column label="监控区域" align="center" prop="location" /> - <el-table-column label="备注" align="center" prop="remark" width="200" /> - <el-table-column label="操作" align="center" min-width="110" fixed="right"> - <template #default="scope"> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="监控区域" prop="code"> + <el-input + v-model="queryParams.location" + placeholder="请输入监控区域" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"> + <Icon icon="ep:search" class="mr-5px"/> + 搜索 + </el-button> + <el-button @click="resetQuery"> + <Icon icon="ep:refresh" class="mr-5px"/> + 重置 + </el-button> <el-button - link type="primary" - @click="openForm('update', scope.row.id)" + plain + @click="openForm('create')" > - 编辑 + <Icon icon="ep:plus" class="mr-5px"/> + 新增 </el-button> <el-button - link - type="danger" - @click="handleDelete(scope.row.id)" + type="success" + plain + @click="handleExport" + :loading="exportLoading" + v-hasPermi="['dev:camera:export']" > - 删除 + <Icon icon="ep:download" class="mr-5px"/> + 导出 </el-button> - </template> - </el-table-column> - </el-table> - <!-- 分页 --> - <Pagination - :total="total" - v-model:page="queryParams.pageNo" - v-model:limit="queryParams.pageSize" - @pagination="getList" - /> + </el-form-item> + </el-form> + + <!-- 列表 --> + <el-table v-loading="loading" :data="list"> + <el-table-column label="编码" align="center" prop="code" width="80"/> + <el-table-column label="抓图方式" align="center" prop="captureType" width="80"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.CAPTURE_TYPE" :value="scope.row.captureType"/> + </template> + </el-table-column> + <el-table-column label="通道" align="center" prop="channel" width="80"/> + <el-table-column label="监控区域" align="center" prop="location"/> + <el-table-column label="备注" align="center" prop="remark" width="200"/> + <el-table-column label="操作" align="center" min-width="110" fixed="right"> + <template #default="scope"> + <el-button + link + type="primary" + @click="openForm('update', scope.row.id)" + > + 编辑 + </el-button> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + > + 删除 + </el-button> + <el-button + link + type="success" + @click="imageHandle(scope.row.id)" + v-hasPermi="['video:image:query']" + > + 查看截图 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> </div> </el-drawer> <!-- 表单弹窗:添加/修改 --> - <NvrCameraForm ref="formRef" @success="getList" /> + <NvrCameraForm ref="formRef" @success="getList"/> + <CameraImage ref="imageFormRef"/> </template> <script lang="ts" setup> @@ -102,6 +111,7 @@ import * as CameraApi from '@/api/data/video/camera' import NvrCameraForm from './NvrCameraForm.vue' import {DICT_TYPE} from "@/utils/dict"; +import CameraImage from "../camera/CameraImage.vue"; defineOptions({name: 'NvrCamera'}) @@ -134,7 +144,7 @@ await getList() } -defineExpose({ open }) // 提供 open 方法,用于打开弹窗 +defineExpose({open}) // 提供 open 方法,用于打开弹窗 /** 查询列表 */ const getList = async () => { @@ -148,6 +158,12 @@ } } +/** 查看截图 */ +const imageFormRef = ref() +const imageHandle = (id: string) => { + imageFormRef.value.open(id) +} + /** 搜索按钮操作 */ const handleQuery = () => { queryParams.pageNo = 1 @@ -156,7 +172,7 @@ /** 重置按钮操作 */ const resetQuery = () => { - queryFormRef.value.resetFields() + queryParams.location = undefined handleQuery() } diff --git a/src/views/data/video/nvr/index.vue b/src/views/data/video/nvr/index.vue index 61d0163..b780a0e 100644 --- a/src/views/data/video/nvr/index.vue +++ b/src/views/data/video/nvr/index.vue @@ -52,11 +52,11 @@ </el-form-item> <el-form-item> <el-button @click="handleQuery"> - <Icon icon="ep:search" class="mr-5px" /> + <Icon icon="ep:search" class="mr-5px"/> 搜索 </el-button> <el-button @click="resetQuery"> - <Icon icon="ep:refresh" class="mr-5px" /> + <Icon icon="ep:refresh" class="mr-5px"/> 重置 </el-button> <el-button @@ -65,7 +65,7 @@ @click="openForm('create')" v-hasPermi="['video:nvr:save']" > - <Icon icon="ep:plus" class="mr-5px" /> + <Icon icon="ep:plus" class="mr-5px"/> 新增 </el-button> <el-button @@ -75,7 +75,7 @@ :loading="exportLoading" v-hasPermi="['video:nvr:export']" > - <Icon icon="ep:download" class="mr-5px" /> + <Icon icon="ep:download" class="mr-5px"/> 导出 </el-button> </el-form-item> @@ -87,20 +87,19 @@ <el-table v-loading="loading" :data="list"> <el-table-column label="品牌" align="center" prop="brand" width="80"> <template #default="scope"> - <dict-tag :type="DICT_TYPE.CAMERA_BRAND" :value="scope.row.brand" /> + <dict-tag :type="DICT_TYPE.CAMERA_BRAND" :value="scope.row.brand"/> </template> </el-table-column> <el-table-column label="编码" align="center" prop="code" width="100"/> <el-table-column label="名称" align="center" prop="name"/> - <el-table-column label="IP" align="center" prop="ip" /> + <el-table-column label="IP" align="center" prop="ip"/> <el-table-column label="端口" align="center" prop="port" width="100"/> <el-table-column label="用户名" align="center" prop="username" width="100"/> <el-table-column label="状态" prop="status" width="80"> <template #default="scope"> - <dict-tag :type="DICT_TYPE.NVR_ONLINE_STATUS" :value="scope.row.status" /> + <dict-tag :type="DICT_TYPE.NVR_ONLINE_STATUS" :value="scope.row.status"/> </template> </el-table-column> - <el-table-column label="位置" align="center" prop="position" /> <el-table-column label="备注" align="center" prop="remark" width="150"/> <el-table-column label="操作" align="center" min-width="110" fixed="right"> <template #default="scope"> @@ -120,7 +119,8 @@ > 删除 </el-button> - <el-button link type="success" size="small" @click="cameraHandle(scope.row.id)">摄像头</el-button> + <el-button link type="success" size="small" @click="cameraHandle(scope.row.id)">摄像头 + </el-button> </template> </el-table-column> </el-table> @@ -134,7 +134,7 @@ </ContentWrap> <!-- 表单弹窗:添加/修改 --> - <NvrForm ref="formRef" @success="getList" /> + <NvrForm ref="formRef" @success="getList"/> <!-- 弹窗, 摄像头 --> <NvrCamera ref="videoCameraRef"/> @@ -142,99 +142,100 @@ </template> <script lang="ts" setup> import {DICT_TYPE, getIntDictOptions} from '@/utils/dict' - import download from '@/utils/download' - import * as NvrApi from '@/api/data/video/nvr' - import NvrForm from './NvrForm.vue' - import NvrCamera from './NvrCamera.vue' +import download from '@/utils/download' +import * as NvrApi from '@/api/data/video/nvr' +import NvrForm from './NvrForm.vue' +import NvrCamera from './NvrCamera.vue' - defineOptions({name: 'Nvr'}) +defineOptions({name: 'Nvr'}) - const message = useMessage() // 消息弹窗 - const {t} = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 +const {t} = useI18n() // 国际化 - const loading = ref(true) // 列表的加载中 - const total = ref(0) // 列表的总页数 - const list = ref([]) // 列表的数据 - const queryParams = reactive({ - pageNo: 1, - pageSize: 10, - brand: undefined, - ip: undefined, - code: undefined, - name: undefined, - status: undefined - }) - const queryFormRef = ref() // 搜索的表单 - const exportLoading = ref(false) // 导出的加载中 +const loading = ref(true) // 列表的加载中 +const total = ref(0) // 列表的总页数 +const list = ref([]) // 列表的数据 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + brand: undefined, + ip: undefined, + code: undefined, + name: undefined, + status: undefined +}) +const queryFormRef = ref() // 搜索的表单 +const exportLoading = ref(false) // 导出的加载中 - const videoCameraRef = ref() +const videoCameraRef = ref() - /** 查询列表 */ - const getList = async () => { - loading.value = true - try { - const data = await NvrApi.getNvrPage(queryParams) - list.value = data.list - total.value = data.total - } finally { - loading.value = false - } +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await NvrApi.getNvrPage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false } +} - const cameraHandle = (id: string) => { - // devCameraVisible.value = true - videoCameraRef.value.open(id) - } +const cameraHandle = (id: string) => { + // devCameraVisible.value = true + videoCameraRef.value.open(id) +} - /** 搜索按钮操作 */ - const handleQuery = () => { - queryParams.pageNo = 1 - getList() - } +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} - /** 重置按钮操作 */ - const resetQuery = () => { - queryFormRef.value.resetFields() - handleQuery() - } +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + queryParams.brand = undefined + handleQuery() +} - /** 添加/修改操作 */ - const formRef = ref() - const openForm = (type: string, id?: number) => { - formRef.value.open(type, id) - } +/** 添加/修改操作 */ +const formRef = ref() +const openForm = (type: string, id?: number) => { + formRef.value.open(type, id) +} - /** 删除按钮操作 */ - const handleDelete = async (id: number) => { - try { - // 删除的二次确认 - await message.delConfirm() - // 发起删除 - await NvrApi.deleteNvr(id) - message.success(t('common.delSuccess')) - // 刷新列表 - await getList() - } catch { - } - } - - /** 导出按钮操作 */ - const handleExport = async () => { - try { - // 导出的二次确认 - await message.exportConfirm() - // 发起导出 - exportLoading.value = true - const data = await NvrApi.exportNvr(queryParams) - download.excel(data, '录像机列表.xls') - } catch { - } finally { - exportLoading.value = false - } - } - - /** 初始化 **/ - onMounted(async () => { +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await NvrApi.deleteNvr(id) + message.success(t('common.delSuccess')) + // 刷新列表 await getList() - }) + } catch { + } +} + +/** 导出按钮操作 */ +const handleExport = async () => { + try { + // 导出的二次确认 + await message.exportConfirm() + // 发起导出 + exportLoading.value = true + const data = await NvrApi.exportNvr(queryParams) + download.excel(data, '录像机列表.xls') + } catch { + } finally { + exportLoading.value = false + } +} + +/** 初始化 **/ +onMounted(async () => { + await getList() +}) </script> diff --git a/types/env.d.ts b/types/env.d.ts index 40ce343..82b4cd7 100644 --- a/types/env.d.ts +++ b/types/env.d.ts @@ -25,6 +25,7 @@ readonly VITE_UPLOAD_URL: string readonly VITE_API_URL: string readonly VITE_BASE_PATH: string + readonly VITE_VIDEO_CAMERA_DOMAIN: string readonly VITE_DROP_DEBUGGER: string readonly VITE_DROP_CONSOLE: string readonly VITE_SOURCEMAP: string -- Gitblit v1.9.3