parent
f70b2905dd
commit
9ec6f41e6f
|
@ -13,6 +13,7 @@
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
"echarts": "^5.5.0",
|
"echarts": "^5.5.0",
|
||||||
"element-plus": "^2.6.2",
|
"element-plus": "^2.6.2",
|
||||||
|
"hls.js": "^1.6.5",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"mockjs": "^1.1.0",
|
"mockjs": "^1.1.0",
|
||||||
|
@ -2892,6 +2893,11 @@
|
||||||
"he": "bin/he"
|
"he": "bin/he"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/hls.js": {
|
||||||
|
"version": "1.6.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.6.5.tgz",
|
||||||
|
"integrity": "sha512-KMn5n7JBK+olC342740hDPHnGWfE8FiHtGMOdJPfUjRdARTWj9OB+8c13fnsf9sk1VtpuU2fKSgUjHvg4rNbzQ=="
|
||||||
|
},
|
||||||
"node_modules/hosted-git-info": {
|
"node_modules/hosted-git-info": {
|
||||||
"version": "2.8.9",
|
"version": "2.8.9",
|
||||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
|
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
"echarts": "^5.5.0",
|
"echarts": "^5.5.0",
|
||||||
"element-plus": "^2.6.2",
|
"element-plus": "^2.6.2",
|
||||||
|
"hls.js": "^1.6.5",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"mockjs": "^1.1.0",
|
"mockjs": "^1.1.0",
|
||||||
|
|
|
@ -86,3 +86,23 @@ export const todayHourly=(param:any={})=>{
|
||||||
export const trafficTrend=(param:any={})=>{
|
export const trafficTrend=(param:any={})=>{
|
||||||
return GET('/device/trafficFlow/trafficTrend',param)
|
return GET('/device/trafficFlow/trafficTrend',param)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**事件历史记录管理 历史数据 */
|
||||||
|
export const meventListHistory=(param:any={})=>{
|
||||||
|
return GET('/device/mevent/listHistory',param)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**数据字典-根据类型获取字典数据 */
|
||||||
|
export const getDictData = (dictType: string) => {
|
||||||
|
return GET(`/system/dict/data/type/${dictType}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**视频监控设备列表*/
|
||||||
|
export const getVideoDeviceList = (param: any = {}) => {
|
||||||
|
return POST('/video/device/list', param);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**视频流 */
|
||||||
|
export const videoStream=(param:any={})=>{
|
||||||
|
return GET('/video/stream',param)
|
||||||
|
}
|
||||||
|
|
|
@ -1,52 +1,79 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from "vue";
|
import { ref, computed, onMounted } from "vue";
|
||||||
|
import { meventListHistory } from "@/api/modules/index";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
interface EventItem {
|
interface EventItem {
|
||||||
|
type: string;
|
||||||
location: string;
|
location: string;
|
||||||
time: string;
|
time: string;
|
||||||
status: "处置中" | "待处置";
|
status: "处置中" | "待处置";
|
||||||
}
|
}
|
||||||
|
|
||||||
// 模拟数据
|
const allEvents = ref<EventItem[]>([
|
||||||
const wrongWayEvents = ref<EventItem[]>([
|
// {
|
||||||
{
|
// type: "车辆逆行",
|
||||||
location: "邯郸方向 K34+230",
|
// location: "邯郸方向 K34+230",
|
||||||
time: "2025-4-15 12:34:56",
|
// time: "2025-4-15 12:34:56",
|
||||||
status: "处置中",
|
// status: "处置中",
|
||||||
},
|
// },
|
||||||
|
// {
|
||||||
|
// type: "道路施工",
|
||||||
|
// location: "邯郸方向 K34+230",
|
||||||
|
// time: "2025-4-15 12:34:56",
|
||||||
|
// status: "待处置",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// type: "车辆逆行",
|
||||||
|
// location: "邯郸方向 K34+230",
|
||||||
|
// time: "2025-4-15 12:34:56",
|
||||||
|
// status: "待处置",
|
||||||
|
// },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const roadWorkEvents = ref<EventItem[]>([
|
// const wrongWayEvents = computed(() =>
|
||||||
{
|
// allEvents.value.filter((e) => e.type === "车辆逆行")
|
||||||
location: "邯郸方向 K34+230",
|
// );
|
||||||
time: "2025-4-15 12:34:56",
|
// const roadWorkEvents = computed(() =>
|
||||||
status: "待处置",
|
// allEvents.value.filter((e) => e.type === "道路施工")
|
||||||
},
|
// );
|
||||||
]);
|
|
||||||
|
|
||||||
const formatTime = (time: string) => {
|
const formatTime = (time: string) => {
|
||||||
return dayjs(time).format("YYYY-MM-DD HH:mm:ss");
|
return dayjs(time).format("YYYY-MM-DD HH:mm:ss");
|
||||||
};
|
};
|
||||||
|
onMounted(()=>{
|
||||||
|
const endTime = dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
const startTime = dayjs().subtract(6, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
meventListHistory({startTime, endTime}).then((res)=>{
|
||||||
|
console.log(res, 'rrreeesss')
|
||||||
|
allEvents.value = res.rows.map((item: any) => ({
|
||||||
|
type: item.eventType,
|
||||||
|
location: (item.direction === '1' ? '黄骅港方向 ' : item.direction === '2' ? '邯郸方向 ' : '') + item.pilenum,
|
||||||
|
time: item.time,
|
||||||
|
status: item.status == '0' ? '待处置' : '处置中',
|
||||||
|
}));
|
||||||
|
})
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="event-task">
|
<div class="event-task">
|
||||||
<div class="event-section">
|
<div v-for="(event, index) in allEvents"
|
||||||
|
:key="index" class="event-section">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<div style="color: #fbfbfc">车辆逆行</div>
|
<div style="color: #fbfbfc">{{event.type}}</div>
|
||||||
<div
|
<div
|
||||||
v-if="wrongWayEvents.length"
|
|
||||||
class="event-status"
|
class="event-status"
|
||||||
:class="{ processing: wrongWayEvents[0].status === '处置中' }"
|
:class="{
|
||||||
|
processing: event.status === '处置中',
|
||||||
|
pending: event.status === '待处置'
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
{{ wrongWayEvents[0].status }}
|
{{ event.status }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="event-list" v-if="wrongWayEvents.length">
|
<div class="event-list">
|
||||||
<div
|
<div
|
||||||
v-for="(event, index) in wrongWayEvents"
|
|
||||||
:key="index"
|
|
||||||
class="event-item"
|
class="event-item"
|
||||||
>
|
>
|
||||||
<div class="event-info">
|
<div class="event-info">
|
||||||
|
@ -55,10 +82,10 @@ const formatTime = (time: string) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="empty-state">暂无逆行事件</div>
|
<!-- <div v-else class="empty-state">暂无逆行事件</div> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="event-section">
|
<!-- <div class="event-section">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<div style="color: #fbfbfc">道路施工</div>
|
<div style="color: #fbfbfc">道路施工</div>
|
||||||
<div
|
<div
|
||||||
|
@ -82,7 +109,7 @@ const formatTime = (time: string) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="empty-state">暂无施工事件</div>
|
<div v-else class="empty-state">暂无施工事件</div>
|
||||||
</div>
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -96,7 +123,22 @@ const formatTime = (time: string) => {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
overflow-y: scroll;
|
||||||
|
z-index: 9999;
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background: linear-gradient(180deg, #00c6ff 0%, #085b9f 100%);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background: rgba(0, 198, 255, 0.08);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
.event-section {
|
.event-section {
|
||||||
height: calc(50% - 10px);
|
height: calc(50% - 10px);
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
|
@ -112,7 +154,7 @@ const formatTime = (time: string) => {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
// overflow: hidden;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
content: "";
|
content: "";
|
||||||
|
@ -129,20 +171,20 @@ const formatTime = (time: string) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&::after {
|
// &::after {
|
||||||
content: "";
|
// content: "";
|
||||||
position: absolute;
|
// position: absolute;
|
||||||
bottom: 0;
|
// bottom: 0;
|
||||||
left: 0;
|
// left: 0;
|
||||||
right: 0;
|
// right: 0;
|
||||||
height: 1px;
|
// height: 1px;
|
||||||
background: linear-gradient(
|
// background: linear-gradient(
|
||||||
90deg,
|
// 90deg,
|
||||||
transparent,
|
// transparent,
|
||||||
rgba(0, 198, 255, 0.3),
|
// rgba(0, 198, 255, 0.3),
|
||||||
transparent
|
// transparent
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
.section-header {
|
.section-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -151,22 +193,22 @@ const formatTime = (time: string) => {
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
padding-bottom: 12px;
|
padding-bottom: 12px;
|
||||||
border-bottom: 1px solid rgba(0, 198, 255, 0.1);
|
border: 1px solid rgba(0, 198, 255, 0.1);
|
||||||
position: relative;
|
// position: relative;
|
||||||
background-color: #0e2d51;
|
background-color: #0e2d51;
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
padding: 14px;
|
padding: 14px;
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
|
|
||||||
&::after {
|
// &::after {
|
||||||
content: "";
|
// content: "";
|
||||||
position: absolute;
|
// position: absolute;
|
||||||
bottom: -1px;
|
// bottom: -1px;
|
||||||
left: 0;
|
// left: 0;
|
||||||
width: 100px;
|
// width: 100px;
|
||||||
height: 1px;
|
// height: 1px;
|
||||||
background: linear-gradient(90deg, #00c6ff, transparent);
|
// background: linear-gradient(90deg, #00c6ff, transparent);
|
||||||
}
|
// }
|
||||||
|
|
||||||
i {
|
i {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
|
@ -229,7 +271,7 @@ const formatTime = (time: string) => {
|
||||||
|
|
||||||
.event-list {
|
.event-list {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
//overflow-y: auto;
|
||||||
padding-right: 4px;
|
padding-right: 4px;
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
|
@ -258,36 +300,36 @@ const formatTime = (time: string) => {
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
&::before {
|
// &::before {
|
||||||
content: "";
|
// content: "";
|
||||||
position: absolute;
|
// position: absolute;
|
||||||
top: 0;
|
// top: 0;
|
||||||
left: 0;
|
// left: 0;
|
||||||
right: 0;
|
// right: 0;
|
||||||
height: 1px;
|
// height: 1px;
|
||||||
background: linear-gradient(
|
// background: linear-gradient(
|
||||||
90deg,
|
// 90deg,
|
||||||
transparent,
|
// transparent,
|
||||||
rgba(0, 198, 255, 0.2),
|
// rgba(0, 198, 255, 0.2),
|
||||||
transparent
|
// transparent
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
&:hover {
|
// &:hover {
|
||||||
background: rgba(4, 49, 128, 0.3);
|
// background: rgba(4, 49, 128, 0.3);
|
||||||
border-color: rgba(0, 198, 255, 0.3);
|
// border-color: rgba(0, 198, 255, 0.3);
|
||||||
transform: translateY(-2px);
|
// transform: translateY(-2px);
|
||||||
box-shadow: 0 4px 20px rgba(0, 198, 255, 0.15);
|
// box-shadow: 0 4px 20px rgba(0, 198, 255, 0.15);
|
||||||
|
|
||||||
&::before {
|
// &::before {
|
||||||
background: linear-gradient(
|
// background: linear-gradient(
|
||||||
90deg,
|
// 90deg,
|
||||||
transparent,
|
// transparent,
|
||||||
rgba(0, 198, 255, 0.4),
|
// rgba(0, 198, 255, 0.4),
|
||||||
transparent
|
// transparent
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
.event-info {
|
.event-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import { ref, onMounted, nextTick } from "vue";
|
||||||
import demoImage from "@/assets/img/demo.png";
|
import demoImage from "@/assets/img/demo.png";
|
||||||
|
import { getVideoDeviceList, videoStream } from "@/api/modules/index";
|
||||||
|
import Hls from "hls.js";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
const videoUrl = ref(""); // 保存m3u8地址
|
||||||
|
const videoRef = ref<HTMLVideoElement | null>(null);
|
||||||
// 设备状态数据
|
// 设备状态数据
|
||||||
const deviceStatus = ref({
|
const deviceStatus = ref({
|
||||||
normal: 248,
|
normal: 248,
|
||||||
|
@ -10,49 +15,50 @@ const deviceStatus = ref({
|
||||||
|
|
||||||
// 监控点分组数据
|
// 监控点分组数据
|
||||||
const monitoringGroups = ref([
|
const monitoringGroups = ref([
|
||||||
{
|
// {
|
||||||
id: 1,
|
// id: 1,
|
||||||
name: "邯港方向",
|
// name: "邯港方向",
|
||||||
count: 5,
|
// count: 5,
|
||||||
expanded: true,
|
// expanded: true,
|
||||||
points: [
|
// points: [
|
||||||
{ id: 1, name: "邯港方向监控001", status: "正常" },
|
// { id: 1, name: "邯港方向监控001", status: "正常" },
|
||||||
{ id: 2, name: "邯港方向监控002", status: "正常" },
|
// { id: 2, name: "邯港方向监控002", status: "正常" },
|
||||||
{ id: 3, name: "邯港方向监控003", status: "正常" },
|
// { id: 3, name: "邯港方向监控003", status: "正常" },
|
||||||
{ id: 4, name: "邯港方向监控004", status: "正常" },
|
// { id: 4, name: "邯港方向监控004", status: "正常" },
|
||||||
{ id: 5, name: "邯港方向监控005", status: "正常" },
|
// { id: 5, name: "邯港方向监控005", status: "正常" },
|
||||||
],
|
// ],
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
id: 2,
|
// id: 2,
|
||||||
name: "黄驿港方向",
|
// name: "黄驿港方向",
|
||||||
count: 12,
|
// count: 12,
|
||||||
expanded: false,
|
// expanded: false,
|
||||||
points: [],
|
// points: [],
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
id: 3,
|
// id: 3,
|
||||||
name: "收费站",
|
// name: "收费站",
|
||||||
count: 5,
|
// count: 5,
|
||||||
expanded: true,
|
// expanded: true,
|
||||||
points: [
|
// points: [
|
||||||
{ id: 6, name: "收费站监控001", status: "正常" },
|
// { id: 6, name: "收费站监控001", status: "正常" },
|
||||||
{ id: 7, name: "收费站监控002", status: "正常" },
|
// { id: 7, name: "收费站监控002", status: "正常" },
|
||||||
{ id: 8, name: "收费站监控003", status: "正常" },
|
// { id: 8, name: "收费站监控003", status: "正常" },
|
||||||
{ id: 9, name: "收费站监控004", status: "正常" },
|
// { id: 9, name: "收费站监控004", status: "正常" },
|
||||||
],
|
// ],
|
||||||
},
|
// },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 固定的六个道路监控数据
|
// // 固定的六个道路监控数据
|
||||||
const fixedRoadMonitors = ref([
|
// const fixedRoadMonitors = ref([
|
||||||
{ id: 101, name: "高速公路监控001", image: demoImage },
|
// { id: 101, name: "高速公路监控001", src:'' },
|
||||||
{ id: 102, name: "高速公路监控002", image: demoImage },
|
// { id: 102, name: "高速公路监控002", src:'' },
|
||||||
{ id: 103, name: "高速公路监控003", image: demoImage },
|
// { id: 103, name: "高速公路监控003", src:'' },
|
||||||
{ id: 104, name: "高速公路监控004", image: demoImage },
|
// { id: 104, name: "高速公路监控004", src:'' },
|
||||||
{ id: 105, name: "高速公路监控005", image: demoImage },
|
// { id: 105, name: "高速公路监控005", src:'' },
|
||||||
{ id: 106, name: "高速公路监控006", image: demoImage },
|
// { id: 106, name: "高速公路监控006", src:'' },
|
||||||
]);
|
// ]);
|
||||||
|
const fixedRoadMonitors = ref<any[]>([]);
|
||||||
|
|
||||||
// 切换分组展开状态
|
// 切换分组展开状态
|
||||||
const toggleGroup = (group: any) => {
|
const toggleGroup = (group: any) => {
|
||||||
|
@ -62,15 +68,72 @@ const toggleGroup = (group: any) => {
|
||||||
// 选中的监控点
|
// 选中的监控点
|
||||||
const selectedPoints = ref<any[]>([]);
|
const selectedPoints = ref<any[]>([]);
|
||||||
|
|
||||||
// 选择监控点
|
// // 选择监控点
|
||||||
const togglePoint = (point: any) => {
|
// const togglePoint = (point: any) => {
|
||||||
const index = selectedPoints.value.findIndex((p) => p.id === point.id);
|
// const index = selectedPoints.value.findIndex((p) => p.channelCode === point.channelCode);
|
||||||
|
// if (index > -1) {
|
||||||
|
// selectedPoints.value.splice(index, 1);
|
||||||
|
// } else {
|
||||||
|
// selectedPoints.value.push(point);
|
||||||
|
// }
|
||||||
|
// videoStream({deviceCode:'00000000001311027500',channelCode:'00000000001311028602',}).then((res)=>{
|
||||||
|
// videoUrl.value = res;
|
||||||
|
// nextTick();
|
||||||
|
// if (videoRef.value) {
|
||||||
|
// if (Hls.isSupported()) {
|
||||||
|
// const hls = new Hls();
|
||||||
|
// hls.loadSource(videoUrl.value);
|
||||||
|
// hls.attachMedia(videoRef.value);
|
||||||
|
// } else if (videoRef.value.canPlayType('application/vnd.apple.mpegurl')) {
|
||||||
|
// videoRef.value.src = videoUrl.value;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// };
|
||||||
|
const togglePoint = async (point: any) => {
|
||||||
|
const index = selectedPoints.value.findIndex((p) => p.channelCode === point.channelCode);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
|
// 已选中则取消选中,并移除对应视频
|
||||||
selectedPoints.value.splice(index, 1);
|
selectedPoints.value.splice(index, 1);
|
||||||
|
const monitorIdx = fixedRoadMonitors.value.findIndex(m => m.channelCode === point.channelCode);
|
||||||
|
if (monitorIdx > -1) {
|
||||||
|
fixedRoadMonitors.value.splice(monitorIdx, 1);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// 超过6个,移除最早的
|
||||||
|
if (selectedPoints.value.length >= 6) {
|
||||||
|
const removed = selectedPoints.value.shift();
|
||||||
|
// 同步移除fixedRoadMonitors中对应的
|
||||||
|
const rmIdx = fixedRoadMonitors.value.findIndex(m => m.channelCode === removed.channelCode);
|
||||||
|
if (rmIdx > -1) fixedRoadMonitors.value.splice(rmIdx, 1);
|
||||||
|
}
|
||||||
selectedPoints.value.push(point);
|
selectedPoints.value.push(point);
|
||||||
|
|
||||||
|
// 获取视频流并放入fixedRoadMonitors
|
||||||
|
const res = await videoStream({ deviceCode: point.deviceCode, channelCode: point.channelCode });
|
||||||
|
// 保证fixedRoadMonitors只有6个
|
||||||
|
if (fixedRoadMonitors.value.length >= 6) {
|
||||||
|
fixedRoadMonitors.value.shift();
|
||||||
|
}
|
||||||
|
fixedRoadMonitors.value.push({
|
||||||
|
...point,
|
||||||
|
src: res
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
onMounted(()=>{
|
||||||
|
const endTime = dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
getVideoDeviceList({endTime:endTime, currentPage: 1, pageSize: 2}).then((res)=>{ //暂时是公共的 先取两条
|
||||||
|
console.log(res, 'rrrrrrrrrrrrrrrrrrrrrrrrrsssssssssssss')
|
||||||
|
if(res.code == 200){
|
||||||
|
res.data.data.forEach((item)=>{
|
||||||
|
item.expanded = true
|
||||||
|
})
|
||||||
|
monitoringGroups.value = res.data.data
|
||||||
|
}
|
||||||
|
console.log(monitoringGroups.value, 'monitoringGroups.value')
|
||||||
|
})
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -107,36 +170,36 @@ const togglePoint = (point: any) => {
|
||||||
</div>
|
</div>
|
||||||
<div class="list-content">
|
<div class="list-content">
|
||||||
<div
|
<div
|
||||||
v-for="group in monitoringGroups"
|
v-for="(group, index) in monitoringGroups"
|
||||||
:key="group.id"
|
:key="index"
|
||||||
class="group-item"
|
class="group-item"
|
||||||
>
|
>
|
||||||
<div class="group-header" @click="toggleGroup(group)">
|
<div class="group-header" @click="toggleGroup(group)">
|
||||||
<div class="group-name">
|
<div class="group-name">
|
||||||
<i class="arrow" :class="{ expanded: group.expanded }"></i>
|
<!-- <i class="arrow" :class="{ expanded: group.expanded }"></i> -->
|
||||||
<span style="font-size: 22.5px"
|
<span style="font-size: 22.5px"
|
||||||
>{{ group.name }}({{ group.count }})</span
|
>{{ group.deviceName }}({{ group.channels.length }})</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="group-content" v-show="group.expanded">
|
<div class="group-content" v-show="group.channels">
|
||||||
<div
|
<div
|
||||||
v-for="point in group.points"
|
v-for="(point, vic) in group.channels"
|
||||||
:key="point.id"
|
:key="point.channelCode"
|
||||||
class="point-item"
|
class="point-item"
|
||||||
:class="{ active: selectedPoints.some((p) => p.id === point.id) }"
|
:class="{ active: selectedPoints.some((p) => p.channelCode === point.channelCode) }"
|
||||||
@click="togglePoint(point)"
|
@click="togglePoint(point)"
|
||||||
>
|
>
|
||||||
<div class="point-info">
|
<div class="point-info">
|
||||||
<span class="point-name">{{ point.name }}</span>
|
<span class="point-name">{{ point.channelName }}</span>
|
||||||
<div class="status-wrapper">
|
<div class="status-wrapper">
|
||||||
<span
|
<span
|
||||||
class="status-indicator"
|
class="status-indicator"
|
||||||
:class="point.status === '正常' ? 'normal' : 'warning'"
|
:class="point.status === 1 ? 'normal' : 'warning'"
|
||||||
></span>
|
></span>
|
||||||
<span
|
<span
|
||||||
class="point-status"
|
class="point-status"
|
||||||
:class="point.status === '正常' ? 'normal' : 'warning'"
|
:class="point.status === 1 ? 'normal' : 'warning'"
|
||||||
>
|
>
|
||||||
{{ point.status }}
|
{{ point.status }}
|
||||||
</span>
|
</span>
|
||||||
|
@ -150,7 +213,7 @@ const togglePoint = (point: any) => {
|
||||||
|
|
||||||
<!-- 右侧图像网格 - 改为固定显示六张图片 -->
|
<!-- 右侧图像网格 - 改为固定显示六张图片 -->
|
||||||
<div class="monitor-grid">
|
<div class="monitor-grid">
|
||||||
<div class="fixed-grid-container">
|
<!-- <div class="fixed-grid-container">
|
||||||
<div
|
<div
|
||||||
v-for="monitor in fixedRoadMonitors"
|
v-for="monitor in fixedRoadMonitors"
|
||||||
:key="monitor.id"
|
:key="monitor.id"
|
||||||
|
@ -161,7 +224,34 @@ const togglePoint = (point: any) => {
|
||||||
<div class="corner-decoration bottom-left"></div>
|
<div class="corner-decoration bottom-left"></div>
|
||||||
<div class="corner-decoration bottom-right"></div>
|
<div class="corner-decoration bottom-right"></div>
|
||||||
<div class="image-container">
|
<div class="image-container">
|
||||||
<img :src="monitor.image" :alt="monitor.name" />
|
<video
|
||||||
|
ref="videoRef"
|
||||||
|
v-if="videoUrl"
|
||||||
|
controls
|
||||||
|
autoplay
|
||||||
|
style="width:100%;height:300px;background:#000"
|
||||||
|
></video>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div> -->
|
||||||
|
<div class="fixed-grid-container">
|
||||||
|
<div
|
||||||
|
v-for="(monitor, idx) in fixedRoadMonitors"
|
||||||
|
:key="monitor.channelCode || monitor.id || idx"
|
||||||
|
class="grid-item"
|
||||||
|
>
|
||||||
|
<div class="corner-decoration top-left"></div>
|
||||||
|
<div class="corner-decoration top-right"></div>
|
||||||
|
<div class="corner-decoration bottom-left"></div>
|
||||||
|
<div class="corner-decoration bottom-right"></div>
|
||||||
|
<div class="image-container">
|
||||||
|
<video
|
||||||
|
v-if="monitor.src"
|
||||||
|
:src="monitor.src"
|
||||||
|
controls
|
||||||
|
autoplay
|
||||||
|
style="width:100%;height:300px;background:#000"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -321,30 +321,38 @@ const handleTimeRangeChange = (event: Event) => {
|
||||||
// 这里可以添加切换时间范围的逻辑
|
// 这里可以添加切换时间范围的逻辑
|
||||||
};
|
};
|
||||||
const items = ref([
|
const items = ref([
|
||||||
{
|
{
|
||||||
defaultImg: eqm1Default,
|
defaultImg: eqm1Default,
|
||||||
activeImg: eqm1Active,
|
activeImg: eqm1Active,
|
||||||
isActive: false,
|
isActive: true, // 默认选中
|
||||||
|
type: '44', //
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
defaultImg: eqm2Default,
|
defaultImg: eqm2Default,
|
||||||
activeImg: eqm2Active,
|
activeImg: eqm2Active,
|
||||||
isActive: false,
|
isActive: true,
|
||||||
|
type: '22', // 视频监控
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
defaultImg: eqm3Default,
|
defaultImg: eqm3Default,
|
||||||
activeImg: eqm3Active,
|
activeImg: eqm3Active,
|
||||||
isActive: false,
|
isActive: true,
|
||||||
|
type: '53', // 广播
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
defaultImg: eqm4Default,
|
defaultImg: eqm4Default,
|
||||||
activeImg: eqm4Active,
|
activeImg: eqm4Active,
|
||||||
isActive: false,
|
isActive: true,
|
||||||
|
type: '15', // 情报板
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const toggleSelection = (index: number) => {
|
const toggleSelection = (index: number) => {
|
||||||
items.value[index].isActive = !items.value[index].isActive;
|
items.value[index].isActive = !items.value[index].isActive;
|
||||||
|
// 通知地图组件切换显示/隐藏
|
||||||
|
window.dispatchEvent(new CustomEvent('toggle-map-device', {
|
||||||
|
detail: { type: items.value[index].type, show: items.value[index].isActive }
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -50,14 +50,14 @@ const iconModules = import.meta.glob("@/assets/img/map/*.png", {
|
||||||
function getIcon(type, active = false) {
|
function getIcon(type, active = false) {
|
||||||
const iconName =
|
const iconName =
|
||||||
{
|
{
|
||||||
1: active ? "lianzhen.png" : "lianzhen.png",
|
// 1: active ? "lianzhen.png" : "lianzhen.png",
|
||||||
2: active ? "datun.png" : "datun.png",
|
// 2: active ? "datun.png" : "datun.png",
|
||||||
3: active ? "zhaowang.png" : "zhaowang.png",
|
// 3: active ? "zhaowang.png" : "zhaowang.png",
|
||||||
4: active ? "dongguang.png" : "dongguang.png",
|
// 4: active ? "dongguang.png" : "dongguang.png",
|
||||||
5: active ? "nanpi.png" : "nanpi.png",
|
// 5: active ? "nanpi.png" : "nanpi.png",
|
||||||
6: active ? "zhaizi.png" : "zhaizi.png",
|
// 6: active ? "zhaizi.png" : "zhaizi.png",
|
||||||
7: active ? "xiaozhuang.png" : "xiaozhuang.png",
|
// 7: active ? "xiaozhuang.png" : "xiaozhuang.png",
|
||||||
8: active ? "warning.png" : "warning.png",
|
// 8: active ? "warning.png" : "warning.png",
|
||||||
22: active ? "video-active.png" : "video.png",
|
22: active ? "video-active.png" : "video.png",
|
||||||
44: active ? "weather-active.png" : "weather.png",
|
44: active ? "weather-active.png" : "weather.png",
|
||||||
53: active ? "broadcast-active.png" : "broadcast.png",
|
53: active ? "broadcast-active.png" : "broadcast.png",
|
||||||
|
@ -101,6 +101,18 @@ const customDevices = [
|
||||||
];
|
];
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
// 监听设备的显示隐藏
|
||||||
|
window.addEventListener('toggle-map-device', (e) => {
|
||||||
|
const { type, show } = e.detail;
|
||||||
|
showType.value[type] = show;
|
||||||
|
markerMap.value[type]?.forEach((marker) => {
|
||||||
|
if (show) {
|
||||||
|
map.addOverlay(marker);
|
||||||
|
} else {
|
||||||
|
map.removeOverlay(marker);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
await nextTick();
|
await nextTick();
|
||||||
map = new BMap.Map("baiduMap");
|
map = new BMap.Map("baiduMap");
|
||||||
const point = new BMap.Point(116.827009, 37.890385);
|
const point = new BMap.Point(116.827009, 37.890385);
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted } from "vue";
|
import { ref, onMounted, computed } from "vue";
|
||||||
import EventStatus from "./EventStatus.vue";
|
import EventStatus from "./EventStatus.vue";
|
||||||
import EventTask from "./EventTask.vue";
|
import EventTask from "./EventTask.vue";
|
||||||
import RealTimeImage from "./RealTimeImage.vue";
|
import RealTimeImage from "./RealTimeImage.vue";
|
||||||
import { weatherForecast, weatherHourly, todayStatusCount, todayHourly } from "@/api/modules/index";
|
import { weatherForecast, weatherHourly, todayStatusCount, todayHourly, getDictData } from "@/api/modules/index";
|
||||||
import { useTodayTime, } from "@/utils/packge";
|
import { useTodayTime, } from "@/utils/packge";
|
||||||
|
|
||||||
const { todayTime, getTodayTime } = useTodayTime();
|
const { todayTime, getTodayTime } = useTodayTime();
|
||||||
// 模拟数据,实际项目中可能需要从API获取
|
// 模拟数据,实际项目中可能需要从API获取
|
||||||
const pendingCount = ref<string>('');
|
const pendingCount = ref<string>('');
|
||||||
const processingCount = ref<string>('');
|
const processingCount = ref<string>('');
|
||||||
|
const serviceArea = ref<any[]>([]);
|
||||||
|
const Hub = ref<any[]>([]);
|
||||||
|
const Intercommunication = ref<any[]>([]);
|
||||||
// 服务区数据
|
// 服务区数据
|
||||||
const selectedServiceArea = ref("全部");
|
const selectedServiceArea = ref("全部");
|
||||||
const serviceAreas = ref(["全部", "南皮服务区", "东光服务区"]);
|
const serviceAreas = ref([]);
|
||||||
// 服务区选择变更处理
|
// 服务区选择变更处理
|
||||||
const handleServiceAreaChange = (area: string) => {
|
const handleServiceAreaChange = (area: string) => {
|
||||||
selectedServiceArea.value = area;
|
selectedServiceArea.value = area;
|
||||||
|
@ -28,25 +30,49 @@ const areaTypes = ref([
|
||||||
|
|
||||||
// 切换区域类型选中状态
|
// 切换区域类型选中状态
|
||||||
const toggleAreaType = (id: string) => {
|
const toggleAreaType = (id: string) => {
|
||||||
const areaType = areaTypes.value.find((type) => type.id === id);
|
areaTypes.value.forEach((type) => {
|
||||||
if (areaType) {
|
type.selected = type.id === id;
|
||||||
areaType.selected = !areaType.selected;
|
});
|
||||||
}
|
|
||||||
};
|
};
|
||||||
onMounted(()=>{
|
onMounted(()=>{
|
||||||
todayStatusCount({todayTime: todayTime.value}).then((res: any) => {
|
todayStatusCount({todayTime: todayTime.value}).then((res: any) => {
|
||||||
if(res.code === 200){
|
if(res.code === 200){
|
||||||
res.data.forEach((item: any) => {
|
res.data.forEach((item: any) => {
|
||||||
if(item.status == '0'){ //待处理
|
if(item.status == '0'){ //待处理
|
||||||
pendingCount.value = item.count
|
pendingCount.value = item.count || 0
|
||||||
} else if(item.status == '2'){ //处置中
|
} else if(item.status == '2'){ //处置中
|
||||||
processingCount.value = item.count
|
processingCount.value = item.count || 0
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
getDictData("hb_service_area").then((res: any) => { //服务区
|
||||||
|
if (res.code === 200) {
|
||||||
|
serviceArea.value = res.data || [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
getDictData("hb_hub").then((res: any) => { //枢纽
|
||||||
|
if (res.code === 200) {
|
||||||
|
Hub.value = res.data || [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
getDictData("hb_interchange").then((res: any) => { //互通
|
||||||
|
if (res.code === 200) {
|
||||||
|
Intercommunication.value = res.data || [];
|
||||||
|
}
|
||||||
|
});
|
||||||
})
|
})
|
||||||
|
const currentAreaList = computed(() => {
|
||||||
|
const selectedType = areaTypes.value.find((type) => type.selected);
|
||||||
|
if (selectedType?.id === "service") {
|
||||||
|
return serviceArea.value;
|
||||||
|
} else if (selectedType?.id === "interchange") {
|
||||||
|
return Intercommunication.value;
|
||||||
|
} else if (selectedType?.id === "hub") {
|
||||||
|
return Hub.value;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
const isDropdownOpen = ref(false);
|
const isDropdownOpen = ref(false);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -91,16 +117,16 @@ const isDropdownOpen = ref(false);
|
||||||
</div>
|
</div>
|
||||||
<div class="dropdown-menu" v-show="isDropdownOpen">
|
<div class="dropdown-menu" v-show="isDropdownOpen">
|
||||||
<div
|
<div
|
||||||
v-for="area in serviceAreas"
|
v-for="(item, index) in currentAreaList"
|
||||||
:key="area"
|
:key="index"
|
||||||
class="dropdown-item"
|
class="dropdown-item"
|
||||||
:class="{ active: selectedServiceArea === area }"
|
:class="{ active: selectedServiceArea === item.dictLabel }"
|
||||||
@click="
|
@click="
|
||||||
handleServiceAreaChange(area);
|
handleServiceAreaChange(item.dictLabel);
|
||||||
isDropdownOpen = false;
|
isDropdownOpen = false;
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
{{ area }}
|
{{ item.dictLabel }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -118,6 +144,7 @@ const isDropdownOpen = ref(false);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
margin-right: 20px;
|
||||||
// justify-content: space-between;
|
// justify-content: space-between;
|
||||||
// align-items: flex-start;
|
// align-items: flex-start;
|
||||||
.top {
|
.top {
|
||||||
|
|
Loading…
Reference in New Issue