Commit d73c08a6 authored by 张牧越's avatar 张牧越

扬尘噪音联调

parent 03f644c2
File added
......@@ -100,6 +100,14 @@ export function getVideoTreeData() {
method: 'get',
})
}
export function getVideoLiveAddressUrl(cameraId) {
return request({
url: `/pweb/s/camera/liveaddress/${cameraId}`,
method: 'get',
})
}
// early-warning
export function getAIdata() {
......@@ -150,4 +158,19 @@ export function getNoiseDeviceList(params) {
method: 'get',
params
})
}
\ No newline at end of file
}
export function getCurrentMonitorStatis() {
return request({
url: '/pweb/s/env/basedata',
method: 'get',
})
}
export function getWarningMessageList(params) {
return request({
url: '/pweb/s/env/warning/list',
method: 'get',
params
})
}
......@@ -44,8 +44,13 @@
<el-divider></el-divider>
<el-progress
type="circle"
:percentage="60"
:format="(value) => formatText(value, '40')"
:percentage="
computedPercentage(
currentMonitorStatis.pm10,
currentMonitorStatis.pm10_max
)
"
:format="(value) => formatText(value, currentMonitorStatis.pm10)"
:color="customMonitorColors"
:stroke-width="12"
></el-progress>
......@@ -55,8 +60,13 @@
<el-divider></el-divider>
<el-progress
type="circle"
:percentage="40"
:format="(value) => formatText(value, '35')"
:percentage="
computedPercentage(
currentMonitorStatis.pm25,
currentMonitorStatis.pm25_max
)
"
:format="(value) => formatText(value, currentMonitorStatis.pm25)"
:color="customMonitorColors"
:stroke-width="12"
></el-progress>
......@@ -68,8 +78,13 @@
<el-divider></el-divider>
<el-progress
type="circle"
:percentage="10"
:format="(value) => formatText(value, '52')"
:percentage="
computedPercentage(
currentMonitorStatis.noise,
currentMonitorStatis.noise_max
)
"
:format="(value) => formatText(value, currentMonitorStatis.noise)"
:color="customMonitorColors"
:stroke-width="12"
></el-progress>
......@@ -85,20 +100,21 @@
</div>
<div class="aqi-level">
<div class="aqi-level-line">
<div style="background: #18d7b9"></div>
<div style="background: #feba01"></div>
<div style="background: #ff754c"></div>
<div style="background: #f11b1f"></div>
<div style="background: #6339e6"></div>
<div style="background: #751dbf"></div>
<div
v-for="(color, index) in color_arr"
:key="index"
:style="{ background: color }"
></div>
</div>
<div class="aqi-level-text">
<div></div>
<div></div>
<div>轻度</div>
<div>中度</div>
<div>重度</div>
<div>严重</div>
<div
v-for="(
aqi_level, index
) in currentMonitorStatis.aqi_level_arr"
:key="index"
>
{{ aqi_level.name }}
</div>
</div>
</div>
</div>
......@@ -109,7 +125,10 @@
<div>温度</div>
<img src="@/assets/noise/icon_f_san@2x.png" alt="" />
</div>
<div class="rt-value"><span>14.5</span></div>
<div class="rt-value">
<span>{{ currentMonitorStatis.temperature }}</span
>
</div>
</div>
<el-divider> </el-divider>
<div class="rt-single-status">
......@@ -117,7 +136,10 @@
<div>湿度</div>
<img src="@/assets/noise/icon_f_si@2x.png" alt="" />
</div>
<div class="rt-value"><span>36</span>%</div>
<div class="rt-value">
<span>{{ currentMonitorStatis.humidity }}</span
>%
</div>
</div>
<el-divider> </el-divider>
<div class="rt-single-status">
......@@ -125,7 +147,10 @@
<div>风速</div>
<img src="@/assets/noise/icon_f_wu@2x.png" alt="" />
</div>
<div class="rt-value"><span>5</span>m/s</div>
<div class="rt-value">
<span>{{ currentMonitorStatis.wind_speed }}</span
>m/s
</div>
</div>
<el-divider> </el-divider>
<div class="rt-single-status">
......@@ -137,9 +162,15 @@
class="rt-value"
style="line-height: 20px; padding-top: 16px"
>
2023-03-02
{{
currentMonitorStatis.log_time &&
currentMonitorStatis.log_time.split(" ")[0]
}}
<br />
09:23:12
{{
currentMonitorStatis.log_time &&
currentMonitorStatis.log_time.split(" ")[1]
}}
</div>
</div>
<el-divider> </el-divider>
......@@ -157,12 +188,21 @@
<div class="warp">
<div
class="warn-title"
:style="{ borderColor: '#fff' }"
v-for="(warn, index) in warnings"
:key="index"
>
<div class="warn-text">{{ warn.text }}</div>
<div class="warn-time">{{ warn.time }}</div>
<div class="warn-text">
<el-tooltip
class="item"
effect="dark"
:content="warn.alarm_content"
>
<div class="overflowed">
{{ warn.alarm_content }}
</div>
</el-tooltip>
</div>
<div class="warn-time">{{ warn.log_time }}</div>
</div>
</div>
</ShadowContainer>
......@@ -183,7 +223,12 @@
</div>
</template>
<script>
import { getDeviceTotalStatus, getNoiseDeviceList } from "@/api/index";
import {
getDeviceTotalStatus,
getCurrentMonitorStatis,
getNoiseDeviceList,
getWarningMessageList,
} from "@/api/index";
export default {
name: "Noise",
data() {
......@@ -214,26 +259,45 @@ export default {
{ color: "#FF754C", percentage: 75 },
{ color: "#FF754C", percentage: 100 },
],
warnings: [
{
text: "报警标题:报警内容123123123报警标题:报警内容123123123报警标题:报警内容123123123",
time: "2023-02-12 09:12",
},
{ text: "报警标题:报警内容123123123", time: "2023-02-12 09:12" },
{ text: "报警标题:报警内容123123123", time: "2023-02-12 09:12" },
{ text: "报警标题:报警内容123123123", time: "2023-02-12 09:12" },
{ text: "报警标题:报警内容123123123", time: "2023-02-12 09:12" },
{ text: "报警标题:报警内容123123123", time: "2023-02-12 09:12" },
{ text: "报警标题:报警内容123123123", time: "2023-02-12 09:12" },
{ text: "报警标题:报警内容123123123", time: "2023-02-12 09:12" },
],
warnings: [],
noiseMonitors: [],
classOption: {
singleHeight: 36,
},
currentMonitorStatis: {
aqi_level_arr: [],
},
color_arr: [
"#18d7b9",
"#feba01",
"#ff754c",
"#f11b1f",
"#6339e6",
"#751dbf",
],
};
},
computed: {},
methods: {
computedPercentage(value, max) {
return Number(Number(Number(value) / max) * 100).toFixed(2);
},
getTime(ts) {
let date = new Date(ts);
let year = date.getFullYear();
let month = date.getMonth() + 1;
let day = date.getDate();
month = month > 9 ? month : "0" + month;
day = day < 10 ? "0" + day : day;
let today = year + "-" + month + "-" + day;
let hour = date.getHours() < 10 ? "0" + date.getHours() : date.getHours();
let min =
date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes();
let sec =
date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
return `${today} ${hour}:${min}:${sec}`;
},
renderDeviceChart() {
const pieData = [
{ value: this.pieChartData.zxCount, name: "在线设备数量" },
......@@ -469,19 +533,31 @@ export default {
return text;
},
renderAqiQualityChart() {
this.currentMonitorStatis.aqi_level_arr.map((item, index) => {
item.color = this.color_arr[index];
});
const currentLevel = this.currentMonitorStatis.aqi_level_arr.filter(
(item) => {
return (
Number(this.currentMonitorStatis.aqi) >= item.min &&
Number(this.currentMonitorStatis.aqi) < item.max
);
}
)[0];
console.log(currentLevel);
const chart = this.$echarts.init(this.$refs.aqiQualityChart);
const option = {
title: {
x: "49%", //X坐标
y: "64%",
subtext: "优",
subtext: this.currentMonitorStatis.aqi_level_name,
textAlign: "center",
subtextStyle: {
//副标题样式
fontSize: 24,
fontWeight: "bolder",
lineHeight: 24,
color: "rgba(255,255,255,0.7)",
color: currentLevel.color,
},
},
series: [
......@@ -490,10 +566,12 @@ export default {
center: ["50%", "60%"],
startAngle: 230,
endAngle: -50,
min: 0,
max: 300,
min: this.currentMonitorStatis.aqi_level_arr[0].min,
max: this.currentMonitorStatis.aqi_level_arr[
this.currentMonitorStatis.aqi_level_arr.length - 1
].max,
itemStyle: {
color: "#FFAB91",
color: currentLevel.color,
},
progress: {
show: true,
......@@ -541,19 +619,40 @@ export default {
},
data: [
{
value: 20,
value: Number(this.currentMonitorStatis.aqi),
},
],
},
],
};
chart.setOption(option);
this.$nextTick(() => {
// 解决echarts图表放大溢出父容器
const resizeOb = new ResizeObserver((entries) => {
for (const entry of entries) {
this.$echarts.getInstanceByDom(entry.target).resize();
}
});
resizeOb.observe(document.getElementById("aqi-quality-chart"));
});
},
getNoiseData() {
getDeviceTotalStatus({ top_cat: 9 }).then((res) => {
this.pieChartData = res.data;
this.renderDeviceChart();
});
getCurrentMonitorStatis().then((res) => {
this.currentMonitorStatis = res.data;
this.renderAqiQualityChart();
});
getWarningMessageList({
start_log_time: this.getTime(new Date().getTime() - 2592000000),
end_log_time: this.getTime(new Date().getTime()),
}).then((res) => {
this.warnings = res.data;
});
getNoiseDeviceList({ top_cat: 9 }).then((res) => {
this.noiseMonitors = res.data;
});
......@@ -563,7 +662,6 @@ export default {
this.getNoiseData();
this.renderNoiseChart();
this.renderAQIChart();
this.renderAqiQualityChart();
this.$nextTick(() => {
// 解决echarts图表放大溢出父容器
const resizeOb = new ResizeObserver((entries) => {
......@@ -573,7 +671,7 @@ export default {
});
resizeOb.observe(document.getElementById("noise-chart"));
resizeOb.observe(document.getElementById("aqi-chart"));
resizeOb.observe(document.getElementById("aqi-quality-chart"));
// resizeOb.observe(document.getElementById("aqi-quality-chart"));
});
},
};
......@@ -755,6 +853,15 @@ export default {
display: flex;
justify-content: space-between;
margin-bottom: 18px;
&:nth-child(3n-2) {
border-color: #fd175d;
}
&:nth-child(3n-1) {
border-color: #ab75ff;
}
&:nth-child(3n) {
border-color: #ffbb37;
}
.warn-text {
width: 60%;
text-overflow: ellipsis;
......@@ -783,4 +890,9 @@ export default {
height: 180px;
overflow: hidden;
}
.overflowed {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
</style>
\ No newline at end of file
......@@ -15,33 +15,58 @@
:props="defaultProps"
icon-class="el-icon-arrow-right"
:render-content="renderContent"
@node-expand="expandEvent"
:filter-node-method="filterNode"
accordion
></el-tree>
</div>
</ShadowContainer>
</div>
<div class="video-area">
<div
v-for="(videoImg, index) in cameraList"
:key="index"
:class="['camera', activeIndex == index ? 'active' : '']"
>
<div v-if="!videoImg.live_address_url">
<div class="video-info">
<div class="video-no">{{ index + 1 }}</div>
<div class="video-description">暂无画面~</div>
<div class="video-out-container">
<div class="splice-screen">
<el-button
:class="[activeSplice == 1 ? 'active' : '']"
@click="activeSplice = 1"
>一分屏</el-button
>
<el-button
:class="[activeSplice == 4 ? 'active' : '']"
@click="activeSplice = 4"
>四分屏</el-button
>
<el-button
:class="[activeSplice == 6 ? 'active' : '']"
@click="activeSplice = 6"
>六分屏</el-button
>
<el-button
:class="[activeSplice == 9 ? 'active' : '']"
@click="activeSplice = 9"
>九分屏</el-button
>
<el-button @click="closeAll">全部关闭</el-button>
</div>
<div class="video-area">
<div
v-for="(videoImg, index) in activeSplice"
:key="index"
:class="[
'camera',
activeIndex == index ? 'active' : '',
`camera-in-${activeSplice}`,
]"
>
<div v-if="!cameraList[index] || cameraList[index].player === null">
<div class="video-info">
<div class="video-no">{{ index + 1 }}</div>
<div class="video-description">暂无画面~</div>
</div>
</div>
<div v-else class="video-pic" :ref="`video-js-container-${index}`">
<i class="el-icon-circle-close" @click="closeSingle(index)"></i>
<div
:id="[`video-container-${index + 1}`]"
style="height: 100%"
></div>
</div>
</div>
<div v-else class="video-pic">
<img src="@/assets/swiper-pic.png" alt="" />
<img
@click="openLive(videoImg.live_address_url)"
class="play"
src="@/assets/play.png"
alt=""
/>
</div>
</div>
</div>
......@@ -58,7 +83,7 @@
</div>
</template>
<script>
import { getVideoTreeData } from "@/api/index";
import { getVideoTreeData, getVideoLiveAddressUrl } from "@/api/index";
require("vue-video-player/node_modules/video.js/dist/video-js.css");
import videojs from "video.js";
import "videojs-contrib-hls";
......@@ -67,57 +92,17 @@ export default {
data() {
return {
filterText: "",
treeData: [
{
area_name: "北门",
device_data: [
{
id: 7,
device_name: "测试监控",
device_no: "223311",
top_cat: 2,
child_cat: 0,
factory_no: "",
model: "",
produce_name: "",
produce_date: null,
use_project_id: 12,
company_id: 0,
status: 1,
ip_address: "",
port: 0,
remark: "",
data_channel: 1,
config_extra: "",
is_online: 0,
area_id: 5,
area_name: "北门",
project_name: "绍兴二院兰亭院区(康复医院)工程123",
client_name: null,
top_cat_name: "摄像头",
status_name: "启用",
config_extra_arr: {
deviceSerial: "D46048684",
appKey: "f963254fd4dd47f683982402a2741538",
appSecret: "9a2a939f45f05e86364eeee43a506626",
},
live_address_url:
"https://open.ys7.com/v3/openlive/D46048684_1_1.m3u8?expire=1681955110&id=568733826499485696&t=5d69a2f783296e60109f6de3424ab1b62898ad5d8ead10482ba89f9780cbf56d&ev=100",
},
],
device_count: 1,
},
],
treeData: [],
defaultProps: {
children: "children",
label: "label",
},
cameraList: [1, 2, 3, 4],
cameraList: [],
activeIndex: null,
liveVisible: false,
singlePlayer: null,
activeSplice: 4,
};
},
mounted() {
......@@ -142,8 +127,8 @@ export default {
return data.map((item, index) => {
return {
label: item.device_name,
live_address_url: item.live_address_url,
index: index,
id: item.id,
};
});
},
......@@ -171,47 +156,112 @@ export default {
</span>
);
},
expandEvent(data) {
this.cameraList = data.children;
},
selectNode(node, data) {
console.log(node, data);
if (node.level == 1) {
this.cameraList = data.children;
} else {
this.activeIndex = data.index;
if (node.level > 1) {
const playerArray = this.cameraList.map((item) => {
return item.player;
});
const cameraIdList = this.cameraList.map((item) => {
return item.id;
});
if (cameraIdList.indexOf(data.id) != -1) {
return this.$message.warning("该监控已存在!");
}
const nullIndex = playerArray.indexOf(null);
if (nullIndex != -1) {
this.cameraList[nullIndex] = {
player: undefined,
id: data.id,
};
this.$forceUpdate();
getVideoLiveAddressUrl(data.id).then((res) => {
this.$nextTick(() => {
const myVideoDiv = document.getElementById(
`video-container-${nullIndex + 1}`
);
myVideoDiv.innerHTML = `<video
id="singleVideo${nullIndex + 1}"
autoplay="autoplay"
class="video-js vjs-default-skin"
></video>`;
const singlePlayer = videojs(`singleVideo${nullIndex + 1}`, {
autoplay: true, // 自动播放
controls: true, // 控件显示
preload: "auto", //定义视频加载模式
loop: true, //是否循环播放
});
singlePlayer.src({
src: res.data.live_address_url,
type: "application/x-mpegURL",
});
singlePlayer.play();
this.cameraList[nullIndex].player = singlePlayer;
});
});
} else {
if (this.cameraList.length == this.activeSplice) {
return this.$message.warning("请先关闭多余的监控!");
}
this.cameraList.push({
player: undefined,
id: data.id,
});
this.$forceUpdate();
getVideoLiveAddressUrl(data.id).then((res) => {
this.$nextTick(() => {
const myVideoDiv = document.getElementById(
`video-container-${this.cameraList.length}`
);
myVideoDiv.innerHTML = `<video
id="singleVideo${this.cameraList.length}"
autoplay="autoplay"
class="video-js vjs-default-skin"
></video>`;
const singlePlayer = videojs(
`singleVideo${this.cameraList.length}`,
{
autoplay: true, // 自动播放
controls: true, // 控件显示
preload: "auto", //定义视频加载模式
loop: true, //是否循环播放
}
);
singlePlayer.src({
src: res.data.live_address_url,
type: "application/x-mpegURL",
});
singlePlayer.play();
this.cameraList[this.cameraList.length - 1].player = singlePlayer;
});
});
}
}
},
openLive(url) {
this.liveVisible = true;
this.$nextTick(() => {
this.singlePlayer = videojs("singleVideo", {
autoplay: true, // 自动播放
controls: true, // 控件显示
width: "100%", // 视频框宽度
height: "600px", // 视频框高度
preload: "auto", //定义视频加载模式
loop: true, //是否循环播放
});
this.singlePlayer.src({ src: url, type: "application/x-mpegURL" });
this.singlePlayer.play();
closeAll() {
this.cameraList.map((item) => {
item.player.dispose();
});
this.cameraList = [];
},
closeSingle(index) {
this.cameraList[index].player.dispose();
this.cameraList[index].player = null;
this.cameraList[index].live_address_url = "";
this.cameraList[index].id = "";
this.$forceUpdate();
},
},
watch: {
filterText(val) {
this.$refs.tree.filter(val);
},
liveVisible(val) {
if (!val) {
this.singlePlayer.dispose();
this.singlePlayer = null;
const myVideoDiv = document.getElementById("video-container");
myVideoDiv.innerHTML = `<video
id="singleVideo"
autoplay="autoplay"
class="video-js vjs-default-skin"
></video>`;
activeSplice(newValue, oldValue) {
if (newValue < oldValue && this.cameraList.length > newValue) {
for (let i = newValue; i < this.cameraList.length; i++) {
this.cameraList[i].player = null;
this.cameraList[i].id = "";
this.cameraList[i].live_address_url = "";
}
}
},
},
......@@ -270,19 +320,42 @@ export default {
height: 860px;
overflow-y: scroll;
}
.video-area {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
border: 1px solid #0f95d4;
height: 974px;
.video-out-container {
width: calc(80% - 20px);
overflow-y: scroll;
}
.video-area {
height: 914px;
width: 100%;
.camera {
width: calc(50% - 2px);
height: 487px;
&:hover {
border-color: #fff;
}
border: 1px solid #0f95d4;
position: relative;
float: left;
box-sizing: border-box;
&.camera-in-1 {
width: 100%;
height: 100%;
}
&.camera-in-4 {
width: calc(50% - 2px);
height: calc(914px / 2 - 2px);
}
&.camera-in-6 {
width: calc(100% / 3 - 2px);
height: calc(910px / 3 - 1px);
display: inline-block;
&:first-child {
width: calc(calc(100% / 3 - 2px) * 2);
height: calc(calc(906px / 3) * 2);
}
}
&.camera-in-9 {
width: calc(100% / 3 - 2px);
height: calc(914px / 3 - 2px);
}
.video-info {
position: absolute;
left: 50%;
......@@ -304,6 +377,12 @@ export default {
.video-pic {
padding: 8px;
height: calc(100% - 16px);
&:hover {
.el-icon-circle-close {
display: block;
cursor: pointer;
}
}
img {
width: 100%;
vertical-align: top;
......@@ -319,10 +398,6 @@ export default {
cursor: pointer;
}
}
&.active {
border: 4px solid #0f95d4;
box-sizing: border-box;
}
}
}
......@@ -357,5 +432,31 @@ export default {
}
::v-deep .video-js {
width: 100%;
height: 100%;
}
.splice-screen {
display: flex;
justify-content: space-between;
margin-bottom: 12px;
.el-button {
background: rgba(40, 137, 195, 0.2);
color: #c6def9;
font-size: 18px;
border-color: rgba(33, 127, 247, 1);
width: calc(100% / 5 - 20px);
&.active {
background: #00a2ff;
color: #fff;
}
}
}
.el-icon-circle-close {
display: none;
color: #fff;
font-size: 24px;
position: absolute;
right: 20px;
top: 20px;
z-index: 999;
}
</style>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment