You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

723 lines
20 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div class="container">
<div class="centerImg"></div>
<div class="table2">
<div style="background-color: #094170">
<div class="scrollTable" style="font-weight: bold;width: 20%">
设备名称
</div>
<div class="scrollTable" style="font-weight: bold;width: 20%">
经度
</div>
<div class="scrollTable" style="font-weight: bold;width: 20%">
纬度
</div>
<div class="scrollTable" style="font-weight: bold;width: 40%">
操作
</div>
</div>
<vue-seamless-scroll
:class-option="{...chart1TableOption,limitMoveNum:10}"
:data="table2Data"
class="case-item"
style="height: 84%;overflow: hidden;"
>
<div
v-for="(item, index) in table2Data"
:key="index"
>
<div :style='"background-color:" + ((index % 2 === 0)? "#053460":"#032d57") '>
<div
class="scrollTable" style="width: 20%">
{{ item.deviceName }}
</div>
<div
class="scrollTable" style="width: 20%">
{{ item.longitude }}
</div>
<div
class="scrollTable" style="width: 20%">
{{ item.latitude }}
</div>
<div class="scrollTable" style="width: 40%">
<el-button size="mini" type="primary" @click=" mapOrientation(item)">地图定点</el-button>
<el-button size="mini" type="primary" @click="toEquipmentInfo(item)">设备信息</el-button>
</div>
</div>
</div>
</vue-seamless-scroll>
</div>
<div class="table1">
<div style="background-color: #094170">
<div class="scrollTable" style="font-weight: bold;">
告警编号
</div>
<div class="scrollTable" style="font-weight: bold;">
告警类型
</div>
<div class="scrollTable" style="font-weight: bold;">
告警单元
</div>
<div class="scrollTable" style="font-weight: bold;">
操作
</div>
</div>
<vue-seamless-scroll
:class-option="{...chart1TableOption,limitMoveNum:10}"
:data="table1Data"
class="case-item"
style="height: 84%;overflow: hidden;"
>
<div
v-for="(item, index) in table1Data"
:key="index"
>
<div :style='"background-color:" + ((index % 2 === 0)? "#053460":"#032d57") '>
<div
class="scrollTable">
{{ item.value1 }}
</div>
<div
class="scrollTable">
{{ item.value2 }}
</div>
<div
class="scrollTable">
{{ item.value3 }}
</div>
<div class="scrollTable" style="width: 25%">
<el-button v-if="item.status === '0'" size="mini" type="primary" @click="dispose(item)">处理</el-button>
<span v-else>已处理</span>
</div>
</div>
</div>
</vue-seamless-scroll>
</div>
<div>
<div class="title1">信标设备列表</div>
<div class="title2">告警信息</div>
<div class="info1">监控单元数量</div>
<div class="info2">设备数量</div>
<div class="info3">在线设备数量</div>
<div class="num1">{{ num2 }}</div>
<div class="num2">{{ num1 }}</div>
<div class="num3">{{ num3 }}</div>
</div>
<div id="map" class="map"></div>
<div class="isRail">
<el-switch
v-model="isRail"
@change="RailChange"
active-text="显示电子围栏"
inactive-text="隐藏电子围栏">
</el-switch>
</div>
<el-dialog title="历史记录" :visible.sync="historyDialog">
<el-descriptions class="margin-top" :column="3" border>
<el-descriptions-item>
<template slot="label">
设备编号
</template>
{{ equipmentInfo.deviceId }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
设备名称
</template>
{{ equipmentInfo.deviceName }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
监控单元
</template>
{{ equipmentInfo.monitorUnitName }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
城市名称
</template>
{{ equipmentInfo.deviceLocation }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
经度
</template>
{{ equipmentInfo.longitude }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
纬度
</template>
{{ equipmentInfo.latitude }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
备注
</template>
{{ equipmentInfo.remark }}
</el-descriptions-item>
</el-descriptions>
<el-form :inline="true" :model="form" class="demo-form-inline" style="margin-top: 12px">
<el-form-item label="查询时间">
<el-date-picker
v-model="form.time"
type="datetimerange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="getHistoryData">查询</el-button>
</el-form-item>
</el-form>
<el-table
v-loading="loading"
:data="historyData"
style="width: 100%">
<el-table-column
prop="deviceId"
label="设备编号"
width="180">
</el-table-column>
<el-table-column
prop="longitude"
label="经度"
width="180">
</el-table-column>
<el-table-column
prop="latitude"
label="纬度"
width="180">
</el-table-column>
<el-table-column
prop="speed"
label="速度">
</el-table-column>
<el-table-column
prop="ts"
label="采集时间">
<template slot-scope="scope">
{{ parseTime(scope.row.ts) }}
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="form.pageNum"
:limit.sync="form.pageSize"
@pagination="getHistoryData"
/>
</el-dialog>
<el-dialog
:visible.sync="isDispose"
width="30%">
<el-descriptions class="margin-top" :column="2" border>
<el-descriptions-item>
<template slot="label">
设备id
</template>
{{ warInfo.deviceId }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
监控单元名称
</template>
{{ warInfo.monitorUnitName }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
监控单元类型
</template>
{{ warInfo.monitorUnitTypeName }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
告警编号
</template>
{{ warInfo.alarmInfoId }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
告警类型
</template>
{{ warInfo.alarmTypeName }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
告警位置
</template>
{{ warInfo.location }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
告警信息
</template>
{{ warInfo.alarmLevelName }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
告警时间
</template>
{{ warInfo.createTime }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
告警区域
</template>
{{ warInfo.areaName }}
</el-descriptions-item>
</el-descriptions>
<span>备注</span>
<el-input
style="margin-top: 12px;margin-bottom:12px"
type="textarea"
:rows="2"
placeholder="请输入内容"
v-model="textarea">
</el-input>
<span slot="footer" class="dialog-footer">
<el-button @click="isDispose = false">取 消</el-button>
<el-button type="primary" @click="disposeThis">处理当前</el-button>
<el-button type="primary" @click="disposeAll">处理所有</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import Chart from "@/components/Charts/Chart";
import vueSeamlessScroll from "vue-seamless-scroll";
import * as echarts from 'echarts';
import {
alarmStats,
subDevice,
getAlarmInfos,
ElectronicNumVo,
selectDeviceLatitudeAndLongitude
} from '@/api/board/GPS'
import red from '@/assets/board/GPS/red.png'
import green from '@/assets/board/GPS/green.png'
import {handleAlarmInfo} from "@/api/board/index";
import {selectBeaconDevicesHistory} from "../../../api/board/GPS";
import {selectMonitorElectronic} from "../../../api/board";
let map = null
let polyEditor = null
let texts = []
let polygons = []
let circles = []
export default {
components: {
Chart,
vueSeamlessScroll,
},
watch: {
async $route(to, from) {
await this.getData()
}
},
data() {
return {
warInfo: {},
textarea: '',
isDispose: false,
disposeNo: '',
loading: false,
equipmentInfo: {},
num1: 0,
num2: 0,
num3: 0,
chart1TableOption: {
step: 0.5, // 数值越大速度滚动越快
limitMoveNum: 3, // 开始无缝滚动的数据量 this.dataList.length
hoverStop: true, // 是否开启鼠标悬停stop
direction: 1, // 0向下 1向上 2向左 3向右
openWatch: true, // 开启数据实时监控刷新dom
singleHeight: 0, // 单步运动停止的高度(默认值0是无缝不停止的滚动) direction => 0/1
singleWidth: 0, // 单步运动停止的宽度(默认值0是无缝不停止的滚动) direction => 2/3
waitTime: 0,
},
chart1Option: {
grid: {
left: "0%",
right: "5%",
top: "0%",
bottom: "0%",
containLabel: true,
},
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
},
xAxis: {
type: "value",
axisLine: {
show: true,
lineStyle: {
color: "#fffa",
},
},
splitLine: {
show: true,
lineStyle: {
color: "#fff4",
type: 'dashed'
},
},
boundaryGap: [0, 0.01],
},
},
table1Data: [],
table2Data: [],
sceneId: 0,
isRail: false,
form: {},
total: 100,
historyDialog: false,
historyData: []
}
},
async mounted() {
await this.getData()
},
methods: {
async getData() {
if (this.$store.getters.sceneId === null) return
await this.createMap()
await this.getAlarmStats()
await this.getAubDevice()
await this.setAlarmInfos()
await this.setElectronicNumVo()
await this.getTable2Data()
},
dispose(e) {
this.warInfo = e
this.textarea = ''
this.isDispose = true
this.disposeNo = e.no
},
createMap() {
map = new AMap.Map('map', {
// mapStyle: 'amap://styles/blue',
zoom: 11,
center: [113.4, 23.35],
});
},
setText(e) {
let position = e.areaPoints?.length > 0 ? e.areaPoints : e.centerPoint
if (position.length > 0) {
let length = position.length
let longitudes = e.areaPoints.map(e => e.longitude).reduce((a, b) => a + b)
let latitudes = e.areaPoints.map(e => e.latitude).reduce((a, b) => a + b)
position = [longitudes / length, latitudes / length]
} else {
position = [e.centerPoint.longitude, e.centerPoint.latitude]
}
let text = new AMap.Text({
text: '<div style="color:#fff;width: 100%;height: 100%;">' + '总数:' + e.deviceCount + (e.abnormalCount ? ('/异常数量:' + e.abnormalCount) : '') + '</div>',
anchor: 'center',
draggable: true,
cursor: 'pointer',
position: position
});
text.setStyle({
'background-color': e.abnormalCount ? '#fe0000' : '#0055fe',
padding: '4px 12px',
'font-size': '1vw'
})
map.add(text)
texts.push(text)
},
setPolygon(position, val) {
let e = position.map(val => {
return [val.longitude, val.latitude]
})
let thisPolygon = new AMap.Polygon({
path: e,
fillColor: val > 0 ? '#ff0000' : '#1791fc',
});
map.add(thisPolygon)
map.setFitView()
polygons.push(thisPolygon)
},
setCircle(center, radius, e) {
let circle = new AMap.Circle({
center,
radius,
borderWeight: 3,
strokeColor: e ? '#ff0000' : "#FF33FF",
strokeWeight: 6,
strokeOpacity: 0.2,
fillOpacity: 0.4,
strokeDasharray: [10, 10],
fillColor: e > 0 ? '#ff0000' : '#1791fc',
})
map.add(circle);
map.setFitView()
circles.push(circle)
},
async getAlarmStats() {
if (this.$store.getters.sceneId === null) return
const {data} = await alarmStats(this.$store.getters.sceneId)
},
async getAubDevice() {
let data = await subDevice(this.$store.getters.sceneId)
this.num1 = data.deviceNum
this.num2 = data.subSum
this.num3 = data.onlineDeviceNum
},
async setAlarmInfos() {
const {rows: data} = await getAlarmInfos({"sceneId": this.$store.getters.sceneId})
this.table1Data = data.map((e, i) => {
return {
value1: e.alarmInfoId,
value2: e.alarmTypeName,
value3: e.monitorUnitName,
status: e.handleStatus
}
})
},
async setElectronicNumVo() {
if (!this.isRail) {
return
}
const {data} = await ElectronicNumVo(this.$store.getters.sceneId)
data.forEach(e => {
if (e.areaPoints?.length > 0) {
this.setPolygon(e.areaPoints, e.abnormalCount)
}
if (e.centerPoint) {
this.setCircle([e.centerPoint.longitude, e.centerPoint.latitude], e.radius, e.abnormalCount)
}
this.setText(e)
})
},
RailChange(e) {
if (e) {
this.setElectronicNumVo()
} else {
map.remove(texts)
map.remove(polygons)
map.remove(circles)
}
},
async getTable2Data() {
const {data} = await selectDeviceLatitudeAndLongitude(this.$store.getters.sceneId)
this.table2Data = data
data.forEach(e => {
this.setMarker(e)
})
},
setMarker(e) {
let marker = new AMap.Marker({
position: [e.longitude, e.latitude], // 经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9]
title: `信息\n经度${e.longitude}\n纬度${e.latitude}\n名称${e.deviceLocation}\n备注${e.remark}`,
offset: new AMap.Pixel(-15, -30),
content: `<div >
<svg t="1718261114618" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4305" style="width: 30px;height: 30px;">
<path d="M512 64.5c-180.5 0-326.9 146.4-326.9 326.9 0 63.3 18 122.3 49.2 172.4 1.1 2 2.1 4 3.2 5.9 34.9 58.2 113.9 128.8 165.5 194.5 66.3 84.4 93.2 158 93.2 158 1.6 1.4 13.1 35.6 15.1 35.8 2.5 0.2 12.6-34 14.5-35.8 0 0 22-69.2 83.7-146.6 56-70.1 142.8-145.7 177-206 0.9-1.6 1.8-3.3 2.6-5 31.5-50.2 49.7-109.6 49.7-173.3 0.1-180.5-146.3-326.8-326.8-326.8z" fill="${(e.ifAlarm === '1') ? '#3D93FD' : '#ff0000'}" p-id="4306">
</path>
</svg>
</div>`
});
marker.on('click', async () => {
if (polygons.length > 0 || circles.length > 0) {
map.remove(polygons)
map.remove(circles)
polygons = []
circles = []
return
}
const {data} = await selectMonitorElectronic(e.deviceId)
data.map(e => e.hwFenceAreaList).flat(1).forEach(e => {
if (e.areaShapeFlag === '1') {
this.setPolygon(e.areaRange.split('_').map(e => e.split(',').map(v => parseFloat(v))))
}
if (e.areaShapeFlag === '2') {
let arr = e.areaRange.split(',')
this.setCircle([arr[0], arr[1]], arr[2])
}
})
})
map.add(marker);
},
mapOrientation(e) {
map.setZoomAndCenter(16, [e.longitude, e.latitude])
},
toEquipmentInfo(e) {
this.equipmentInfo = e
console.log(e)
this.historyDialog = true
this.form = {
pageNum: 1,
pageSize: 10,
deviceId: e.deviceId,
time: [],
}
this.getHistoryData()
// this.$router.push(`/board/senso?id=${e.deviceId}&deviceModeId=${e.deviceModeId}`)
},
async getHistoryData() {
this.loading = true
let query = this.form
query.startTime = this.form.time?.[0]?.getTime()
query.endTime = this.form.time?.[1]?.getTime()
delete query.time
const data = await selectBeaconDevicesHistory(query)
this.loading = false
this.historyData = data.rows
this.total = data.total
}
}
};
</script>
<style scoped lang="less">
.container {
background-image: url("~@/assets/board/GPS/bg.jpg");
background-repeat: no-repeat;
background-size: 100% 100%;
width: 100%;
height: calc(100vh);
position: relative;
}
.table2 {
position: absolute;
width: 25%;
height: 21%;
top: 16%;
left: 3%;
}
.table1 {
position: absolute;
top: 48%;
left: 2.5%;
width: 26%;
height: 48%;
overflow: hidden;
}
.scrollTable {
color: rgb(185, 186, 192);
margin: auto 0px;
padding: 4px 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: center;
display: inline-block;
width: 25%;
}
.title1 {
position: absolute;
top: 13%;
left: 15.5%;
transform: translate(-50%, -50%);
font-size: 1vw;
color: #fff;
}
.title2 {
position: absolute;
top: 45.4%;
left: 15.6%;
transform: translate(-50%, -50%);
font-size: 1vw;
color: #fff;
}
.info1 {
position: absolute;
top: 18%;
left: 39%;
transform: translate(-50%, -50%);
font-size: 1vw;
color: #179ce1;
}
.info2 {
position: absolute;
top: 18%;
left: 62%;
transform: translate(-50%, -50%);
font-size: 1vw;
color: #179ce1;
}
.info3 {
position: absolute;
top: 18%;
left: 83%;
transform: translate(-50%, -50%);
font-size: 1vw;
color: #179ce1;
}
.num1 {
position: absolute;
top: 18%;
left: 47%;
transform: translate(-50%, -50%);
font-size: 1.4vw;
color: #fff;
}
.num2 {
position: absolute;
top: 18%;
left: 69%;
transform: translate(-50%, -50%);
font-size: 1.4vw;
color: #fff;
}
.num3 {
position: absolute;
top: 18%;
left: 91%;
transform: translate(-50%, -50%);
font-size: 1.4vw;
color: #fff;
}
.map {
position: absolute;
top: 28%;
left: 31%;
width: 67%;
height: 69%;
}
.isRail {
position: absolute;
top: 25%;
left: 31%;
/*transform: translate(-50%,-50%);*/
/deep/ .el-switch__label {
color: #fff;
}
/deep/ .el-switch__label.is-active {
color: #1890ff;
}
}
</style>