监控室,摄像头工作列表

This commit is contained in:
Teo
2025-06-19 19:58:33 +08:00
parent f59d725b20
commit ea98580b07
19 changed files with 1141 additions and 167 deletions

View File

@ -1,6 +1,6 @@
import request from '@/utils/request'; import request from '@/utils/request';
import { AxiosPromise } from 'axios'; import { AxiosPromise } from 'axios';
import { Ys7DeviceVO, Ys7DeviceForm, Ys7DeviceQuery } from '@/api/other/ys7Device/types'; import { Ys7DeviceVO, Ys7DeviceForm, Ys7DeviceQuery, CameraListForm } from '@/api/other/ys7Device/types';
/** /**
* 查询萤石摄像头列表 * 查询萤石摄像头列表
@ -89,3 +89,15 @@ export const getAccessToken = () => {
method: 'get' method: 'get'
}); });
}; };
/**
* 根据项目查询萤石摄像头列表
* @param id
*/
export const getCameraListByProjectId = (params: CameraListForm): AxiosPromise<Ys7DeviceVO> => {
return request({
url: '/other/ys7Device/list/project',
method: 'get',
params
});
};

View File

@ -129,3 +129,12 @@ export interface Ys7DeviceQuery extends PageQuery {
*/ */
params?: any; params?: any;
} }
export interface CameraListForm {
/**
* 项目id
*/
projectId: string | number;
pageSize?: number;
pageNum?: number;
}

View File

@ -0,0 +1,63 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { Ys7DeviceImgVO, Ys7DeviceImgForm, Ys7DeviceImgQuery } from '@/api/other/ys7DeviceImg/types';
/**
* 查询萤石摄像头图片列表
* @param query
* @returns {*}
*/
export const listYs7DeviceImg = (query?: Ys7DeviceImgQuery): AxiosPromise<Ys7DeviceImgVO[]> => {
return request({
url: '/other/ys7DeviceImg/list',
method: 'get',
params: query
});
};
/**
* 查询萤石摄像头图片详细
* @param id
*/
export const getYs7DeviceImg = (id: string | number): AxiosPromise<Ys7DeviceImgVO> => {
return request({
url: '/other/ys7DeviceImg/' + id,
method: 'get'
});
};
/**
* 新增萤石摄像头图片
* @param data
*/
export const addYs7DeviceImg = (data: Ys7DeviceImgForm) => {
return request({
url: '/other/ys7DeviceImg',
method: 'post',
data: data
});
};
/**
* 修改萤石摄像头图片
* @param data
*/
export const updateYs7DeviceImg = (data: Ys7DeviceImgForm) => {
return request({
url: '/other/ys7DeviceImg',
method: 'put',
data: data
});
};
/**
* 删除萤石摄像头图片
* @param id
*/
export const delYs7DeviceImg = (id: string | number | Array<string | number>) => {
return request({
url: '/other/ys7DeviceImg/' + id,
method: 'delete'
});
};

View File

@ -0,0 +1,76 @@
export interface Ys7DeviceImgVO {
/**
* 设备序列号
*/
deviceSerial: string;
id?: string | number;
/**
* 设备名称
*/
deviceName: string;
/**
* 图片地址
*/
url: string;
/**
* 备注
*/
createTime: string;
}
export interface Ys7DeviceImgForm extends BaseEntity {
/**
* 主键id
*/
id?: string | number;
/**
* 设备id
*/
deviceId?: string | number;
/**
* 设备序列号
*/
deviceSerial?: string;
/**
* 设备名称
*/
deviceName?: string;
/**
* 图片地址
*/
url?: string;
/**
* 备注
*/
createTime?: string;
}
export interface Ys7DeviceImgQuery extends PageQuery {
/**
* 设备id
*/
id?: string | number;
/**
* 设备序列号
*/
deviceSerial?: string;
/**
* 设备名称
*/
deviceName?: string;
/**
* 日期范围参数
*/
params?: any;
createTime?: string;
}

View File

@ -192,7 +192,7 @@ export const deleteDaily = (query: { id: string; detailIdList: string[] }) => {
export const workScheduleDel = () => { export const workScheduleDel = () => {
return request({ return request({
url: '/facility/matrix/gis/position/1933358820034174995', url: '/facility/matrix/gis/position?projectId=1930896467736707073',
method: 'get' method: 'get'
}); });
}; };

View File

@ -3,7 +3,11 @@ export interface ProgressCategoryTemplateVO {
* 类别名称 * 类别名称
*/ */
name: string; name: string;
/**
* 主键id
*/
id?: string | number;
pid?: string | number;
/** /**
* 计量方式0无 1数量 2百分比 * 计量方式0无 1数量 2百分比
*/ */
@ -24,10 +28,10 @@ export interface ProgressCategoryTemplateVO {
*/ */
remark: string; remark: string;
/** /**
* 子对象 * 子对象
*/ */
children: ProgressCategoryTemplateVO[]; children: ProgressCategoryTemplateVO[];
} }
export interface ProgressCategoryTemplateForm extends BaseEntity { export interface ProgressCategoryTemplateForm extends BaseEntity {
@ -65,11 +69,9 @@ export interface ProgressCategoryTemplateForm extends BaseEntity {
* 备注 * 备注
*/ */
remark?: string; remark?: string;
} }
export interface ProgressCategoryTemplateQuery { export interface ProgressCategoryTemplateQuery {
/** /**
* 父类别id * 父类别id
*/ */
@ -95,11 +97,8 @@ export interface ProgressCategoryTemplateQuery {
*/ */
projectId?: string | number; projectId?: string | number;
/** /**
* 日期范围参数 * 日期范围参数
*/ */
params?: any; params?: any;
} }

View File

@ -1,6 +1,5 @@
<template> <template>
<div class="ol-map" id="olMap"></div> <div class="ol-map" id="olMap"></div>
<el-button class="btn" type="primary" @click="drawRectangle">绘制矩形</el-button>
</template> </template>
<script setup> <script setup>
@ -12,8 +11,7 @@ const initFacilities = async () => {
const res = await workScheduleDel('1933358821565095951'); const res = await workScheduleDel('1933358821565095951');
console.log(res); console.log(res);
renderFacilitiesToCesium(sdk.viewer, res.data.photovoltaicPanelPositionList, 'Polygon', { flyToFirst: true }); renderFacilitiesToCesium(sdk.viewer, res.data);
renderFacilitiesToCesium(sdk.viewer, res.data.photovoltaicPanelPointPositionList, 'Point', { flyToFirst: true });
}; };
// 初始化 Cesium 地球 // 初始化 Cesium 地球
@ -41,22 +39,6 @@ const createEarth = () => {
new YJ.Tools(sdk).flyHome(0); new YJ.Tools(sdk).flyHome(0);
}; };
// 点击按钮时绘制一个矩形并飞行过去
const drawRectangle = () => {
const viewer = sdk.viewer;
const rectangleEntity = viewer.entities.add({
name: '测试矩形',
rectangle: {
coordinates: Cesium.Rectangle.fromDegrees(100.0, 30.0, 102.0, 32.0), // 西南角到东北角
material: Cesium.Color.YELLOW.withAlpha(0.5),
outline: true,
height: 2.5,
outlineColor: Cesium.Color.BLACK
}
});
viewer.flyTo(viewer.entities);
};
onMounted(async () => { onMounted(async () => {
// 最早执行 // 最早执行
window.CESIUM_BASE_URL = '/Cesium/'; window.CESIUM_BASE_URL = '/Cesium/';

View File

@ -1,153 +1,177 @@
import imgurl from '../img/guanfuban.png'; import imgurl from '../img/guanfuban.png';
function getStatusColor(status) {
switch (status) {
case '0':
return Cesium.Color.RED.withAlpha(0.4);
case '1':
return Cesium.Color.GREEN.withAlpha(0.4);
default:
return Cesium.Color.BLUE.withAlpha(0.4);
}
}
//计算经纬度包围框 → 生成矩形边界(空心) //计算经纬度包围框 → 生成矩形边界(空心)
function getBoundingBoxRectangle(positionList) { // function getBoundingBoxRectangle(positionList) {
const lons = positionList.map((p) => parseFloat(p[0])); // const lons = positionList.map((p) => parseFloat(p[0]));
const lats = positionList.map((p) => parseFloat(p[1])); // const lats = positionList.map((p) => parseFloat(p[1]));
const west = Math.min(...lons); // const west = Math.min(...lons);
const east = Math.max(...lons); // const east = Math.max(...lons);
const south = Math.min(...lats); // const south = Math.min(...lats);
const north = Math.max(...lats); // const north = Math.max(...lats);
return Cesium.Rectangle.fromDegrees(west, south, east, north); // return Cesium.Rectangle.fromDegrees(west, south, east, north);
} // }
export function renderFacilitiesToCesium(viewer, facilityList, type, options = {}) { // export function renderFacilitiesToCesium(viewer, facilityList, type, options = {}) {
const dataSource = new Cesium.CustomDataSource('facility-layer'); // const dataSource = new Cesium.CustomDataSource('facility-layer');
facilityList.forEach((facility) => { // facilityList.forEach((facility) => {
const { positionList, id, name, status, finishDate } = facility; // const { positionList, id, name, status, finishDate } = facility;
if (type === 'Polygon' && positionList.length >= 3) { // if (type === 'Polygon' && positionList.length >= 3) {
// 渲染 Polygon 面 + 贴图 // // 渲染 Polygon 面 + 贴图
// const cartesianPositions = positionList.map(([lon, lat]) => Cesium.Cartesian3.fromDegrees(parseFloat(lon), parseFloat(lat)));
// dataSource.entities.add({
// id,
// name,
// polygon: {
// hierarchy: new Cesium.PolygonHierarchy(cartesianPositions),
// material: new Cesium.ImageMaterialProperty({
// image: imgurl,
// repeat: new Cesium.Cartesian2(1.0, 1.0),
// transparent: true
// }),
// outline: false,
// height: 2.5
// },
// properties: {
// status,
// finishDate
// }
// });
// // 添加外框矩形线
// const rectangle = getBoundingBoxRectangle(positionList);
// dataSource.entities.add({
// name: `${name}_bbox`,
// rectangle: {
// coordinates: rectangle,
// fill: false,
// outline: true,
// outlineColor: Cesium.Color.YELLOW.withAlpha(0.8),
// outlineWidth: 2
// }
// });
// } else if (type === 'Point') {
// const [lonStr, latStr] = positionList;
// const lon = parseFloat(lonStr);
// const lat = parseFloat(latStr);
// dataSource.entities.add({
// id,
// name,
// position: Cesium.Cartesian3.fromDegrees(lon, lat),
// point: {
// pixelSize: 10,
// color:Cesium.Color.GREEN.withAlpha(0.4),
// outlineColor: Cesium.Color.WHITE,
// outlineWidth: 2
// },
// properties: {
// status,
// finishDate
// }
// });
// }
// });
// viewer.dataSources.add(dataSource).then(() => {
// if (options.flyToFirst && facilityList.length > 0) {
// const firstEntity = dataSource.entities.getById(facilityList[0].id);
// if (firstEntity) {
// viewer.flyTo(firstEntity);
// }
// }
// });
// }
export function renderFacilitiesToCesium(viewer, dataList) {
const pointCollection = new Cesium.PointPrimitiveCollection(); // gfbzd
const polygonDataSource = new Cesium.CustomDataSource('polygon-textured'); // gfb
const cylinderInstances = []; // gfblz
let flyTargetPositions = [];
dataList.forEach((data) => {
const { category, positionList, status } = data;
// ✅ gfbzd 点PointPrimitive 浮空)
if (category === 'gfbzd' && positionList.length === 2) {
const [lon, lat] = positionList.map(parseFloat);
pointCollection.add({
position: Cesium.Cartesian3.fromDegrees(lon, lat, 1), // 高度1米
color: Cesium.Color.GREEN.withAlpha(0.9),
pixelSize: 6,
outlineColor: Cesium.Color.WHITE,
outlineWidth: 1
});
}
// ✅ gfb 面Entity + Image 贴图)
else if (category === 'gfb' && positionList.length >= 3) {
const cartesianPositions = positionList.map(([lon, lat]) => Cesium.Cartesian3.fromDegrees(parseFloat(lon), parseFloat(lat))); const cartesianPositions = positionList.map(([lon, lat]) => Cesium.Cartesian3.fromDegrees(parseFloat(lon), parseFloat(lat)));
dataSource.entities.add({ polygonDataSource.entities.add({
id, id: data.id,
name, name: data.name,
polygon: { polygon: {
hierarchy: new Cesium.PolygonHierarchy(cartesianPositions), hierarchy: new Cesium.PolygonHierarchy(cartesianPositions),
material: new Cesium.ImageMaterialProperty({ material: new Cesium.ImageMaterialProperty({
image: imgurl, image: imgurl,
repeat: new Cesium.Cartesian2(1.0, 1.0), repeat: new Cesium.Cartesian2(1, 1),
transparent: true transparent: true
}), }),
outline: false, height: 2.5,
height: 2.5
},
properties: {
status,
finishDate
}
});
// 添加外框矩形线
const rectangle = getBoundingBoxRectangle(positionList);
dataSource.entities.add({
name: `${name}_bbox`,
rectangle: {
coordinates: rectangle,
fill: false,
outline: true, outline: true,
outlineColor: Cesium.Color.YELLOW.withAlpha(0.8), outlineColor: Cesium.Color.GREEN.withAlpha(0.4)
outlineWidth: 2
}
});
} else if (type === 'Point') {
const [lonStr, latStr] = positionList;
const lon = parseFloat(lonStr);
const lat = parseFloat(latStr);
dataSource.entities.add({
id,
name,
position: Cesium.Cartesian3.fromDegrees(lon, lat),
point: {
pixelSize: 10,
color:Cesium.Color.GREEN.withAlpha(0.4),
outlineColor: Cesium.Color.WHITE,
outlineWidth: 2
},
properties: {
status,
finishDate
} }
}); });
} }
// ✅ gfblz 数据Cylinder + Primitive 批量渲染
// else if (category === 'gfblz' && positionList.length === 2) {
// const [lon, lat] = positionList.map(parseFloat);
// const position = Cesium.Cartesian3.fromDegrees(lon, lat, 1.25); // 圆柱中点浮空 1.25 米(高的一半)
// cylinderInstances.push(
// new Cesium.GeometryInstance({
// geometry: new Cesium.CylinderGeometry({
// length: 2.5, // 高度 2.5 米
// topRadius: 0.5,
// bottomRadius: 0.5,
// vertexFormat: Cesium.MaterialAppearance.VERTEX_FORMAT
// }),
// modelMatrix: Cesium.Transforms.eastNorthUpToFixedFrame(position),
// attributes: {
// color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.GREEN.withAlpha(0.8))
// }
// })
// );
// }
}); });
viewer.dataSources.add(dataSource).then(() => { // ✅ 添加图层
if (options.flyToFirst && facilityList.length > 0) { viewer.scene.primitives.add(pointCollection); // gfbzd
const firstEntity = dataSource.entities.getById(facilityList[0].id); viewer.dataSources.add(polygonDataSource); // gfb
if (firstEntity) {
viewer.flyTo(firstEntity); // ✅ 批量添加圆柱 Primitivegfblz
} if (cylinderInstances.length > 0) {
} viewer.scene.primitives.add(
}); new Cesium.Primitive({
geometryInstances: cylinderInstances,
appearance: new Cesium.MaterialAppearance({
material: Cesium.Material.fromType('Color'),
translucent: true,
closed: false
}),
asynchronous: true
})
);
}
// ✅ fz 数据:仅用于定位,不渲染
if (dataList[0].category === 'fz') {
flyTargetPositions = dataList[0].positionList.map(([lon, lat]) => Cesium.Cartesian3.fromDegrees(parseFloat(lon), parseFloat(lat)));
}
// ✅ 飞到 fz 区域
if (flyTargetPositions.length > 0) {
const sphere = Cesium.BoundingSphere.fromPoints(flyTargetPositions);
viewer.camera.flyToBoundingSphere(sphere, {
duration: 2.0,
offset: new Cesium.HeadingPitchRange(0, -0.5, sphere.radius * 2)
});
}
} }
// export function renderFacilitiesToCesium(viewer, dataList) {
// const pointCollection = new Cesium.PointPrimitiveCollection();
// const polygonInstances = [];
// dataList.forEach(data => {
// if (data.type === 'Point') {
// const { position, status } = data;
// const [lon, lat] = position;
// pointCollection.add({
// position: Cesium.Cartesian3.fromDegrees(lon, lat),
// color: getStatusColor(status),
// pixelSize: 6,
// outlineColor: Cesium.Color.WHITE,
// outlineWidth: 1,
// });
// }
// else if (data.type === 'Polygon' && data.positions.length >= 3) {
// const hierarchy = new Cesium.PolygonHierarchy(
// data.positions.map(([lon, lat]) => Cesium.Cartesian3.fromDegrees(lon, lat))
// );
// polygonInstances.push(
// new Cesium.GeometryInstance({
// geometry: new Cesium.PolygonGeometry({
// polygonHierarchy: hierarchy,
// vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
// }),
// attributes: {
// color: Cesium.ColorGeometryInstanceAttribute.fromColor(getStatusColor(data.status).withAlpha(0.6)),
// },
// })
// );
// }
// });
// // 添加点图层
// viewer.scene.primitives.add(pointCollection);
// // 添加面图层Primitive
// if (polygonInstances.length > 0) {
// viewer.scene.primitives.add(
// new Cesium.Primitive({
// geometryInstances: polygonInstances,
// appearance: new Cesium.PerInstanceColorAppearance({
// flat: true,
// translucent: true,
// }),
// asynchronous: true,
// })
// );
// }
// }

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 613 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 555 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 940 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 482 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 B

View File

@ -0,0 +1,607 @@
<template>
<div class="system-monitor-list wh100">
<div class="main">
<div class="app-container">
<!-- 左边视频窗口 -->
<div class="left" id="divPlugin">
<div class="hello-ezuikit-js" ref="videoBoxRef" v-loading="loading">
<!-- 最多16格 -->
<div
v-for="item in param.pageSize"
v-show="param.pageSize == 1 || param.pageSize == 4 || param.pageSize == 9"
:key="item"
:class="param.pageSize == 1 ? 'width' : param.pageSize == 4 ? 'width2' : 'width3'"
:id="'video-container-parent' + item"
>
<div :id="'video-container' + item"></div>
</div>
</div>
</div>
<img v-if="!tableData.data.length && !loading" src="./icon/em.png" alt="" class="image_position" />
<!-- 右边操作区 -->
<div class="right">
<el-table v-loading="loading" :data="tableData.data" @row-click="handleRowClick">
<el-table-column label="序号" align="center" type="index" :index="indexMethod" width="60" />
<el-table-column label="摄像头名称" align="center" prop="deviceName" min-width="100px" show-overflow-tooltip />
<!-- <el-table-column label="操作" align="center" class-name="small-padding" fixed="right">
<template #default="scope">
<el-switch v-model="scope.row.status" />
</template>
</el-table-column> -->
</el-table>
</div>
</div>
<!-- 底部切屏按钮 -->
<div class="btm-btns">
<div class="left">
<div style="width: 15%">
<div @click="changePage(1)">
<img class="rect" v-if="param.pageSize == 1" src="./icon/one_1.png" alt="" />
<img class="rect" v-else src="./icon/one.png" alt="" />
</div>
<div @click="changePage(4)">
<img class="rect" v-if="param.pageSize == 4" src="./icon/four_1.png" alt="" />
<img class="rect" v-else src="./icon/four.png" alt="" />
</div>
<div @click="changePage(9)">
<img class="rect" v-if="param.pageSize == 9" src="./icon/nine_1.png" alt="" />
<img class="rect" v-else src="./icon/nine.png" alt="" />
</div>
<!-- <div @click="select = 4">
<img class="rect" v-if="param.pageSize == 4" src="./icon/six_1.png" alt="" />
<img class="rect" v-else src="./icon/six.png" alt="" />
</div> -->
</div>
<div>
<el-pagination
background
v-show="tableData.total > 0"
layout="total, prev, pager, next"
:total="tableData.total"
:page-sizes="[1, 4, 9]"
v-model:current-page="param.pageNum"
v-model:page-size="param.pageSize"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
<div class="right"></div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { getCameraListByProjectId, getAccessToken } from '@/api/other/ys7Device';
import EZUIKit from 'ezuikit-js';
import { useUserStoreHook } from '@/store/modules/user';
const { proxy } = getCurrentInstance() as any;
const loading = ref(false);
const videoBoxRef = ref<HTMLElement>();
// 获取用户 store
const userStore = useUserStoreHook();
// 从 store 中获取项目列表和当前选中的项目
const currentProject = computed(() => userStore.selectedProject);
const tableData = ref({
data: [] as any[],
total: 0,
param: {
pageNum: 1,
pageSize: 4,
projectId: currentProject.value.id
}
});
const param = ref({
pageNum: 1,
pageSize: 4
});
const cameraList = ref<any[][]>([]);
const accessToken = ref('');
const templateType = ref<'standard' | 'pcLive'>('pcLive');
const pageSizes = ref([1, 4, 9]);
const changePage = (num: number) => {
param.value.pageNum = 1;
param.value.pageSize = num;
reRender();
};
const handleSizeChange = (val: number) => {
param.value.pageSize = val;
reRender();
};
const handleCurrentChange = (val: number) => {
param.value.pageNum = val;
reRender();
};
const getDeviceToken = async () => {
const res: any = await getAccessToken();
if (res.code === 200) {
accessToken.value = res.data;
}
};
const getCameraList = async () => {
loading.value = true;
await clearDom();
const res: any = await getCameraListByProjectId({
pageNum: 1,
pageSize: 10,
projectId: currentProject.value.id
});
let list = res.rows?.list ?? [];
tableData.value.data = res.rows;
tableData.value.total = res.rows?.total ?? 0;
cameraList.value = groupArr(tableData.value.data, param.value.pageSize);
console.log(cameraList.value, tableData.value.data);
if (cameraList.value.length) {
cameraList.value[param.value.pageNum - 1].forEach((item, index) => {
StructureEZUIKitPlayer(`ezopen://open.ys7.com/${item.deviceSerial}/1.hd.live`, item, index);
});
}
loading.value = false;
};
const clearDom = async () => {
tableData.value.data.forEach((item) => {
if (item.player) {
item.player.destroy().then((data: any) => {
console.log('clearDom destroy', data);
});
item.player = null;
}
});
for (let index = 0; index < param.value.pageSize; index++) {
let parentElement: HTMLElement | null = null;
if (templateType.value === 'standard') {
parentElement = document.getElementById(`video-container${index + 1}`);
} else if (templateType.value === 'pcLive') {
parentElement = document.getElementById(`video-container-parent${index + 1}`);
}
const child = document.createElement('div');
child.setAttribute('id', `video-container${index + 1}`);
if (parentElement) {
while (parentElement.firstChild) {
parentElement.removeChild(parentElement.firstChild);
}
if (templateType.value === 'pcLive') {
parentElement.appendChild(child);
}
}
}
};
const StructureEZUIKitPlayer = (url: string, item: any, index: number) => {
item.player = new EZUIKit.EZUIKitPlayer({
audio: '0',
id: `video-container${index + 1}`,
accessToken: accessToken.value,
url,
template: templateType.value,
width: (videoBoxRef.value?.children[0] as HTMLElement | undefined)?.offsetWidth,
height: (videoBoxRef.value?.children[0] as HTMLElement | undefined)?.offsetHeight,
plugin: ['talk']
});
};
const indexMethod = (index: number) => {
const pageNum = tableData.value.param.pageNum - 1;
return index + 1 + (pageNum > 0 ? pageNum * tableData.value.param.pageSize : 0);
};
const groupArr = (array: any[], subGroupLength: number) => {
const newArray: any[][] = [];
for (let i = 0; i < array.length; i += subGroupLength) {
newArray.push(array.slice(i, i + subGroupLength));
}
return newArray;
};
const reRender = async () => {
await clearDom();
cameraList.value = groupArr(tableData.value.data, param.value.pageSize);
if (cameraList.value.length) {
cameraList.value[param.value.pageNum - 1].forEach((item, index) => {
StructureEZUIKitPlayer(`ezopen://open.ys7.com/${item.deviceSerial}/1.hd.live`, item, index);
});
}
};
const handleRowClick = async (row: any) => {
if (param.value.pageSize === 1) {
for (let i = 0; i < tableData.value.data.length; i++) {
if (tableData.value.data[i].id === row.id) {
param.value.pageNum = i + 1;
break;
}
}
await reRender();
}
};
onMounted(async () => {
await getDeviceToken();
await getCameraList();
});
onBeforeUnmount(() => {
tableData.value.data.forEach((item) => {
if (item.player) {
item.player.destroy().then((data: any) => {
console.log('player destroyed', data);
});
item.player = null;
}
});
});
//监听项目id刷新数据
const listeningProject = watch(
() => currentProject.value.id,
(nid, oid) => {
// queryParams.value.projectId = nid;
// form.value.projectId = nid;
// resetMatrix();
// getList();
}
);
onUnmounted(() => {
listeningProject();
});
</script>
<style lang="scss" scoped>
.wh100 {
width: 100% !important;
height: 87.5vh !important;
}
.system-monitor-list {
.main {
width: 100%;
height: 100%;
.app-container {
width: 100%;
height: 95%;
left: 0;
// background-color: rgb(116, 228, 24);
margin: 0rem auto;
position: relative;
top: 0;
overflow: hidden;
display: flex;
flex-direction: row;
.image_position {
position: absolute;
top: 30%;
left: 40%;
transform: translateX(-40%);
}
.left {
overflow: hidden;
width: 85%;
// border: 0.125rem solid rgb(226, 181, 33);
height: 100%;
// position: absolute;
// left: 0;
// top: 0rem;
.title {
position: absolute;
top: 1rem;
left: 0.75rem;
font-size: 1rem;
font-weight: 600;
color: #000;
}
.time {
position: absolute;
top: 1rem;
right: 1.25rem;
font-size: 1rem;
font-weight: 600;
color: #000;
}
}
.left1 {
width: 85%;
border: 0.125rem solid rgb(37, 43, 102);
height: 100%;
top: 0rem;
position: absolute;
left: 0;
overflow: hidden;
}
.left2 {
top: 0rem;
width: 85%;
border: 0.125rem solid rgb(37, 43, 102);
height: 100%;
position: absolute;
overflow: hidden;
left: 0;
}
.right {
display: flex;
flex-direction: column;
justify-content: space-between;
background: rgb(255, 255, 255);
width: 15%;
height: 100%;
// top: 0;
// // border: .0625rem solid rgb(22, 21, 27);
// position: absolute;
// right: 0;
.right1 {
width: 100%;
text-align: start;
line-height: 2.5rem;
color: #6e727a;
margin: 0.3125rem auto;
height: 2.5rem;
padding: 0 1.25rem;
cursor: pointer;
}
.right1:hover {
background: #1393fc;
color: rgb(255, 255, 255);
border: none;
}
.right2 {
width: 100%;
padding: 0 1.25rem;
height: 2.5rem;
margin: 0.3125rem auto;
line-height: 2.5rem;
background: #1393fc;
color: rgb(255, 255, 255);
cursor: pointer;
}
}
}
.btm-btns {
width: 100%;
height: 5%;
background: #fff;
display: flex;
justify-content: space-between;
.left {
width: 90%;
display: flex;
justify-content: space-between;
> div {
height: 100%;
display: flex;
flex-direction: row;
align-content: center;
align-items: center;
justify-content: space-around;
background: #fff;
padding-left: 1.35rem;
> div {
cursor: pointer;
}
}
}
.right {
width: 10%;
}
}
}
.hello-ezuikit-js {
width: 100%;
height: 100%;
display: flex;
flex-wrap: wrap;
overflow: hidden;
justify-content: space-between;
align-content: space-between;
background: #f5f5f5;
}
.width {
width: 100%;
height: 100%;
}
.width2 {
width: 49.5%;
height: 49%;
}
.width3 {
width: 33%;
height: 32.6%;
}
.width4 {
width: 25%;
height: 25%;
}
.video-active {
// border: 0.125rem solid rgb(255, 133, 62) !important;
}
.rect {
width: 1.625rem;
height: 1.625rem;
}
.video-cover {
display: flex;
justify-content: center;
align-items: center;
font-size: 12px;
color: rgb(153, 0, 0);
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 5;
border-top: 0.0313rem solid #fff;
border-right: 0.0313rem solid #fff;
}
:deep(.el-dialog__wrapper) {
display: flex;
justify-content: center;
align-items: center;
}
:deep(.el-dialog__header) {
background: #efefef;
}
:deep(.el-dialog) {
width: 36rem;
}
:deep(.el-dialog__body) {
padding-top: 3.75rem;
display: flex;
justify-content: center;
}
:deep(.el-form) {
width: 28.125rem;
}
:deep(.el-form-item__label) {
width: 6.875rem !important;
}
:deep(.el-input) {
width: 20rem;
}
.tabs {
width: 100%;
height: 2.4375rem;
display: flex;
position: absolute;
top: 3.375rem;
left: 0;
border-top: 1px solid #f5f5f5;
.tab-item {
width: 50%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.tab-active {
background: #f5f5f5;
color: #fff;
}
}
.wheel {
position: relative;
width: 9.375rem;
height: 9.375rem;
border-radius: 50%;
background: rgb(77, 77, 77);
.camera {
position: absolute;
left: 3.75rem;
top: 3.75rem;
z-index: 5;
width: 1.875rem;
height: 1.875rem;
text-align: center;
line-height: 1.875rem;
font-size: 1.25rem;
color: #fff;
cursor: pointer;
}
.top {
height: 33.3%;
display: flex;
justify-content: center;
align-items: center;
}
.center {
height: 33.3%;
display: flex;
justify-content: space-between;
align-items: center;
.center-left,
.center-right {
width: 33.3%;
display: flex;
justify-content: center;
}
}
.bottom {
height: 33.3%;
display: flex;
justify-content: center;
align-items: center;
}
.triangle {
width: 0;
height: 0;
border: 0.625rem solid transparent;
cursor: pointer;
}
.triangle-top {
border-bottom: 0.9375rem solid #fff;
}
.triangle-bottom {
border-top: 0.9375rem solid #fff;
}
.triangle-left {
border-right: 0.9375rem solid #fff;
}
.triangle-right {
border-left: 0.9375rem solid #fff;
}
}
.bg-black {
display: flex;
justify-content: center;
align-items: center;
background: #000;
color: rgb(151, 0, 0);
font-size: 12px;
}
:deep(.el-button) {
background: rgb(77, 77, 77);
border-color: rgb(77, 77, 77);
}
:deep(.el-button.right-btn) {
width: 11.25rem;
margin: 0 auto 0.625rem;
}
}
</style>

View File

@ -0,0 +1,202 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="摄像头名称" prop="deviceName" :label-width="100">
<el-input v-model="queryParams.deviceName" placeholder="请输入摄像头名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="拍摄时间" prop="createTime">
<el-date-picker clearable v-model="queryParams.createTime" type="date" value-format="YYYY-MM-DD" placeholder="请选择拍摄时间" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['other:ys7DeviceImg:remove']"
>删除</el-button
>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="ys7DeviceImgList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="设备序列号" align="center" prop="deviceSerial" />
<el-table-column label="摄像头名称" align="center" prop="deviceName" />
<el-table-column label="图片地址" align="center" prop="url">
<template #default="scope">
<el-image :z-index="9999" :preview-src-list="[scope.row.url]" preview-teleported :src="scope.row.url" class="w20" />
</template>
</el-table-column>
<el-table-column label="拍摄时间" align="center" prop="createTime" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['other:ys7DeviceImg:remove']">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
</div>
</template>
<script setup name="Ys7DeviceImg" lang="ts">
import { listYs7DeviceImg, getYs7DeviceImg, delYs7DeviceImg, addYs7DeviceImg, updateYs7DeviceImg } from '@/api/other/ys7DeviceImg';
import { Ys7DeviceImgVO, Ys7DeviceImgQuery, Ys7DeviceImgForm } from '@/api/other/ys7DeviceImg/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const ys7DeviceImgList = ref<Ys7DeviceImgVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const ys7DeviceImgFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: Ys7DeviceImgForm = {
id: undefined,
deviceId: undefined,
deviceSerial: undefined,
deviceName: undefined,
url: undefined
};
const data = reactive<PageData<Ys7DeviceImgForm, Ys7DeviceImgQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
deviceSerial: undefined,
deviceName: undefined,
params: {}
},
rules: {
id: [{ required: true, message: '主键id不能为空', trigger: 'blur' }],
deviceId: [{ required: true, message: '设备id不能为空', trigger: 'blur' }],
deviceSerial: [{ required: true, message: '设备序列号不能为空', trigger: 'blur' }],
url: [{ required: true, message: '图片地址不能为空', trigger: 'blur' }]
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询萤石摄像头图片列表 */
const getList = async () => {
loading.value = true;
const res = await listYs7DeviceImg(queryParams.value);
ys7DeviceImgList.value = res.rows;
total.value = res.total;
loading.value = false;
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
ys7DeviceImgFormRef.value?.resetFields();
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: Ys7DeviceImgVO[]) => {
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
};
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = '添加萤石摄像头图片';
};
/** 修改按钮操作 */
const handleUpdate = async (row?: Ys7DeviceImgVO) => {
reset();
const _id = row?.id || ids.value[0];
const res = await getYs7DeviceImg(_id);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = '修改萤石摄像头图片';
};
/** 提交按钮 */
const submitForm = () => {
ys7DeviceImgFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
await updateYs7DeviceImg(form.value).finally(() => (buttonLoading.value = false));
} else {
await addYs7DeviceImg(form.value).finally(() => (buttonLoading.value = false));
}
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
await getList();
}
});
};
/** 删除按钮操作 */
const handleDelete = async (row?: Ys7DeviceImgVO) => {
const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除萤石摄像头图片编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
await delYs7DeviceImg(_ids);
proxy?.$modal.msgSuccess('删除成功');
await getList();
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download(
'other/ys7DeviceImg/export',
{
...queryParams.value
},
`ys7DeviceImg_${new Date().getTime()}.xlsx`
);
};
onMounted(() => {
getList();
});
</script>