Browse Source

摄像机 弹窗 功能 完善, UI 交互修改

wangqin
Joe 11 months ago
parent
commit
e498147220
  1. 5
      package.json
  2. 2
      ruoyi-ui/src/views/JiHeExpressway/components/Teleport.vue
  3. 13
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/AMapContainer/loadAMap.js
  4. 24
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Dialogs/Camera/Descriptions.vue
  5. 16
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Dialogs/Camera/index.vue
  6. 66
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Dialogs/ControlCamera/index.vue
  7. 11
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Dialogs/mixin.js
  8. 56
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/RoadAndEvents/index.vue
  9. 140
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/RoadAndEvents/utils/buttonEvent.js
  10. 60
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/RoadAndEvents/utils/httpList.js
  11. 56
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/RoadAndEvents/utils/map.js
  12. 47
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/RoadAndEvents/utils/videoStream.js
  13. 3
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/index.vue
  14. 3
      ruoyi-ui/src/views/JiHeExpressway/utils/common.js
  15. 52
      ruoyi-ui/src/views/JiHeExpressway/utils/enum.js

5
package.json

@ -0,0 +1,5 @@
{
"dependencies": {
"flv.js": "^1.6.2"
}
}

2
ruoyi-ui/src/views/JiHeExpressway/components/Teleport.vue

@ -21,7 +21,7 @@ export default {
this.queryParentDom.appendChild(this.addDom = this.$el); this.queryParentDom.appendChild(this.addDom = this.$el);
}, },
beforeDestroy() { beforeDestroy() {
if (!this.queryParentDom) return; if (!this.queryParentDom || !this.queryParentDom.contains(this.addDom)) return;
this.queryParentDom.removeChild(this.addDom); this.queryParentDom.removeChild(this.addDom);
}, },

13
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/AMapContainer/loadAMap.js

@ -1,4 +1,5 @@
import AMapLoader from "@amap/amap-jsapi-loader"; import AMapLoader from "@amap/amap-jsapi-loader";
// import { delay } from "@screen/utils/common";
let LoadAMap; let LoadAMap;
@ -6,20 +7,26 @@ export function loadAMap() {
if (LoadAMap) return LoadAMap; if (LoadAMap) return LoadAMap;
try { try {
return AMapLoader.load({ return new Promise(async (resolve, reject) => {
// await delay(1500);
AMapLoader.load({
key: "3b5479d9ad9f01d138fef5e70daed7bd", // 申请好的Web端开发者Key,首次调用 load 时必填 key: "3b5479d9ad9f01d138fef5e70daed7bd", // 申请好的Web端开发者Key,首次调用 load 时必填
// key: "b8053e4abb974f8a4346209ff5af93e9", // 申请好的Web端开发者Key,首次调用 load 时必填 // key: "b8053e4abb974f8a4346209ff5af93e9", // 申请好的Web端开发者Key,首次调用 load 时必填
// version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15 // version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
plugins: [], // 需要使用的的插件列表,如比例尺'AMap.Scale'等 // plugins: ["AMap.MarkerCluster"], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
plugins: ["AMap.MarkerClusterer"], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
}) })
.then((AMap) => { .then((AMap) => {
console.log("地图加载成功"); console.log("地图加载成功");
return (LoadAMap = AMap); resolve((LoadAMap = AMap));
}) })
.catch((e) => { .catch((e) => {
reject();
console.log("地图加载失败", e); console.log("地图加载失败", e);
}); });
});
} catch (error) { } catch (error) {
console.log( console.log(
"%c [ try catch 地图加载失败 ]-27-「loadAMap.js」", "%c [ try catch 地图加载失败 ]-27-「loadAMap.js」",

24
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Dialogs/Camera/Descriptions.vue

@ -3,21 +3,21 @@
<p class="desc-item"> <p class="desc-item">
<span>设备类型</span> <span>设备类型</span>
<span> <span>
球机 {{ cameraType || '-' }}
<img src="@screen/images/dialog/icon-control.png"> <img src="@screen/images/dialog/icon-control.png">
</span> </span>
</p> </p>
<p class="desc-item"> <p class="desc-item">
<span>道路名称</span> <span>道路名称</span>
<span>G35济菏高速</span> <span>{{ data.road || '-' }}</span>
</p> </p>
<p class="desc-item"> <p class="desc-item">
<span>所属机构</span> <span>所属机构</span>
<span>山东高速济南发展公司</span> <span>{{ data.deptName || '-' }}</span>
</p> </p>
<p class="desc-item"> <p class="desc-item">
<span>设备桩号</span> <span>设备桩号</span>
<span>K094+079</span> <span>{{ data.pileNum || '-' }}</span>
</p> </p>
<p class="desc-item"> <p class="desc-item">
<span>上行相机</span> <span>上行相机</span>
@ -35,7 +35,7 @@
</p> </p>
<p class="desc-item singleline"> <p class="desc-item singleline">
<span>设备状态</span> <span>设备状态</span>
<span :style="{ color: '#19E1B1' }">在线</span> <span :style="{ color: statusEnum.color || '#19E1B1' }">{{ statusEnum.text }}</span>
</p> </p>
<p class="desc-item singleline"> <p class="desc-item singleline">
<span>状态更新时间:</span> <span>状态更新时间:</span>
@ -45,8 +45,22 @@
</template> </template>
<script> <script>
import { CameraStatusEnum, CameraControlTypeEnum } from "@screen/utils/enum.js"
export default { export default {
name: 'Descriptions', name: 'Descriptions',
props: {
data: {
type: Object,
default: () => ({})
}
},
data() {
return {
statusEnum: CameraStatusEnum[this.data.status] || {},
cameraType: CameraControlTypeEnum[this.data.ptzCtrl]?.text
}
}
} }
</script> </script>

16
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Dialogs/Camera/index.vue

@ -1,16 +1,16 @@
<template> <template>
<Dialog v-model="obverseVisible" title="摄像机 G35 K094+079 下行(可控)"> <Dialog v-model="obverseVisible" :title="dialogData.camName">
<div class="Camera"> <div class="Camera">
<div class="icon-content"> <div class="icon-content">
<img src="@screen/images/dialog/icon-photo.png" /> <img src="@screen/images/dialog/icon-photo.png" />
<img src="@screen/images/dialog/icon-video.png" /> <img src="@screen/images/dialog/icon-video.png" />
</div> </div>
<div class="video-presentation"></div> <video controls class="video-stream" ref="videoContainerRef" />
<ElTabs v-model="activeName" @tab-click="handleClickTabs" class="tabs"> <ElTabs v-model="activeName" @tab-click="handleClickTabs" class="tabs">
<ElTabPane label="详细设计" name="first"> <ElTabPane label="详细设计" name="first">
<Descriptions /> <Descriptions :data="dialogData" />
</ElTabPane> </ElTabPane>
<ElTabPane label="摄相机参数" name="second">摄相机参数</ElTabPane> <ElTabPane label="摄相机参数" name="second">摄相机参数</ElTabPane>
</ElTabs> </ElTabs>
@ -26,6 +26,7 @@
<script> <script>
import Dialog from "@screen/components/Dialog/index.vue"; import Dialog from "@screen/components/Dialog/index.vue";
import Button from "@screen/components/Buttons/Button.vue" import Button from "@screen/components/Buttons/Button.vue"
import { openVideoStream } from "@screen/pages/Home/components/RoadAndEvents/utils/videoStream.js"
import Descriptions from "./Descriptions.vue" import Descriptions from "./Descriptions.vue"
import { dialogDelayVisible } from "./../mixin" import { dialogDelayVisible } from "./../mixin"
@ -43,6 +44,11 @@ export default {
activeName: 'first' activeName: 'first'
} }
}, },
mounted() {
this.$nextTick(() => {
openVideoStream(this.dialogData.camId, this.$refs.videoContainerRef)
})
},
methods: { methods: {
handleClickTabs() { } handleClickTabs() { }
} }
@ -67,11 +73,11 @@ export default {
} }
} }
.video-presentation { .video-stream {
width: 100%; width: 100%;
height: 216px; height: 216px;
margin-top: 10px; margin-top: 10px;
background: #00ebc1; // background: #00ebc1;
img { img {
width: 100%; width: 100%;

66
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Dialogs/ControlCamera/index.vue

@ -14,13 +14,14 @@
<div class="btn">全屏</div> <div class="btn">全屏</div>
</div> </div>
</div> </div>
<video controls class="videoStream" ref="videoContainerRef" />
</div> </div>
<div class="content"> <div class="content">
<div class="left"> <div class="left">
<p class="desc-item"> <p class="desc-item">
<span>设备名称: </span> <span>设备名称: </span>
<span>疲劳唤醒设备1</span> <span>{{ dialogData.camName || '-' }}</span>
</p> </p>
<p class="desc-item"> <p class="desc-item">
<span>设备编号: </span> <span>设备编号: </span>
@ -29,42 +30,42 @@
<div> <div>
<p class="desc-item"> <p class="desc-item">
<span>设备桩号: </span> <span>设备桩号: </span>
<span>k097+900</span> <span>{{ dialogData.pileNum || '-' }}</span>
</p> </p>
<p class="desc-item"> <p class="desc-item">
<span>方向: </span> <span>方向: </span>
<span class="color1">菏泽</span> <span class="color1">{{ cameraDirection || '-' }}</span>
</p> </p>
</div> </div>
<p class="desc-item"> <p class="desc-item">
<span>/纬度: </span> <span>/纬度: </span>
<span>117.071152 / 35.910659</span> <span>{{ dialogData.camLong || '-' }} / {{ dialogData.camLat || '-' }}</span>
</p> </p>
<p class="desc-item"> <p class="desc-item">
<span>道路名称: </span> <span>道路名称: </span>
<span>G35济菏告诉</span> <span>{{ dialogData.road }}</span>
</p> </p>
<p class="desc-item"> <p class="desc-item">
<span>道路名称: </span> <span>设备状态: </span>
<span class="color1">正常</span> <span class="color1">{{ statusEnum.text || '-' }}</span>
</p> </p>
</div> </div>
<div class="dash-line"></div> <div class="dash-line"></div>
<div class="right"> <div class="right">
<div class="direction"> <div class="direction">
<img class="top" src="./images/top.svg"> <img class="top" src="./images/top.svg" @click="controlClick(21)">
<img class="right" src="./images/right.svg"> <img class="right" src="./images/right.svg" @click="controlClick(24)">
<div class="center"></div> <div class="center"></div>
<img class="bottom" src="./images/bottom.svg"> <img class="bottom" src="./images/bottom.svg" @click="controlClick(22)">
<img class="left" src="./images/left.svg"> <img class="left" src="./images/left.svg" @click="controlClick(23)">
</div> </div>
<div class="options"> <div class="options">
<div v-for="item in options" :key="item.key"> <div v-for="item in options" :key="item.key">
<img src="./images/sub.svg"> <img src="./images/sub.svg" @click="controlClick(item.sub)">
<span>{{ item.label }}</span> <span>{{ item.label }}</span>
<img src="./images/add.svg"> <img src="./images/add.svg" @click="controlClick(item.add)">
</div> </div>
</div> </div>
</div> </div>
@ -81,6 +82,10 @@
import Dialog from "@screen/components/Dialog/index.vue" import Dialog from "@screen/components/Dialog/index.vue"
import Button from "@screen/components/Buttons/Button.vue" import Button from "@screen/components/Buttons/Button.vue"
import { dialogDelayVisible } from "./../mixin" import { dialogDelayVisible } from "./../mixin"
import { CameraStatusEnum, CameraDirectionEnum } from "@screen/utils/enum.js"
import { controlCamera } from "@screen/pages/Home/components/RoadAndEvents/utils/httpList.js"
import { throttle } from "lodash"
import { openVideoStream } from "@screen/pages/Home/components/RoadAndEvents/utils/videoStream.js"
export default { export default {
name: 'ControlCamera', name: 'ControlCamera',
@ -91,22 +96,40 @@ export default {
}, },
data() { data() {
return { return {
statusEnum: CameraStatusEnum[this.dialogData.status] || {},
cameraDirection: CameraDirectionEnum[this.dialogData.camOrientation]?.text,
options: [ options: [
{ {
label: "变倍", label: "变倍",
key: "zoom" key: "zoom",
add: "12",
sub: "11"
}, },
{ {
label: "光圈", label: "光圈",
key: "aperture" key: "aperture",
add: "15",
sub: "16"
}, },
{ {
label: "聚焦", label: "聚焦",
key: "focus" key: "focus",
add: "13",
sub: "14"
}, },
] ]
} }
}, },
mounted() {
this.$nextTick(() => {
openVideoStream(this.dialogData.camId, this.$refs.videoContainerRef)
})
},
methods: {
controlClick: throttle(function (type) {
controlCamera(this.dialogData.camId, type)
}, 360)
}
} }
</script> </script>
@ -126,9 +149,18 @@ export default {
.camera-video { .camera-video {
position: relative; position: relative;
flex: 1; flex: 1;
background-color: #19E1B1; // background-color: #19E1B1;
.videoStream {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
.control-btns { .control-btns {
z-index: 1;
position: absolute; position: absolute;
padding: 6px 9px; padding: 6px 9px;
width: 100%; width: 100%;

11
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Dialogs/mixin.js

@ -1,9 +1,10 @@
export const dialogDelayVisible = { export const dialogDelayVisible = {
// props: { props: {
// visible: { dialogData: {
// default: false, type: Object,
// }, default: () => ({}),
// }, },
},
data() { data() {
return { return {
visibleData: false, visibleData: false,

56
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/RoadAndEvents/index.vue

@ -11,12 +11,8 @@
<TransitionGroup name="fade-group" tag="div" class="tabs-content"> <TransitionGroup name="fade-group" tag="div" class="tabs-content">
<div class="device-item" v-for="(item, index) in tabContentData" :key="item.id" @click="handleDevice(item)" <div class="device-item" v-for="(item, index) in tabContentData" :key="item.id" @click="handleDevice(item)"
:style="getStart(index)"> :style="getStart(index)">
<Transition name="fade"> <div class="device-icon"
<img v-if="!item.status" class="device-icon" key="normal" :style="{ backgroundImage: `url(${require(`@screen/images/layer/${active}/${item.title}${item.status}.svg`)})` }" />
:src="require(`@screen/images/layer/${active}/${item.title}.svg`)" />
<img v-else class="device-icon" key="fault"
:src="require(`@screen/images/layer/${active}/${item.title}${item.status}.svg`)" />
</Transition>
<span>{{ item.title }}</span> <span>{{ item.title }}</span>
</div> </div>
</TransitionGroup> </TransitionGroup>
@ -32,14 +28,14 @@
<!-- <ControlCamera :data="cameraDialogConfig.data" :visible="cameraDialogConfig.visibleType === 0" /> --> <!-- <ControlCamera :data="cameraDialogConfig.data" :visible="cameraDialogConfig.visibleType === 0" /> -->
<!-- 摄像机 G35 K094+079 下行可控 枪机 可打开--> <!-- 摄像机 G35 K094+079 下行可控 枪机 可打开-->
<!-- <Camera :data="cameraDialogConfig.data" :visible="cameraDialogConfig.visibleType === 1" /> --> <!-- <Camera :data="cameraDialogConfig.data" :visible="cameraDialogConfig.visibleType === 1" /> -->
<component :data="cameraDialogConfig.data" :is="cameraDialogConfig.component" @change="handleCameraChange" /> <component :dialogData="cameraDialogConfig.data" :is="cameraDialogConfig.component" @change="handleCameraChange" />
</div> </div>
</template> </template>
<script> <script>
import { getLayerData } from "./utils/layerImages"; import { getLayerData } from "./utils/layerImages";
import { debounce } from "lodash"; import { debounce } from "lodash";
import { eventMap } from "./utils/buttonEvent"; import { eventMap, cacheRemoveFunc } from "./utils/buttonEvent";
import ControlCamera from "./../Dialogs/ControlCamera/index.vue" import ControlCamera from "./../Dialogs/ControlCamera/index.vue"
import Camera from "./../Dialogs/Camera/index.vue"; import Camera from "./../Dialogs/Camera/index.vue";
@ -58,15 +54,19 @@ export default {
// - // -
cameraDialogConfig: { cameraDialogConfig: {
// 0 ControlCamera | 1 Camera // 0 ControlCamera | 1 Camera
component: null, component: void 0,
data: null data: void 0,
// component: ControlCamera,
// data: {
// camId: "57937",
// }
} }
} }
}, },
inject: ['getMap'], inject: ['getMap'],
created() { created() {
const defaultActive = 2; const defaultActive = 0;
const layerData = getLayerData(); const layerData = getLayerData();
this.layerData = layerData; this.layerData = layerData;
@ -94,6 +94,10 @@ export default {
if (!mapIns) return; if (!mapIns) return;
for (const key in cacheRemoveFunc) {
cacheRemoveFunc[key]?.();
}
// mapIns.clearMap(); // mapIns.clearMap();
mapIns.getLayers().forEach((layer, index) => index > 1 && mapIns.remove(layer)) mapIns.getLayers().forEach((layer, index) => index > 1 && mapIns.remove(layer))
}, },
@ -121,7 +125,7 @@ export default {
<style lang='scss' scoped> <style lang='scss' scoped>
.fade-enter-active, .fade-enter-active,
.fade-leave-active { .fade-leave-active {
transition: opacity .36s; transition: opacity .24s;
} }
.fade-enter, .fade-enter,
@ -131,7 +135,7 @@ export default {
.fade-group-enter-active, .fade-group-enter-active,
.fade-group-leave-active { .fade-group-leave-active {
transition: all 0.36s ease; transition: all 0.24s ease;
} }
.fade-group-enter-from, .fade-group-enter-from,
@ -142,6 +146,7 @@ export default {
.fade-group-leave-active { .fade-group-leave-active {
// transform: translateY(24px); // transform: translateY(24px);
position: absolute !important; position: absolute !important;
overflow: hidden;
} }
.RoadAndEvents { .RoadAndEvents {
@ -155,6 +160,7 @@ export default {
padding: 24px 18px; padding: 24px 18px;
padding-bottom: 15px; padding-bottom: 15px;
overflow: hidden; overflow: hidden;
max-height: min-content;
.tabs-content { .tabs-content {
margin-top: 15px; margin-top: 15px;
@ -177,14 +183,30 @@ export default {
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
--height-svg: 24px; --height-svg: 24px;
--gap: 9px; --gap: 12px;
padding-top: calc(var(--height-svg) + var(--gap)); padding-top: calc(var(--height-svg) + var(--gap));
.device-icon { .device-icon {
display: block; display: flex;
align-items: center;
justify-content: center;
position: absolute; position: absolute;
top: 0; top: 0;
height: 24px; height: 33px;
width: 100%;
background-repeat: no-repeat;
background-size: auto;
background-position: center;
transition: all .18s linear;
&::before {
content: "";
position: absolute;
width: 33px;
height: 33px;
border: 1px solid rgba(0, 209, 255, .5);
border-radius: 50%;
}
} }
span { span {

140
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/RoadAndEvents/utils/buttonEvent.js

@ -1,14 +1,18 @@
import { axiosIns } from "@screen/utils/axios/auth.js"; import { axiosIns } from "@screen/utils/axios/auth.js";
import { Message } from "element-ui"; import { Message } from "element-ui";
const PilePointJSON = require(`@screen/pages/Home/components/AMapContainer/data/lcz.json`);
import { setMarkerCluster } from "./map";
// 0 有 可控(球机)ControlCamera | 1 ⽆ 不可控(枪机)Camera // 0 有 可控(球机)ControlCamera | 1 ⽆ 不可控(枪机)Camera
const cameraCtrlMap = { const cameraCtrlMap = {
0: "ControlCamera", 0: "ControlCamera",
1: "Camera", 1: "Camera",
}; };
const cacheData = { export const cacheRemoveFunc = {
DriveTestEquipment_Camera: null, DriveTestEquipment_Camera_Remove: null,
}; };
export const eventMap = { export const eventMap = {
@ -18,15 +22,32 @@ export const eventMap = {
if (!mapIns) return Message.error("地图加载失败!"); if (!mapIns) return Message.error("地图加载失败!");
// const { code, data } = await axiosIns // {
// .get("/system/camera/camList") // camId: "57937",
// // .get("/system/dept/camTreeselect") // camLat: "0",
// .catch(() => ({})); // camLong: "0",
const { code, data } = { code: 200 }; // camName: "G35 K055+565 (可控)",
// camOrientation: "2",
// camStatus: "0",
// camType: "1",
// deptId: "1751",
// deptName: "G35济菏改扩建",
// firmType: "2",
// pileNum: "K055+565",
// ptzCtrl: "0",
// road: "G35",
// status: "0",
// };
const { code, data } = await axiosIns
.get("/system/camera/camList")
// .get("/system/dept/camTreeselect")
.catch(() => ({}));
// const { code, data } = { code: 200 };
if (code != 200) return Message.error("摄像机加载失败!"); if (code != 200) return Message.error("摄像机加载失败!");
// cacheData.DriveTestEquipment_Camera?.clear(); cacheRemoveFunc.DriveTestEquipment_Camera_Remove?.();
const normal = require(`@screen/images/layer${item.id.replace(".", "")}`); const normal = require(`@screen/images/layer${item.id.replace(".", "")}`);
const fault = require(`@screen/images/layer${item.id.replace( const fault = require(`@screen/images/layer${item.id.replace(
@ -34,85 +55,52 @@ export const eventMap = {
(data) => (data === "." ? "" : `${data}_fault`) (data) => (data === "." ? "" : `${data}_fault`)
)}`); )}`);
const size = 42; const markers = [];
const iconOption = { const markerClick = (e) => {
type: "image", const {
size: [size, size], target: {
anchor: "center", w: { extData },
};
const layer =
cacheData.DriveTestEquipment_Camera ||
(cacheData.DriveTestEquipment_Camera = new AMap.LabelsLayer({
zooms: [3, 20],
zIndex: 1000,
collision: false,
}));
mapIns.add(layer);
const fitViewOverlays = [];
[
{
camLong: 116.471814,
camLat: 39.995856,
status: 0,
camName: "名称1",
camId: "666",
ptzCtrl: 0,
},
{
camLong: 116.456474,
camLat: 39.991563,
status: 1,
camName: "名称2",
camId: "333",
ptzCtrl: 1,
}, },
].forEach((item) => { } = e;
iconOption.image = item.status ? fault : normal;
const labelMarker = new AMap.LabelMarker({
position: [item.camLong, item.camLat],
icon: { ...iconOption },
name: item.camId,
zooms: [3, 20],
zIndex: 36,
opacity: 1,
text: {
content: item.camName,
direction: "right",
offset: [(size / 2 + 18) * -1, (size / 2 + 12) * -1],
style: {
fontSize: 12,
fontWeight: "normal",
fillColor: "#fff",
strokeColor: "#fff",
strokeWidth: 1,
fold: true,
padding: "3, 6",
},
},
});
fitViewOverlays.push(labelMarker);
labelMarker.on("click", (e) => {
this.cameraDialogConfig = { this.cameraDialogConfig = {
// 0 有(球机) 1 ⽆(枪机) // 0 有(球机) 1 ⽆(枪机)
component: cameraCtrlMap[item.ptzCtrl], component: cameraCtrlMap[extData.ptzCtrl],
data: item, data: extData,
}; };
console.log(this.cameraDialogConfig);
};
data.forEach((item) => {
const { lng, lat } = PilePointJSON[item.pileNum] || {};
if (!lat || !lng) return;
const marker = new AMap.Marker({
position: [lng, lat],
content: `<img style="width: 36px; height: 36px" src='${
item.status !== "0" ? fault : normal
}'>`,
offset: new AMap.Pixel(-15, -15),
extData: item,
clickable: true,
}); });
layer.add(labelMarker); marker.on("click", markerClick);
markers.push(marker);
}); });
mapIns.setFitView(fitViewOverlays, false, [360, 360, 360, 360]); const markerCluster = await setMarkerCluster(mapIns, markers);
cacheRemoveFunc.DriveTestEquipment_Camera_Remove = () =>
markerCluster.clearMarkers();
mapIns.setFitView(markers, false, [360, 360, 360, 360]);
}, },
async "路测设备/摄像机_close"() { async "路测设备/摄像机_close"() {
cacheData.DriveTestEquipment_Camera?.clear(); cacheRemoveFunc.DriveTestEquipment_Camera_Remove?.();
}, },
}; };

60
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/RoadAndEvents/utils/httpList.js

@ -0,0 +1,60 @@
import { Message } from "element-ui";
import { axiosIns } from "@screen/utils/axios/auth.js";
/**
*
* @param {string} camId 相机 ID
* @param {1 | 2 | 3} media 参数说明: 1: http+flv; 2: rtmp; 3: hls
* @returns
*/
export function getCameraStream(camId, media = 1) {
return axiosIns.post("/videoInfo/api/externalVideoStreaming", {
type: 1,
camId,
media,
});
}
/**
* 球机进行控制
* @param {string} camId 相机 ID
* @param {number} cmdType 12 /焦距变(倍率变)/
11 /焦距变(倍率变)/
13 /焦点前调/
14 /焦点后调/
15 /光圈扩/
16 /光圈缩/
21 /云台向上/
22 /云台向下/
23 /云台左转/
24 /云台右转/
50 /云台左上转/
51 /云台左下转/
52 /云台右上转/
53 /云台右下转/
8 /设置预置位/
39 /预置位/
49 /辅助开关开/
48 /辅助开关关/
51 /设置巡航起始点/
52 /设置巡航结束点/
53 /开始巡航/
54 /巡航/
(-1) //
99 /向动作停/
* @returns
*/
export function controlCamera(camId, cmdType) {
return axiosIns
.post("/videoInfo/api/PTZControl", {
msgType: 3,
camId,
cmdType,
})
.then((result) => {
if (result.code != 200) Message.error(`相机操作失败!`);
})
.catch((err) => {
Message.error(`相机操作失败!`);
});
}

56
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/RoadAndEvents/utils/map.js

@ -0,0 +1,56 @@
import { loadAMap } from "@screen/pages/Home/components/AMapContainer/loadAMap.js";
/**
* @typedef {Object} Point
* @property {number} weight - The weight of the item.
* @property {number[]} lngLat - The longitude and latitude coordinates.
* @property {string} name - The name of the item.
* @property {string} icon - The icon of the item.
* @property {Object} extData - The icon of the item.
* @property {*} [x] - Additional properties of the item.
*/
/**
* https://lbs.amap.com/api/javascript-api-v2/documentation#markercluster 2.x
* https://lbs.amap.com/demo/javascript-api/example/marker/markerclusterer 1.x
* @param {*} map
* @param {Point[]} points
* @param {Marker[]} markers
* @param {*} options
* @returns
*/
export async function setMarkerCluster(map, markers, options) {
const AMap = await loadAMap();
return new AMap.MarkerClusterer(map, markers, {
// gridSize: 15,
maxZoom: 15,
...options,
// 自定义聚合点样式
renderClusterMarker(context) {
// 聚合中点个数
const clusterCount = context.count;
const div = document.createElement("div");
let bgColor = "204,235,197";
div.style.backgroundColor = `rgba(${bgColor}, .5)`;
const size = Math.round(
25 + Math.pow(clusterCount / markers.length, 1 / 5) * 36
);
div.style.borderRadius =
div.style.lineHeight =
div.style.width =
div.style.height =
`${size}px`;
div.style.border = `solid 1px rgba(${bgColor}, 1)`;
div.innerHTML = context.count;
div.style.color = "#ffffff";
div.style.fontSize = "24px";
div.style.textAlign = "center";
context.marker.setOffset(new AMap.Pixel(-size / 2, -size / 2));
context.marker.setContent(div);
},
});
}

47
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/RoadAndEvents/utils/videoStream.js

@ -0,0 +1,47 @@
import flvJs from "flv.js";
import { getCameraStream } from "./httpList";
/**
*
* @param {string} camId 相机ID
* @param {HTMLElement} container 容器
* @param {DPlayerOptions?} options 配置项
* @returns
*/
export async function openVideoStream(camId, container) {
const { code, data } = await getCameraStream(camId).catch(() => ({}));
if (code != 200) return;
const flvPlayer = flvJs.createPlayer({
type: "flv",
url: data.liveUrl,
isLive: true,
hasVideo: true,
hasAudio: true,
});
console.log(
"%c [ flvPlayer ]-26-「videoStream.js」",
"font-size:15px; background:#b2b540; color:#f6f984;",
flvPlayer
);
flvPlayer.attachMediaElement(container);
flvPlayer.load();
flvPlayer.play();
return flvPlayer;
// return new DPlayer({
// container,
// autoplay: true,
// ...options,
// hotkey: false,
// video: {
// url: data.liveUrl,
// type: "hls",
// // type: "flv",
// },
// });
}

3
ruoyi-ui/src/views/JiHeExpressway/pages/Home/index.vue

@ -97,6 +97,8 @@ export default {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 24px; gap: 24px;
overflow: hidden;
height: 100%;
>div { >div {
pointer-events: all; pointer-events: all;
@ -105,6 +107,7 @@ export default {
.content-l-b { .content-l-b {
height: 332px; height: 332px;
max-height: 332px;
flex: 0.81; flex: 0.81;
} }
} }

3
ruoyi-ui/src/views/JiHeExpressway/utils/common.js

@ -0,0 +1,3 @@
export function delay(ms = 240) {
return new Promise((resolve) => setTimeout(() => resolve(void 0), ms));
}

52
ruoyi-ui/src/views/JiHeExpressway/utils/enum.js

@ -0,0 +1,52 @@
/**
* color: 颜色
* text: 文字
* icon: 展示的图标
*/
// 相机的状态
export const CameraStatusEnum = {
"-1": {
color: "",
text: "未启用",
},
0: {
color: "",
text: "正常",
},
1: {
color: "",
text: "网络中断",
},
2: {
color: "",
text: "网络正常无图像",
},
3: {
color: "",
text: "有图像, 图像存在问题",
},
};
// 摄像机方向
export const CameraDirectionEnum = {
0: {
text: "上行",
},
1: {
text: "下行",
},
2: {
text: "上下行 (双向)",
},
};
// 摄像 是否可控 是否有云台控制 0 有(球机) 1 ⽆(枪机)
export const CameraControlTypeEnum = {
0: {
text: "球机",
},
1: {
text: "枪机",
},
};
Loading…
Cancel
Save