Browse Source

Merge branch 'develop' of http://39.106.31.193:9211/mengff/jihe-hs into develop

wangqin
hui 1 year ago
parent
commit
cc34991739
  1. 88
      ruoyi-ui/README.md
  2. 22
      ruoyi-ui/src/api/commandDispatch/index.js
  3. 10
      ruoyi-ui/src/views/JiHeExpressway/components/Card1/index.vue
  4. 24
      ruoyi-ui/src/views/JiHeExpressway/components/Card2/Card.vue
  5. 27
      ruoyi-ui/src/views/JiHeExpressway/components/Descriptions.vue
  6. 2
      ruoyi-ui/src/views/JiHeExpressway/components/Dialog/index.vue
  7. 1
      ruoyi-ui/src/views/JiHeExpressway/components/Video/Video.vue
  8. 3
      ruoyi-ui/src/views/JiHeExpressway/components/Video/index.vue
  9. 2
      ruoyi-ui/src/views/JiHeExpressway/components/Video/videoStream.js
  10. 41
      ruoyi-ui/src/views/JiHeExpressway/components/VideoMulti/flv-stream.js
  11. 166
      ruoyi-ui/src/views/JiHeExpressway/components/VideoMulti/index.vue
  12. 241
      ruoyi-ui/src/views/JiHeExpressway/components/VideoMulti/videoStream.js
  13. 18
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/AMapContainer/index.vue
  14. 221
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Dialogs/Broadcast/components/BroadcastParam.vue
  15. 71
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Dialogs/Broadcast/components/BroadcastReleases.vue
  16. 18
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Dialogs/Broadcast/index.vue
  17. 184
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Dialogs/FatigueWakesUp/components/DeviceControlDialog.vue
  18. 1
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Dialogs/FatigueWakesUp/index.vue
  19. 5
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Dialogs/PerceiveEvent/index.vue
  20. 201
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Dialogs/SmartDevice/components/DeviceParamsMulti.vue
  21. 38
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Dialogs/TrafficIncidents/index.vue
  22. 1
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/HomeFilter/index.vue
  23. 105
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/HomeFrameControl/index.vue
  24. 1
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/RoadAndEvents/index.vue
  25. 30
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/index.vue
  26. 5
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/businessDataManagement/components/PeopleCard/index.vue
  27. 26
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/businessDataManagement/views/firstResponders/components/AddNEditDialog.vue
  28. 2
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/businessDataManagement/views/firstResponders/index.vue
  29. 83
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/CrowdnessIndicatorRankings/charts.js
  30. 158
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/CrowdnessIndicatorRankings/index.vue
  31. 51
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/DeviceControl/index.vue
  32. 207
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/DeviceControl/utils.js
  33. 187
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/DispatchLiaison/StatsDialogVisible/index.vue
  34. 201
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/DispatchLiaison/index.vue
  35. 237
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/DisposalProcess/index.vue
  36. BIN
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/EventInformation/images/lane.jpg
  37. 79
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/EventInformation/images/lane.svg
  38. 15
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/EventInformation/index.vue
  39. 13
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/RealTimeVideo/index.vue
  40. 0
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/ReleaseInformation/images/1.svg
  41. 0
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/ReleaseInformation/images/2.svg
  42. 0
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/ReleaseInformation/images/3.svg
  43. 0
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/ReleaseInformation/images/4.svg
  44. 39
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/ReleaseInformation/index.vue
  45. 105
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/index.vue
  46. 13
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/event/EventDetailDialog/Carousel/index.vue
  47. 104
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/event/EventDetailDialog/index.vue
  48. 2
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/event/FormEvent/data.js
  49. 39
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/event/FormEvent/index.vue
  50. 5
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/event/index.vue
  51. 413
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/plan/addAndEditDialog/ScopeTable.vue
  52. 472
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/plan/addAndEditDialog/index copy.vue
  53. 316
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/plan/addAndEditDialog/index.vue
  54. 58
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/plan/data.js
  55. 14
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/plan/index.vue
  56. 34
      ruoyi-ui/src/views/JiHeExpressway/pages/control/event/plan/qbbDialog/index.vue
  57. 4
      ruoyi-ui/src/views/JiHeExpressway/pages/control/manual/statistic/components/Eventfiltering/trafficIncidentsCharts.js
  58. 3
      ruoyi-ui/src/views/JiHeExpressway/pages/control/manual/statistic/components/Sitefiltering/trafficIncidentsCharts.js
  59. 3
      ruoyi-ui/src/views/JiHeExpressway/pages/control/manual/statistic/components/Timefiltering/trafficIncidentsCharts.js
  60. 4
      ruoyi-ui/src/views/JiHeExpressway/pages/perception/trafficFlow/components/classification/index.vue
  61. 2
      ruoyi-ui/src/views/JiHeExpressway/pages/perception/trafficFlow/components/flowstate/index.vue
  62. 197
      ruoyi-ui/src/views/JiHeExpressway/utils/enum.js
  63. 2
      ruoyi-ui/vue.config.js

88
ruoyi-ui/README.md

@ -84,84 +84,13 @@ data.js 表单封装,里面会使用PresetFormItems的一些FormItem.
Form表单属性介绍:
<Form labelWidth="90px" column="2" class="form" ref="FormConfigRef" :formList="formList" />
{
key: "ServiceAreaAbnormal", //唯一标识 一般tabs才需要
label: "服务区异常",//表单标题
formConfig: {
formOptions: {//element中表单配置参数
column: 2, //一行放多少元素(基于:style grid布局实现)
},
list: [//FormItem定义
{
label: "异常分类:",
key: "eventSubclass",
type: "RadioGroup",
isAlone: true,
required: true,
options: {
activeColor: "linear-gradient(180deg, #37E7FF 0%, #009BCC 100%)",
options: [
{
key: "8-1",
label: "封闭、暂停营业",
},
{
key: "8-2",
label: "重要设施停用",
},
{
key: "8-3",
label: "其他异常",
},
],
},
},
PresetFormItems.freeway,
PresetFormItems.direction,
{
label: "服务区:",
key: "dcEventServiceArea.facilityId",
type: "select",
isAlone: true,
required: true,
options: {
options: [
{
value: "选项1",
label: "黄金糕",
},
],
},
},
{
label: "出入口:",
key: "dcEventServiceArea.exitsInlets",
type: "RadioGroup",
isAlone: true,
required: true,
options: {
activeColor: "linear-gradient(180deg, #37E7FF 0%, #009BCC 100%)",
options: [
{
key: "1",
label: "出口",
},
{
key: "2",
label: "入口",
},
],
},
},
PresetFormItems.startTime,
PresetFormItems.expectedEndTime,
PresetFormItems.remark,
PresetFormItems.source,
],
},
}
/*
column: 一行放多少元素(基于:style grid布局实现)
formList: 表单项(FormItem)配置集合
v-model: 双向绑定的表单数据(可以获取到表单数据(同步表单数据this.$emit("update:value", this.formData)), 也可修改表单数据)
*/
FormItem属性介绍:
FormItem属性介绍 & ons 事件监听:
export const additionalNotes = {
label: "补充说明:", // 标题
@ -184,8 +113,9 @@ export const additionalNotes = {
},
ons: { //on监听 element事件
change(value, ...args) {
const { data, formList } = args.slice(-1)\[0]; //data 为数据 formList为传入的配置项
mileCal(data);
const { data, formList } = args.slice(-1)\[0]; //data 为表单数据 formList为传入的配置项
formList[4].options.options = []; //调整配置项 修改下拉框options选项
this.$set(this.data, 'deviceState', undefined); //更新v-model双向绑定的表单数据
}
},
};

22
ruoyi-ui/src/api/commandDispatch/index.js

@ -0,0 +1,22 @@
import request from "@/utils/request";
// 查询事件气象
export function WeatherForecast(lng, lat) {
return request({
url:
"/weatherForecast/queryTheSpecifiedLatitudeAndLongitudeWeather/" +
lng +
"/" +
lat,
method: "get",
});
}
// 查询调度联络
export function commandDispatch(data) {
return request({
url: "/business/warning/commandAndDispatch",
method: "post",
data,
});
}

10
ruoyi-ui/src/views/JiHeExpressway/components/Card1/index.vue

@ -11,7 +11,7 @@
<span :style="{'width': labelWidth+'px'}">{{ item.label }}: </span>
<span>
<slot :name="`form-${item.key}`" :data="cardData">
{{ getValue(item.key) }}{{ item.suffix }}
{{ getValue(item) }}{{ item.suffix }}
</slot>
</span>
</p>
@ -110,8 +110,12 @@ export default {
};
},
methods: {
getValue(key) {
return pathGet(this.cardData, key) ?? "-";
getValue(item) {
let value = pathGet(this.cardData, item.key) ?? "-";
if (item.value) {
value = item.value(this.cardData);
}
return value;
},
onClick() {

24
ruoyi-ui/src/views/JiHeExpressway/components/Card2/Card.vue

@ -1,5 +1,5 @@
<template>
<div class='Card'>
<div class="Card">
<Title :title="title">
<template #suffix>
<slot name="title-suffix" />
@ -12,27 +12,31 @@
</template>
<script>
import Title from "@screen/components/Title/index.vue"
import Title from "@screen/components/Title/index.vue";
export default {
name: 'Card',
name: "Card",
components: {
Title
Title,
},
props: {
title: {
type: String
}
}
}
type: String,
},
},
};
</script>
<style lang='scss' scoped>
<style lang="scss" scoped>
.Card {
height: 100%;
width: 100%;
overflow: hidden;
background: linear-gradient(180deg, rgba(6, 66, 88, 0) 0%, rgba(6, 66, 88, .4) 93%);
background: linear-gradient(
180deg,
rgba(6, 66, 88, 0) 0%,
rgba(6, 66, 88, 0.4) 93%
);
display: flex;
flex-direction: column;

27
ruoyi-ui/src/views/JiHeExpressway/components/Descriptions.vue

@ -1,8 +1,16 @@
<template>
<div class="Descriptions keep-ratio" origin="left" :style="getStyle()">
<div class="item" v-for="(item, index) in getResolveList" :key="`${item.key || item.label}${index}`"
:style="[gridStyle(item, index), transformStyle(itemStyle)]" v-if="item.visible != false">
<div class="text title" :style="{...transformStyle(titleStyle), width: labelWidth }">
<div
class="item"
v-for="(item, index) in getResolveList"
:key="`${item.key || item.label}${index}`"
:style="[gridStyle(item, index), transformStyle(itemStyle)]"
v-if="item.visible != false"
>
<div
class="text title"
:style="{ ...transformStyle(titleStyle), width: labelWidth }"
>
<p>
<slot :name="`title-${item.key || item.label}`" :data="item">
{{ item.label || "-" }}
@ -10,9 +18,18 @@
</p>
:
</div>
<ContentText :style="transformStyle(titleStyle)" class="content text" :data="data" :_config="item">
<ContentText
:style="transformStyle(titleStyle)"
class="content text"
:data="data"
:_config="item"
>
<template #default="{ value }">
<slot :name="`content-${item.key || item.label}`" :value="value" :data="item">
<slot
:name="`content-${item.key || item.label}`"
:value="value"
:data="item"
>
{{ value }}
</slot>
</template>

2
ruoyi-ui/src/views/JiHeExpressway/components/Dialog/index.vue

@ -113,7 +113,7 @@ export default {
height: 100%;
background: rgba(0, 0, 0, 0.36);
border-radius: 0px 0px 0px 0px;
z-index: 1999;
z-index: 1100;
// display: flex;
// align-items: center;
// justify-content: center;

1
ruoyi-ui/src/views/JiHeExpressway/components/Video/Video.vue

@ -46,7 +46,6 @@ export default {
},
async mounted() {
// setTimeout(() => {
// this.$nextTick(() => {
this.playVideo();
this.$once("hook:beforeDestroy", () => this.player?.destroy());
// })

3
ruoyi-ui/src/views/JiHeExpressway/components/Video/index.vue

@ -13,6 +13,7 @@
</div>
</div>
<Transition name="fade" mode="out-in">
<Video :rangeIndex="rangeIndex" v-if="active === 'video'" class="video-stream" :pileNum="pileNum" :camId="camId"
:url="url" :videoType="videoType" />
<img v-else src="./view.png" />
@ -60,7 +61,7 @@ export default {
return {
active: "video"
}
}
},
}
</script>

2
ruoyi-ui/src/views/JiHeExpressway/components/Video/videoStream.js

@ -67,7 +67,6 @@ export async function openVideoStream(container, { camId, url } = {}) {
async function getUrl({ camId, url, pileNum, rangeIndex } = {}) {
// return testFlvUrl;
if (url) return url;
if (pileNum) {
let code, data, cameraData;
if (rangeIndex) {
@ -128,7 +127,6 @@ export class HttpLivePlayer {
this.container = container;
if (!flvJs.getFeatureList().mseLiveFlvPlayback)
return Message.error("浏览器不支持播放 flv 视频流");
getUrl(options).then((url) => {
this.url = url;
this.initLiveVideo();

41
ruoyi-ui/src/views/JiHeExpressway/components/VideoMulti/flv-stream.js

@ -0,0 +1,41 @@
import flvJs from "flv.js";
import { getCameraStream } from "@screen/pages/Home/components/RoadAndEvents/utils/httpList.js";
/**
*
* @param {HTMLElement} container 容器
* @param {{camId?: string; url?: string}?} options {camId: 相机ID; url: 直播地址}
* @returns
*/
export async function openVideoStream(container, { camId, url } = {}) {
if (camId) {
const { code, data } = await getCameraStream(camId).catch(() => ({}));
if (code != 200) return;
url = data.liveUrl;
}
if (!url) return;
const flvPlayer = flvJs.createPlayer({
type: "flv",
url: url,
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;
}

166
ruoyi-ui/src/views/JiHeExpressway/components/VideoMulti/index.vue

@ -0,0 +1,166 @@
<template>
<div class="video-container">
<div class="header">
<ElSelect @change="showVideo" v-model="cameraId">
<ElOption v-for="item in urls"
:key="item.id" :label="item.deviceName" :value="item.iotDeviceId">
</ElOption>
</ElSelect>
</div>
<Transition name="fade" mode="out-in">
<video controls autoplay muted class="video-stream" v-bind="$attrs" ref="videoContainerRef" />
</Transition>
</div>
</template>
<script>
import { HttpLivePlayer, openLiveVideo } from "./videoStream.js"
import {
getNearCameraNew,
} from "@screen/pages/Home/components/RoadAndEvents/utils/httpList.js";
export default {
name: 'VideoControls',
components: {
},
props: {
//
pileNum: {
type: String,
default: null
},
rangeIndex: {
type: String,
default: null
},
showHeader: {
type: Boolean,
default: true
}
},
data() {
return {
active: "video",
player: null,
urls:[],
cameraId: null
}
},
async mounted() {
this.playVideo();
this.$once("hook:beforeDestroy", () => this.player?.destroy());
},
methods: {
async playVideo() {
this.player?.destroy();
let {code,data} = await getNearCameraNew(this.pileNum).catch(() => ({}));
if (
code != 200 ||
(Array.isArray(data) ? !data?.length : !Object.keys(data || {}).length)
) {
Message.warning("未获取到附近的相机信息");
return;
}
if(data[this.rangeIndex]){
this.urls = data[this.rangeIndex]
this.cameraId = data[this.rangeIndex][0]['iotDeviceId']
this.showVideo();
}
},
showVideo(){
this.player = new HttpLivePlayer(this.$refs.videoContainerRef, { camId: this.cameraId});
}
}
}
</script>
<style lang='scss' scoped>
.video-container {
position: relative;
background-color: #000;
height: 240px;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: center;
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.24s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
.header {
width: 100%;
z-index: 999;
display: flex;
justify-content: space-between;
.radio {
background: #265A70;
border-radius: 41px 41px 41px 41px;
overflow: hidden;
opacity: 1;
border: 1px solid #3DE8FF;
font-size: 12px;
// font-family: PingFang SC, PingFang SC;
font-weight: 400;
color: #FFFFFF;
line-height: 14px;
height: fit-content;
// -webkit-background-clip: text;
// -webkit-text-fill-color: transparent;
.active {
background-color: rgba(61, 232, 255, 1);
}
span {
background-color: rgba(38, 90, 112, 1);
padding: 4px 9px;
display: inline-block;
cursor: pointer;
&:hover {
background-color: rgba(61, 232, 255, 1);
}
}
}
.btn {
background: #265A70;
border-radius: 6px 6px 6px 6px;
opacity: 1;
border: 1px solid #3DE8FF;
font-size: 12px;
// font-family: PingFang SC, PingFang SC;
font-weight: 400;
color: #FFFFFF;
line-height: 14px;
padding: 3px 9px;
cursor: pointer;
// -webkit-background-clip: text;
// -webkit-text-fill-color: transparent;
}
}
.video-stream,
img {
height: 100%;
max-height: 100%;
max-width: 100%;
}
.video-stream {
width: 100%;
}
}
</style>

241
ruoyi-ui/src/views/JiHeExpressway/components/VideoMulti/videoStream.js

@ -0,0 +1,241 @@
import flvJs from "flv.js";
import mpegTsJs from "mpegts.js";
import { Message } from "element-ui";
// mpegts.js
import {
getCameraStream,
getNearCamera,
getNearCameraNew,
} from "@screen/pages/Home/components/RoadAndEvents/utils/httpList.js";
const ErrorTypesCn = {
NetworkError: "网络错误",
MediaError: "媒体错误",
OtherError: "其他错误",
};
/**
* flv 视频测试
* https://bilibili.github.io/flv.js/demo/
* https://www.zngg.net/tool/detail/FlvPlayer
* https://xqq.im/mpegts.js/demo/arib.html
*/
/**
* flv 视频流
*/
const testFlvUrl =
"https://sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/flv/xgplayer-demo-720p.flv";
// "https://v17.dogevideo.com/vcloud/17/v/20190424/1556036075_818c4125ec9c8cbc7a7a8a7cc1601512/1057/e51d88e79732fb952ebdbb4a57aa628a.flv?vkey=D9EAC2&tkey=17062414799ec7c466dc&auth_key=1706255879-zQK6JYToEeRiUark-0-6e363fb7709e783e64efc919d2267bdc";
/**
*
* @param {HTMLElement} container 容器
* @param {{camId?: string; url?: string}?} options {camId: 相机ID; url: 直播地址}
* @returns
*/
export async function openVideoStream(container, { camId, url } = {}) {
if (camId) {
const { code, data } = await getCameraStream(camId).catch(() => ({}));
if (code != 200) return;
url = data.liveUrl;
}
if (!url) return;
// console.log(flvJs.getFeatureList().mseLivePlayback);
const player = flvJs.createPlayer({
type: "flv",
url: url,
isLive: true,
hasVideo: true,
hasAudio: true,
});
player.attachMediaElement(container);
player.load();
player.play();
player.on(flvJs.Events.ERROR, (e) => {});
return player;
}
async function getUrl({ camId} = {}) {
const { code, data } = await getCameraStream(camId).catch(() => ({}));
if (code != 200) {
Message.warning("未获取到当前相机的播放地址");
return;
}
let url = data.liveUrl;
if (!url) {
Message.warning("未获取到当前相机的播放地址");
return Promise.reject("获取 url 失败!");
}
return url;
}
export class HttpLivePlayer {
/**
* @type { flvJs.Player }
*/
player;
/**
* @type { HTMLVideoElement }
*/
container;
url;
// 解码 帧
lastDecodedFrames;
constructor(container, options) {
this.container = container;
if (!flvJs.getFeatureList().mseLiveFlvPlayback)
return Message.error("浏览器不支持播放 flv 视频流");
getUrl(options).then((url) => {
this.url = url;
this.initLiveVideo();
});
}
destroy() {
if (!this.player) return;
this.player.pause();
this.player.unload();
this.player.detachMediaElement();
this.player.destroy();
this.player = null;
}
initLiveVideo() {
this.destroy();
this.player = null;
this.lastDecodedFrames = null;
if (!this.url) return;
this.player = flvJs.createPlayer(
{
type: "flv",
url: this.url,
isLive: true,
},
{
autoCleanupSourceBuffer: true,
// enableWorker: true, // 启用分离的线程进行转换
enableStashBuffer: false, // 关闭IO隐藏缓冲区 如果您需要实时(最小延迟)来进行实时流播放,则设置为false
stashInitialSize: 128,
isLive: true,
lazyLoad: true,
}
);
this.player.attachMediaElement(this.container);
this.player.load();
this.player.play();
// this.container.addEventListener("progress", () => {
// let end = this.player.buffered.end(0); //获取当前buffered值(缓冲区末尾)
// let delta = end - this.player.currentTime; //获取buffered与当前播放位置的差值
// // 延迟过大,通过跳帧的方式更新视频
// if (delta > 10 || delta < 0) {
// this.player.currentTime = this.player.buffered.end(0) - 1;
// return;
// }
// // 追帧
// if (delta > 1) {
// this.container.playbackRate = 1.1;
// } else {
// this.container.playbackRate = 1;
// }
// });
this.player.on(flvJs.Events.ERROR, (errorType, errorDetail, errorInfo) => {
console.log("errorType", errorType);
console.log("errorDetail", errorDetail);
console.log("errorInfo", errorInfo);
Message.warning(
`视频流加载失败, ${ErrorTypesCn[errorType] || "其他错误"}`
);
let self = this;
setTimeout(() => {
self.initLiveVideo();
}, 300000);
});
// 视频断流
this.player.on(flvJs.Events.STATISTICS_INFO, (res) => {
if (this.lastDecodedFrames != res.decodedFrames) {
this.lastDecodedFrames = res.decodedFrames;
} else {
this.lastDecodedFrames = 0;
// this.destroy();
// this.initLiveVideo();
}
});
}
}
/**
* https://juejin.cn/post/6855577308271476743
* https://www.cnblogs.com/xiahj/p/flvExtend.html
* 使用 mpegTsJs
* @param {*} container
* @param {*} options
*/
export async function openLiveVideo(container, options) {
if (!mpegTsJs.getFeatureList().mseLivePlayback)
return Message.error("浏览器不支持播放 flv 视频流");
const url = await getUrl(options).catch(() => {});
console.log(
"%c [ url ]-212-「videoStream.js」",
"font-size:15px; background:#6f87e8; color:#b3cbff;",
url,
options
);
if (!url) return;
const player = mpegTsJs.createPlayer(
{
type: "flv",
url,
isLive: true,
},
{
autoCleanupSourceBuffer: true,
enableWorker: true, // 启用分离的线程进行转换
// enableStashBuffer: false, // 关闭IO隐藏缓冲区 如果您需要实时(最小延迟)来进行实时流播放,则设置为false
// stashInitialSize: 128,
isLive: true,
lazyLoad: true,
}
);
player.attachMediaElement(container);
container.addEventListener("play", () => {
try {
let end = player.buffered.end(0) - 1;
player.currentTime = end;
} catch (error) {}
});
player.load();
player.play();
return player;
}

18
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/AMapContainer/index.vue

@ -53,6 +53,16 @@ export default {
Vue.prototype.mapIns = this.aMapIns;
//
this.trafficLayer = new this.AMap.TileLayer.Traffic({
zIndex: 99999999999,
zooms: [7, 22],
visible: false,
});
this.trafficLayer.setMap(this.aMapIns);
this.trafficLayer.hide();
this.aMapIns.on("complete", () => { //map
this.loading = false;
this.$emit("update:isGisCompleted", true);
@ -211,6 +221,14 @@ export default {
mapIns: this.aMapIns,
};
},
showRoadConditions(visible){
if(visible){
this.trafficLayer.show();
} else {
this.trafficLayer.hide();
}
}
},
// unmounted() {
beforeDestroy() {

221
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Dialogs/Broadcast/components/BroadcastParam.vue

@ -0,0 +1,221 @@
<template>
<div class="body">
<div v-if="!isMultiControl" class="left">
<div class="title">路测广播列表</div>
<CheckboxGroup class="checkbox-group" gap="9px" :showIcon="true" v-model="checkList" :options="musicList"
id="otherConfig" label="deviceName">
<template #[otherConfig]="{ data }">
<span style="color: #6ee5fe"> {{ data.deviceName }}(当前) </span>
</template>
</CheckboxGroup>
</div>
<div class="right" :style="{ width: isMultiControl ? '100%' : undefined }">
<div class="top-content">
<Video v-if="!isMultiControl" class="item-video" :pileNum="pileNum" />
<label>发布内容: </label>
<ElInput type="textarea" v-model="releaseMessage" :autosize="{ minRows: 3, maxRows: 3 }" :maxlength="150"
showWordLimit placeholder="请输入发布内容" />
</div>
<div class="footer">
<Button style="background-color: rgba(0, 179, 204, 0.3)"
@click.native="(modelVisible = false), (submitting = false)">
取消
</Button>
<Button @click.native="handleSubmit" :loading="submitting">
确定
</Button>
</div>
</div>
</div>
</template>
<script>
import Dialog from "@screen/components/Dialog/index.vue";
import Button from "@screen/components/Buttons/Button.vue";
import Video from "@screen/components/Video";
import CheckboxGroup from "@screen/components/FormConfig/components/ElCheckboxGroup.vue";
import request from "@/utils/request";
import { getDeviceList } from "@screen/pages/Home/components/RoadAndEvents/utils/httpList.js";
import { Message } from "element-ui";
export default {
name: "BroadcastReleases",
components: {
Dialog,
Button,
Video,
CheckboxGroup,
},
model: {
prop: "visible",
event: "update:value",
},
props: {
visible: Boolean,
pileNum: String,
otherConfig: String,
isMultiControl: Boolean
},
data() {
return {
submitting: false,
checkList: [],
releaseMessage: null,
musicList: [],
};
},
computed: {
modelVisible: {
get() {
return this.visible;
},
set(val) {
this.$emit("update:value", val);
},
},
},
created() {
getDeviceList(5).then((data) => {
if (Array.isArray(data))
this.musicList = data.map((item) => ({
...item,
disabled: item.deviceState != 1,
}));
});
},
methods: {
handleSubmit() {
if (!this.releaseMessage?.trim())
return Message.error("发布内容不能为空!");
if (!this.checkList.length)
return Message.error("请至少选择一个广播设备!");
this.submitting = true;
request({
url: `/broadcast/broadcastFunctionCall`,
method: "post",
data: {
name: "task-3",
outVol: "6",
priority: "1",
text: this.releaseMessage.trim(),
repeatTimes: "3",
termList: this.checkList.map((str) => JSON.parse(str)),
functionType: "startPaTts",
},
})
.then((data) => {
// console.log(data);
if (data.retCode == "0") {
Message.success("广播设置成功!");
this.modelVisible = false;
} else {
Message.error("广播设置失败!");
}
})
.finally(() => {
this.submitting = false;
});
},
},
};
</script>
<style lang="scss" scoped>
.body {
display: flex;
gap: 9px;
height: 100%;
.left {
width: 251px;
border-radius: 3px 3px 3px 3px;
border: 1px solid #3de8ff;
padding: 3px 12px;
display: flex;
flex-direction: column;
gap: 9px;
::v-deep .el-checkbox__label {
display: flex !important;
justify-content: space-between;
width: 100%;
.title {
flex: 1;
width: 0;
overflow: hidden;
text-overflow: ellipsis;
word-break: keep-all;
}
.state {
width: 18px;
height: 18px;
margin-right: 4px;
}
.huiduButton {
background: transparent;
border: none;
height: 18px;
width: 18px;
line-height: 20px;
padding: 0;
color: #fff;
font-size: 16px;
}
.huiduButton:hover {
color: #05afe3 !important;
}
}
.checkbox-group {
display: flex;
flex-direction: column;
gap: 6px;
overflow: auto;
height: 100%;
flex: 1;
flex-wrap: nowrap;
}
}
.right {
display: flex;
flex-direction: column;
justify-content: space-between;
.top-content {
display: flex;
flex-direction: column;
gap: 9px;
.item-video {
width: 545px;
}
label {
font-size: 16px;
font-family: PingFang SC, PingFang SC;
font-weight: 400;
color: #3de8ff;
line-height: 19px;
}
}
}
}
.footer {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 9px;
}
</style>

71
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Dialogs/Broadcast/components/BroadcastReleases.vue

@ -1,51 +1,37 @@
<template>
<Dialog v-model="modelVisible" title="广播发布" width="850px">
<div class="BroadcastReleases">
<div class="body">
<BroadcastParam v-model="modelVisible" :pileNum="pileNum" :otherConfig="otherConfig" />
<!-- <div class="body">
<div class="left">
<div class="title">路测广播列表</div>
<CheckboxGroup
class="checkbox-group"
gap="9px"
:showIcon="true"
v-model="checkList"
:options="musicList"
id="otherConfig"
label="deviceName"
>
<CheckboxGroup class="checkbox-group" gap="9px" :showIcon="true" v-model="checkList" :options="musicList"
id="otherConfig" label="deviceName">
<template #[otherConfig]="{ data }">
<span style="color: #6ee5fe"> {{ data.deviceName }}(当前) </span>
</template>
</CheckboxGroup>
</div>
<div class="right">
<div class="top-content">
<Video class="item-video" :pileNum="pileNum" />
<label>发布内容: </label>
<ElInput
type="textarea"
v-model="releaseMessage"
:autosize="{ minRows: 3, maxRows: 3 }"
:maxlength="150"
showWordLimit
placeholder="请输入发布内容"
/>
</div>
<div class="footer">
<Button
style="background-color: rgba(0, 179, 204, 0.3)"
@click.native="(modelVisible = false), (submitting = false)"
>
取消
</Button>
<Button @click.native="handleSubmit" :loading="submitting">
确定
</Button>
</div>
</div>
</div>
</CheckboxGroup>
</div>
<div class="right">
<div class="top-content">
<Video class="item-video" :pileNum="pileNum" />
<label>发布内容: </label>
<ElInput type="textarea" v-model="releaseMessage" :autosize="{ minRows: 3, maxRows: 3 }" :maxlength="150"
showWordLimit placeholder="请输入发布内容" />
</div>
<div class="footer">
<Button style="background-color: rgba(0, 179, 204, 0.3)"
@click.native="(modelVisible = false), (submitting = false)">
取消
</Button>
<Button @click.native="handleSubmit" :loading="submitting">
确定
</Button>
</div>
</div>
</div> -->
</div>
</Dialog>
</template>
@ -55,7 +41,7 @@ import Dialog from "@screen/components/Dialog/index.vue";
import Button from "@screen/components/Buttons/Button.vue";
import Video from "@screen/components/Video";
import CheckboxGroup from "@screen/components/FormConfig/components/ElCheckboxGroup.vue";
import BroadcastParam from "./BroadcastParam.vue";
import request from "@/utils/request";
import { getDeviceList } from "@screen/pages/Home/components/RoadAndEvents/utils/httpList.js";
@ -68,6 +54,7 @@ export default {
Button,
Video,
CheckboxGroup,
BroadcastParam
},
model: {
prop: "visible",
@ -75,9 +62,7 @@ export default {
},
props: {
visible: Boolean,
deviceId: String,
pileNum: String,
currentId: [String, Number],
otherConfig: String,
},
data() {

18
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Dialogs/Broadcast/index.vue

@ -10,11 +10,7 @@
</ElTabPane>
<!-- <ElTabPane label="设备参数" name="second">设备参数</ElTabPane> -->
<ElTabPane label="在线率统计" name="third">
<LineChart
v-if="activeName === 'third'"
:productId="dialogData.id"
style="height: 150px"
/>
<LineChart v-if="activeName === 'third'" :productId="dialogData.id" style="height: 150px" />
</ElTabPane>
</ElTabs>
@ -23,13 +19,7 @@
</div>
</div>
<BroadcastReleases
v-model="releaseVisible"
:deviceId="dialogData.iotDeviceId"
:pileNum="dialogData.stakeMark"
:otherConfig="dialogData.otherConfig"
:currentId="dialogData.id"
/>
<BroadcastReleases v-model="releaseVisible" :pileNum="dialogData.stakeMark" :otherConfig="dialogData.otherConfig" />
</Dialog>
</template>
@ -131,7 +121,7 @@ export default {
if (roadInfo) this.data.roadName = roadInfo.roadName;
},
methods: {
handleClickTabs() {},
handleClickTabs() { },
},
};
</script>
@ -171,7 +161,7 @@ export default {
align-items: center;
justify-content: end;
> div {
>div {
font-size: 16px;
padding: 6px 12px;
}

184
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Dialogs/FatigueWakesUp/components/DeviceControlDialog.vue

@ -3,133 +3,78 @@
<div class="DeviceControlDialog">
<ElTabs v-model="activeName" class="tabs" @tab-click="tabClick">
<ElTabPane label="一般模式" name="first">
<Form
v-model="formData"
class="form"
ref="FormConfigRef"
:formList="formList1"
column="1"
labelWidth="120px"
/>
<Form v-model="formData" class="form" ref="FormConfigRef" :formList="formList1" column="1"
labelWidth="120px" />
</ElTabPane>
<ElTabPane label="自定义模式" name="second">
<div style="display: flex; margin: 20px 0">
<!-- <div style="display: flex; margin: 20px 0">
<p style="width: 115px">工作时长():</p>
<el-input-number
v-model="onWorkStatus2"
:min="0"
:max="999"
label="工作时长(s分):"
></el-input-number>
</div>
<el-input-number v-model="onWorkStatus2" :min="0" :max="999" label="工作时长(s分):"></el-input-number>
</div> -->
<Table :data="tableData">
<ElTableColumn prop="ds" label="段数"></ElTableColumn>
<ElTableColumn prop="time" width="120" label="时间(毫秒)">
<template slot-scope="scope">
<div style="display: flex">
<el-input-number
style="width: 120px"
v-model="scope.row.time"
:min="0"
></el-input-number>
<el-input-number style="width: 120px" v-model="scope.row.time" :min="0"></el-input-number>
</div>
</template>
</ElTableColumn>
<ElTableColumn prop="A" label="线路A">
<template slot-scope="scope">
<el-switch
active-value="1"
inactive-value="0"
v-model="scope.row.A"
active-color="#13ce66"
inactive-color="#C9C9C9"
>
<el-switch active-value="1" inactive-value="0" v-model="scope.row.A" active-color="#13ce66"
inactive-color="#C9C9C9">
</el-switch>
</template>
</ElTableColumn>
<ElTableColumn prop="B" label="线路B">
<template slot-scope="scope">
<el-switch
active-value="1"
inactive-value="0"
v-model="scope.row.B"
active-color="#13ce66"
inactive-color="#C9C9C9"
>
<el-switch active-value="1" inactive-value="0" v-model="scope.row.B" active-color="#13ce66"
inactive-color="#C9C9C9">
</el-switch>
</template>
</ElTableColumn>
<ElTableColumn prop="C" label="线路C">
<template slot-scope="scope">
<el-switch
active-value="1"
inactive-value="0"
v-model="scope.row.C"
active-color="#13ce66"
inactive-color="#C9C9C9"
>
<el-switch active-value="1" inactive-value="0" v-model="scope.row.C" active-color="#13ce66"
inactive-color="#C9C9C9">
</el-switch>
</template>
</ElTableColumn>
<ElTableColumn prop="D" label="线路D">
<template slot-scope="scope">
<el-switch
active-value="1"
inactive-value="0"
v-model="scope.row.D"
active-color="#13ce66"
inactive-color="#C9C9C9"
>
<el-switch active-value="1" inactive-value="0" v-model="scope.row.D" active-color="#13ce66"
inactive-color="#C9C9C9">
</el-switch>
</template>
</ElTableColumn>
<ElTableColumn prop="E" label="线路E">
<template slot-scope="scope">
<el-switch
active-value="1"
inactive-value="0"
v-model="scope.row.E"
active-color="#13ce66"
inactive-color="#C9C9C9"
>
<el-switch active-value="1" inactive-value="0" v-model="scope.row.E" active-color="#13ce66"
inactive-color="#C9C9C9">
</el-switch>
</template>
</ElTableColumn>
<ElTableColumn prop="F" label="线路F">
<template slot-scope="scope">
<el-switch
active-value="1"
inactive-value="0"
v-model="scope.row.F"
active-color="#13ce66"
inactive-color="#C9C9C9"
>
<el-switch active-value="1" inactive-value="0" v-model="scope.row.F" active-color="#13ce66"
inactive-color="#C9C9C9">
</el-switch>
</template>
</ElTableColumn>
<ElTableColumn prop="G" label="线路G">
<template slot-scope="scope">
<el-switch
active-value="1"
inactive-value="0"
v-model="scope.row.G"
active-color="#13ce66"
inactive-color="#C9C9C9"
>
<el-switch active-value="1" inactive-value="0" v-model="scope.row.G" active-color="#13ce66"
inactive-color="#C9C9C9">
</el-switch>
</template>
</ElTableColumn>
<ElTableColumn prop="H" label="线路H">
<template slot-scope="scope">
<el-switch
active-value="1"
inactive-value="0"
v-model="scope.row.H"
active-color="#13ce66"
inactive-color="#C9C9C9"
>
<el-switch active-value="1" inactive-value="0" v-model="scope.row.H" active-color="#13ce66"
inactive-color="#C9C9C9">
</el-switch>
</template>
</ElTableColumn>
@ -139,10 +84,8 @@
</div>
<template #footer>
<Button
style="background-color: rgba(0, 179, 204, 0.3)"
@click.native="(modelVisible = false), (submitting = false)"
>
<Button style="background-color: rgba(0, 179, 204, 0.3)"
@click.native="(modelVisible = false), (submitting = false)">
取消
</Button>
<Button @click.native="handleSubmit" :loading="submitting"> 确定 </Button>
@ -173,6 +116,7 @@ export default {
props: {
visible: Boolean,
deviceId: String,
productId: String
},
data() {
return {
@ -252,6 +196,10 @@ export default {
value: "6",
label: "SOS模式",
},
{
value: "7",
label: "自定义模式",
},
],
},
},
@ -341,16 +289,16 @@ export default {
async initData() {
//
let result = await this.requestURL("ASKMD");
if (result.data == 7) {
this.activeName = "second";
this.tabClick();
} else {
this.formData.controlType = result.data + "" || "1";
//
let resultTime = await this.requestURL("ASKTM");
this.formData.onWorkStatus = resultTime.data || 0;
this.activeName = "first";
}
// if (result.data == 7) {
// this.activeName = "second";
// this.tabClick();
// } else {
this.formData.controlType = result.data + "" || "1";
//
let resultTime = await this.requestURL("ASKTM");
this.formData.onWorkStatus = resultTime.data || 0;
this.activeName = "first";
// }
},
async requestURL(functionId, options = {}) {
let result = await request({
@ -384,6 +332,7 @@ export default {
tData.push({ ...data, ds: item.property });
}
});
tData.sort((a, b) => a.ds.toUpperCase().localeCompare(b.ds.toUpperCase()));
this.tableData = tData;
} else {
//
@ -415,36 +364,51 @@ export default {
this.tableData.forEach((it, index) => {
rData.push({
order: 7,
time: it.time,
numberOfSegments: this.duan[index],
A: Number(it.A),
B: Number(it.B),
C: Number(it.C),
D: Number(it.D),
E: Number(it.E),
F: Number(it.F),
G: Number(it.G),
H: Number(it.H),
functionId: "SETDF",
params: {
order: 7,
time: it.time,
numberOfSegments: it.ds || this.duan[index],
A: Number(it.A),
B: Number(it.B),
C: Number(it.C),
D: Number(it.D),
E: Number(it.E),
F: Number(it.F),
G: Number(it.G),
H: Number(it.H),
}
});
});
//
await request({
url: `/business/device/batchLaserFatigueInvokedFunction`,
let res = await request({
url: `/business/device/batchFunctions`,
method: "post",
// data: {
// deviceId: this.deviceId,
// functionId: "SETDF",
// params: rData,
// },
data: {
deviceId: this.deviceId,
functionId: "SETDF",
params: rData,
},
devices: [{
iotDeviceId: this.deviceId,
id: this.productId,
deviceType: 10
}],
functions: rData
}
});
//
let res = await this.requestURL("SETTM", { SET: this.onWorkStatus2 });
if (res.code == 200) {
Message.success("设置成功!");
this.$emit("update:value", false);
}
//
// let res = await this.requestURL("SETTM", { SET: this.onWorkStatus2 });
// if (res.code == 200) {
// Message.success("");
// this.$emit("update:value", false);
// }
}
},
},

1
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Dialogs/FatigueWakesUp/index.vue

@ -29,6 +29,7 @@
<DeviceControlDialog
v-model="deviceControlVisible"
:deviceId="dialogData.iotDeviceId"
:productId="dialogData.id"
/>
</Dialog>
</template>

5
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Dialogs/PerceiveEvent/index.vue

@ -3,8 +3,9 @@
<div class="PerceiveEvent">
<!-- <Video class="item-video" /> -->
<div class="video-pic">
<Video style="height: 100%; flex: 1" :showHeader="false" :url="dialogData.formData.videoList[0]"
videoType="mp4" />
<!-- <Video style="height: 100%; flex: 1" :showHeader="false" :url="dialogData.formData.videoList[0]"
videoType="mp4" /> -->
<Carousel style="flex: 1; height: 100%;max-width: 350px;" :videos="dialogData.formData.videoList" />
<Carousel style="flex: 1; height: 100%;max-width: 300px;" :pictures="dialogData.formData.pictures" />
</div>
<LineChart class="chart" />

201
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Dialogs/SmartDevice/components/DeviceParamsMulti.vue

@ -0,0 +1,201 @@
<template>
<div class='DeviceParams'>
<div class="no-data" v-if="!devicesList.length" v-loading="secondLoading">暂无设备参数</div>
<Descriptions :list="devicesList" style="gap: 18px;" column="5">
<template v-for="item in devicesList.slice(0, -1)" #[`content-${getSlotKey(item.key)}`]="{ data }">
<span>{{ data.text }}</span>
<Switcher v-if="!disabled" class="switcher" :activeOption="activeOption" :value="data.state"
@change="(value) => handleSwitcherChange(value, data)" />
<ElTag style="margin-left: 20px;" v-else effect="dark" :type="data.state ? '' : 'info'">{{ data.state ? '开' :
'关' }}
</ElTag>
</template>
</Descriptions>
</div>
</template>
<script>
import Descriptions from '@screen/components/Descriptions.vue';
import Switcher from '@screen/pages/service/PublishingChannelManagement/components/Switcher.vue';
import request from "@/utils/request";
import { Message } from 'element-ui';
import { confirm } from "@screen/utils/common";
export default {
name: 'DeviceParams',
components: {
Descriptions,
Switcher
},
props: {
dialogData: {
type: Object,
default: () => ({})
},
disabled: Boolean,
isMultiControl: Boolean
},
data() {
return {
secondLoading: true,
devicesList: [],
activeOption: {
active: {
text: "开"
},
unActive: {
text: "关"
}
}
}
},
created() {
Promise.all([this.getAc(), this.getDc()]).then(res => {
// if (result.code != 200) return;
let ac = res[0].data;
let dc = res[1].data;
let deviceInfo = _.merge({}, ac, dc);
console.log(deviceInfo, "deviceInfo11")
const typeMap = {
ac: '220v',
dc: '12v',
}
for (const key in deviceInfo.formatValue) {
// electricity
// voltage
if (key.includes('electricity')) {
const args = key.match(/[a-z]+|[0-9]+$/g);
const type = args[0], num = args.slice(-1)[0], prefix = args.slice(0, 2).join('_');
// console.log(type , num , prefix , "+++=========="); //dc 2 dc_out
this.devicesList.push(
{
label: `支路${num}${typeMap[type]}) 电压`,
key: `${prefix}_voltage_${num}`,
text: deviceInfo.formatValue[`${prefix}_voltage_${num}`],
gridColumn: 3
},
{
label: '电流',
key: `${prefix}_electricity_${num}`,
text: deviceInfo.formatValue[key],
gridColumn: 2,
state: deviceInfo.value[key] > 0
}
);
}
}
this.devicesList.push(
{
label: '风扇',
key: `fan_status`,
// key: `aa_electricity_1`,
text: { 0: '正常', 1: '开' }[deviceInfo.formatValue['fan_status']] || '-',
gridColumn: 2,
state: (deviceInfo.formatValue['fan_status'] === '0')
},
{
label: '温度',
key: `temperature`,
text: deviceInfo.formatValue['temperature'] ? `${deviceInfo.formatValue['temperature']} °C` : '-',
gridColumn: 2
},
{
label: '箱门',
key: `door_status`,
text: { 0: '关闭', 1: '打开' }[deviceInfo.formatValue['door_status']] || '-',
gridColumn: 1
},
{
label: '湿度',
key: `humidity`,
text: deviceInfo.formatValue['humidity'] ? `${deviceInfo.formatValue['humidity']} %` : '-',
gridColumn: 2
},
{
label: '市电掉电',
key: `power_status`,
text: { 0: '正常', 1: '掉电' }[deviceInfo.formatValue['power_status']] || '-',
gridColumn: 2
},
)
// this.data = result.rows;
// this.total = result.total;
})
.finally(() => {
this.secondLoading = false
})
},
methods: {
getAc() {
return request({
url: `/business/device/properties/latest/${this.dialogData.iotDeviceId || '10.0.36.143-1883'}/1ac`,
method: "get",
params: {}
})
},
getDc() {
return request({
url: `/business/device/properties/latest/${this.dialogData.iotDeviceId || '10.0.36.143-1883'}/1dc`,
method: "get",
params: {}
})
},
async handleSwitcherChange(value, data) {
let str = data.state ? "关闭" : "开启";
let deviceName = "";
if (data.key.includes("fan")) {
str += "风扇?";
deviceName = "fan_out_en";
} else {
str += "该支路?"
deviceName = data.key.match(/^[a-z]+_out|[0-9]+/g).join("_") + "_en"; //dc_out_2_en ac_out_2_en;
}
data.state = value;
const isContinue = await confirm({ message: `${str}` })
.catch(() => {
console.log(data.state, value, 333)
data.state = !value;
});
if (!isContinue) return;
// https://www.yuque.com/dayuanzhong-ovjwn/gkht0m/ww776d5kzs72ilzh?singleDoc=
request({
url: `/business/device/functions/${this.dialogData.iotDeviceId}/${102}`,
method: "POST",
data: {
deviceName,
// 1=0=
value: value ? 1 : 0
}
})
.then(result => {
if (result.code != 200) {
Message.error("操作失败");
data.state = !value;
return;
};
Message.success("操作成功");
})
.catch(() => {
data.state = !value;
Message.error("操作失败");
})
},
getSlotKey(key) {
return key.includes('electricity') || key.includes('fan') ? key : ''
}
}
}
</script>
<style lang='scss' scoped>
.DeviceParams {
height: 100%;
}
</style>

38
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Dialogs/TrafficIncidents/index.vue

@ -1,32 +1,12 @@
<template>
<Dialog
v-model="obverseVisible"
:title="dialogData._itemData && dialogData._itemData.title"
width="650px"
>
<Dialog v-model="obverseVisible" :title="dialogData._itemData && dialogData._itemData.title" width="650px">
<div class="TrafficIncidents">
<div class="header">
<Video
class="video-stream"
:pileNum="dialogData.stakeMark"
rangeIndex="upCamera"
:showHeader="false"
/>
<Video
class="video-stream"
:pileNum="dialogData.stakeMark"
rangeIndex="downCamera"
:showHeader="false"
/>
<Video class="video-stream" :pileNum="dialogData.stakeMark" rangeIndex="upCamera" :showHeader="false" />
<Video class="video-stream" :pileNum="dialogData.stakeMark" rangeIndex="downCamera" :showHeader="false" />
</div>
<Descriptions
labelWidth="72px"
:list="list"
:data="data"
style="gap: 18px"
column="7"
/>
<Descriptions labelWidth="72px" :list="list" :data="data" style="gap: 18px" column="7" />
</div>
</Dialog>
</template>
@ -35,7 +15,7 @@
import Dialog from "@screen/components/Dialog/index.vue";
import Descriptions from "@screen/components/Descriptions.vue";
// import { getRoadInfoByStakeMark, getProduct } from "@screen/pages/Home/components/RoadAndEvents/utils/httpList.js"
import Video from "@screen/components/Video";
import Video from "@screen/components/VideoMulti";
import request from "@/utils/request";
import { dialogDelayVisible } from "./../mixin";
@ -141,10 +121,10 @@ export default {
this.data = { ...data, ...this.data };
console.log("trafficIncidents", data);
})
.catch((err) => {});
.catch((err) => { });
},
methods: {
handleClickTabs() {},
handleClickTabs() { },
},
};
</script>
@ -165,7 +145,7 @@ export default {
display: flex;
gap: 9px;
> div.video-stream {
>div.video-stream {
height: 210px;
}
}
@ -193,7 +173,7 @@ export default {
align-items: center;
justify-content: end;
> div {
>div {
font-size: 16px;
padding: 6px 12px;
}

1
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/HomeFilter/index.vue

@ -90,7 +90,6 @@ export default {
watch: {
activeDeviceTypes: {
handler(val) {
console.log(val, this.activeDeviceTypes, 'xxx')
const options = []
for (let id of DeviceTypeIds) {
if (this.activeDeviceTypes.indexOf(`路测设备_${id}`) >= 0) {

105
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/HomeFrameControl/index.vue

@ -12,7 +12,9 @@
<span class="close" @click="() => { this.activeIcon = null; }">
<i class="el-icon-close" />
</span>
<Form labelWidth="90px" column="2" class="form" ref="FormConfigRef" :formList="formList" />
<Form v-model="data" labelWidth="90px" column="2" class="form" ref="FormConfigRef" :formList="formList" />
<component :is="componentMap[DeviceTopics[data.deviceType]]" :isMultiControl="true"></component>
<!-- -->
<div class="footer">
<Button @click.native="">
确认
@ -35,12 +37,19 @@ import { ChildTypes } from "@screen/utils/enum.js"
import { getDeviceList } from "@screen/pages/Home/components/RoadAndEvents/utils/httpList.js";
import { delay } from "@screen/utils/common";
import { initSearch } from "@screen/utils/enum/common.js"
import DeviceParams from "@screen/pages/Home/components/Dialogs/SmartDevice/components/DeviceParams.vue";
import BroadcastParam from "@screen/pages/Home/components/Dialogs/Broadcast/components/BroadcastParam.vue";
import Vue from "vue";
import { DeviceForMap } from "@screen/pages/Home/components/RoadAndEvents/utils/buttonEvent"
const DeviceTopics = {}
const componentMap = { "语音广播": "BroadcastParam" } //DeviceTopics[deviceType]
//"": undefined, "": undefined, "": undefined, "": undefined,"": "DeviceParams",
const controlMulti = Object.keys(componentMap);//6
const DeviceTopics = {};//6 {key:label}
Object.keys(DeviceForMap).forEach(DeviceLabel => {
DeviceTopics[DeviceForMap[DeviceLabel].deviceType] = DeviceLabel;
controlMulti.indexOf(DeviceLabel) !== -1 && (DeviceTopics[DeviceForMap[DeviceLabel].deviceType] = DeviceLabel);
});
async function setDeviceOptions(config, filterData, formList) {
const data = await getDeviceList(config.deviceType, filterData).then(async (data) => {
@ -48,7 +57,7 @@ async function setDeviceOptions(config, filterData, formList) {
return data;
});
formList[4].options.options = data.map(item => {
return { label: `${item.deviceName}${item.stakeMark}`, value: JSON.stringify({ id: item.id, iotDeviceId: item.iotDeviceId }) }
return { label: `${item.deviceName}`, value: JSON.stringify({ id: item.id, iotDeviceId: item.iotDeviceId }), disabled: item.deviceState != 1 }
})
};
function changeHandle(data, formList) {
@ -57,16 +66,20 @@ function changeHandle(data, formList) {
data.startStakeMark && (filterData.startStakeMark = data.startStakeMark);
data.endStakeMark && (filterData.endStakeMark = data.endStakeMark);
setDeviceOptions({ deviceType: data.deviceType }, filterData, formList);
data.childType = undefined;
}
export default {
name: "HomeFrameControl",//
components: {
Button,
Form,
DeviceParams,
BroadcastParam
},
data() {
return {
activeIcon: null,
data: {},
formList: [
{
label: "设备类型:",
@ -74,12 +87,14 @@ export default {
type: "select",
options: {
clearable: true,
options: [],
options: Object.keys(DeviceTopics).map(key => { return { label: DeviceTopics[key], value: key } }),
},
ons: { //on element
change(value, ...args) {
const { data, formList } = args.slice(-1)[0]; //data formList
data.deviceType && changeHandle(data, formList);
console.log(Vue, this)
if (data.deviceType) changeHandle(data, formList);
else data.childType = undefined;
}
},
},
@ -211,54 +226,54 @@ export default {
options: {
clearable: true,
options: [],
multiple: true,
["collapse-tags"]: true
},
visible: data => {
return true;
},
},
{
label: "控制操作:",
key: "controlOp",
type: "select",
default: null,
options: {
clearable: true,
options: [{
label: "在线",
value: "1"
}, {
label: "离线",
value: "0"
}]
},
visible: data => {
// if (find(this.activeDeviceTypes, (type => type.match("_"))))
// return true;
},
},
]
// {
// label: ":",
// key: "controlOp",
// type: "select",
// default: null,
// options: {
// clearable: true,
// options: [{
// label: "线",
// value: "1"
// }, {
// label: "线",
// value: "0"
// }]
// },
// },
],
DeviceTopics,
componentMap
}
},
inject: ["activeDeviceTypes"],
watch: {
activeDeviceTypes: {
handler(val) {
const activeTopicOptions = []
this.activeDeviceTypes.filter(activeDeviceType => {
const match = activeDeviceType.match(/路测设备_(\d+)/);
if (match) {
const deviceType = match[1];
activeTopicOptions.push({ label: DeviceTopics[deviceType], value: deviceType });
// inject: ["activeDeviceTypes"],
// watch: {
// activeDeviceTypes: {
// handler(val) {
// const activeTopicOptions = []
// this.activeDeviceTypes.filter(activeDeviceType => {
// const match = activeDeviceType.match(/_(\d+)/);
// if (match) {
// const deviceType = match[1];
// DeviceTopics[deviceType] && activeTopicOptions.push({ label: DeviceTopics[deviceType], value: deviceType });
}
});
this.formList[0].options.options = activeTopicOptions;
// }
// });
// this.formList[0].options.options = activeTopicOptions;
},
immediate: true,
deep: true
}
},
// },
// immediate: true,
// deep: true
// }
// },
methods: {
cancelClick() {
this.activeIcon = null;

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

@ -246,7 +246,6 @@ export default {
this.emitter.emit("selectedCompleted", item);
return this.$emit("onClickItem", item);
}
console.log(`${key}${status ? "_close" : ""}`, this.filterData, isDefault)
await eventMap[`${key}${status ? "_close" : ""}`]?.call(this, item, this.filterData, isDefault, this.cb);
},

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

@ -1,13 +1,9 @@
<template>
<div class="Home">
<AMapContainer
ref="AMapContainerRef"
@update:isGisCompleted="
(data) => {
this.isGisCompleted = data;
}
"
/>
<AMapContainer ref="AMapContainerRef" @update:isGisCompleted="(data) => {
this.isGisCompleted = data;
}
" />
<section class="content">
<!-- 左侧 -->
<div class="content-l">
@ -15,11 +11,7 @@
<FocusedMonitoring class="card-menu" />
<!-- -->
<RoadAndEvents
ref="RoadAndEventsRef"
:isGisCompleted="isGisCompleted"
class="content-l-b card-menu"
/>
<RoadAndEvents ref="RoadAndEventsRef" :isGisCompleted="isGisCompleted" class="content-l-b card-menu" />
</div>
<!-- 右侧 -->
@ -39,11 +31,7 @@
<footer class="footer card-menu">
<div class="footer-title">
<span>济菏高速缩略图</span>
<i
class="el-icon-s-fold"
style="color: #2ad9fd; transform: rotate(-90deg)"
@click.stop="handleShrink"
/>
<i class="el-icon-s-fold" style="color: #2ad9fd; transform: rotate(-90deg)" @click.stop="handleShrink" />
</div>
<div class="footer-content">
<Thumbnail />
@ -147,7 +135,7 @@ export default {
height: min-content;
pointer-events: none;
> div {
>div {
pointer-events: auto;
}
@ -161,7 +149,7 @@ export default {
overflow: hidden;
height: 100%;
> div {
>div {
pointer-events: all;
flex: 1;
}
@ -202,7 +190,7 @@ export default {
will-change: height;
bottom: 0;
> div {
>div {
pointer-events: all;
}

5
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/businessDataManagement/components/PeopleCard/index.vue

@ -42,6 +42,10 @@ export default {
key: "name",
label: "姓名"
},
{
key: "type",
label: "类型"
},
{
key: "postName",
label: "岗位"
@ -72,6 +76,7 @@ export default {
display: flex;
flex-direction: column;
gap: 6px;
font-size: 14px;
}
.btns {

26
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/businessDataManagement/views/firstResponders/components/AddNEditDialog.vue

@ -7,7 +7,7 @@
ref="FormConfigRef"
:formList="formList"
column="1"
labelWidth="90px"
labelWidth="100px"
/>
</div>
@ -68,6 +68,24 @@ export default {
},
],
},
{
label: "人员类型:",
key: "employeesType",
required: true,
type: "select",
options: {
options: [
{
key: 1,
label: "路管人员",
},
{
key: 2,
label: "应急人员",
},
],
},
},
{
label: "岗位:",
key: "postId",
@ -127,7 +145,7 @@ export default {
getSelectOptionsStation(2),
]).then(([post, organization]) => {
if (post.status === "fulfilled" && post.value.code == 200) {
this.formList[2].options.options = post.value.data.map((item) => ({
this.formList[3].options.options = post.value.data.map((item) => ({
key: item.postId + "",
label: item.postName,
}));
@ -137,7 +155,7 @@ export default {
organization.status === "fulfilled" &&
Array.isArray(organization.value)
) {
this.formList[3].options.options = organization.value;
this.formList[4].options.options = organization.value;
}
});
},
@ -181,7 +199,7 @@ export default {
<style lang="scss" scoped>
.AddNEditDialog {
width: 450px;
height: 150px;
height: 200px;
display: flex;
flex-direction: column;
gap: 15px;

2
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/businessDataManagement/views/firstResponders/index.vue

@ -203,7 +203,7 @@ export default {
grid-gap: 9px;
// grid-auto-flow: row;
justify-content: space-between;
grid-auto-rows: minmax(270px, max-content);
grid-auto-rows: minmax(280px, max-content);
position: relative;
}
}

83
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/CrowdnessIndicatorRankings/charts.js

@ -0,0 +1,83 @@
import * as echarts from "echarts";
export const StatChartOption = {
width: "100%",
height: "100%",
color: ["#2AD9FD"],
xAxis: {
name: "日",
type: "category",
// boundaryGap: ["15%", "15%"],
nameTextStyle: {
color: "#2AD9FD",
align: "right",
fontSize: 15,
padding: [0, -15, 0, 0],
},
boundaryGap: false,
data: ["1", "5", "10", "15", "20", "25", "30"],
axisTick: {
show: false,
},
axisLabel: {
color: "#fff",
fontSize: 12,
},
axisLine: {
lineStyle: {
color: "#668598",
},
},
},
grid: {
left: 50,
top: 10,
// bottom: 30,
// right: 30,
},
tooltip: {
trigger: "axis",
valueFormatter: (value) => {
return value + "%";
},
},
yAxis: {
max: 100,
type: "value",
splitLine: {
lineStyle: {
type: [6, 9],
color: "rgba(255,255,255, .3)",
},
},
axisLabel: {
color: "#fff",
fontSize: 12,
formatter: "{value} %",
},
},
series: [
{
data: [0, 90, 65, 90, 45, 36, 27],
type: "line",
showSymbol: false,
smooth: true,
lineStyle: {
color: "#2AD9FD",
},
areaStyle: {
opacity: 0.8,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: "rgb(90, 227, 255, .9)",
},
{
offset: 1,
color: "rgba(42,217,253,0)",
},
]),
},
},
],
};

158
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/CrowdnessIndicatorRankings/index.vue

@ -1,17 +1,46 @@
<template>
<Card class='CrowdnessIndicatorRankings border' title="拥挤度指标排名情况">
<Card class="CrowdnessIndicatorRankings border" title="拥挤度指标排名情况">
<div class="map">
<AMapContainer v-if="delayLoad" style="z-index: 9;" />
<AMapContainer ref="AMapContainerRef" v-if="delayLoad" style="z-index: 9" />
</div>
<div class="right border">
<div :class="['item', { active: isActive(item) }]" v-for="item in operation" :key="item.key"
@click="handleClick(item)">
<ElPopover trigger="manual" :value="isActive(item)" :disabled="item.key !== 'weather'" placement="left">
<div v-if="item.key === 'weather'">
天气 能见度 风向西南 风力1
<div
:class="['item', { active: isActive(item) }]"
v-for="item in operation"
:key="item.key"
@click="handleClick(item)"
>
<div
v-if="item.key === 'bar'"
class="label"
slot="reference"
:style="{
backgroundImage: `url(${require(`./images/${item.key}${
isActive(item) ? '-active' : ''
}.svg`)})`,
}"
/>
<ElPopover v-else trigger="manual" :value="isActive(item)" placement="left">
<div v-if="item.key === 'weather'" class="weather">
<span>天气{{ weatherData.text }}</span>
<span>能见度{{ weatherData.vis }}公里</span>
<span>风向{{ weatherData.windDir }}</span>
<span>风力{{ weatherData.windScale }}</span>
</div>
<div class="label" slot="reference"
:style="{ backgroundImage: `url(${require(`./images/${item.key}${isActive(item) ? '-active' : ''}.svg`)})` }" />
<div
v-else-if="item.key === 'pie'"
class="statistics"
id="statChartRef"
></div>
<div
class="label"
slot="reference"
:style="{
backgroundImage: `url(${require(`./images/${item.key}${
isActive(item) ? '-active' : ''
}.svg`)})`,
}"
/>
</ElPopover>
</div>
</div>
@ -19,15 +48,18 @@
</template>
<script>
import Card from "@screen/components/Card2/Card.vue";;
import AMapContainer from "@screen/pages/Home/components/AMapContainer/index.vue"
import { provideMixin } from "./../../mixin"
import Card from "@screen/components/Card2/Card.vue";
import AMapContainer from "@screen/pages/Home/components/AMapContainer/index.vue";
import { provideMixin } from "./../../mixin";
import { WeatherForecast } from "@/api/commandDispatch";
import * as echarts from "echarts";
import { StatChartOption } from "./charts";
export default {
name: 'CrowdnessIndicatorRankings',
name: "CrowdnessIndicatorRankings",
components: {
Card,
AMapContainer
AMapContainer,
},
mixins: [provideMixin],
data() {
@ -35,58 +67,78 @@ export default {
active: [],
operation: [
{
key: 'weather',
label: "天气"
key: "weather",
label: "天气",
},
{
key: 'camera',
label: "摄像机"
key: "camera",
label: "附近相机",
},
{
key: 'bar',
label: "柱状图"
key: "bar",
label: "线路通行情况",
},
{
key: 'level',
label: "天气"
key: "level",
label: "情报板",
},
// {
// key: "car",
// label: "",
// },
{
key: 'car',
label: "天气"
},
{
key: 'pie',
label: "天气"
key: "pie",
label: "断面车流量",
},
],
delayLoad: false
}
delayLoad: true,
weatherData: {},
};
},
computed: {
isActive() {
return item => this.active.includes(item.key)
}
return (item) => this.active.includes(item.key);
},
},
methods: {
handleClick(item) {
const findIndex = this.active.indexOf(item.key);
if (findIndex === -1) this.active.push(item.key);
else
this.active.splice(findIndex, 1)
}
},
mounted() {
setTimeout(() => {
this.delayLoad = true
}, 240);
else this.active.splice(findIndex, 1);
if (item.key === "weather") {
this.getWeatherForecast();
} else if (item.key === "pie") {
this.$nextTick(() => {
const statChart = echarts.init(
document.getElementById("statChartRef")
);
statChart.setOption(StatChartOption);
});
} else if (item.key === 'bar'){
this.$refs.AMapContainerRef.showRoadConditions(findIndex === -1)
}
},
getWeatherForecast() {
WeatherForecast(117.2, 36.65).then((res) => {
this.weatherData =
res.data.weatherInTheAreaWhereTheIncidentOccurred.now;
console.log("气象", res);
});
},
},
}
mounted() {},
};
</script>
<style lang='scss' scoped>
<style lang="scss" scoped>
.border {
border: 1px solid;
border-image: linear-gradient(360deg, rgba(55, 231, 255, .42), rgba(55, 231, 255, 0)) 1 1;
border-image: linear-gradient(
360deg,
rgba(55, 231, 255, 0.42),
rgba(55, 231, 255, 0)
)
1 1;
}
.CrowdnessIndicatorRankings {
@ -108,7 +160,11 @@ export default {
height: 100%;
top: 0;
width: 34px;
background: linear-gradient(180deg, rgba(25, 40, 52, .6) 0%, rgba(28, 50, 60, .6) 100%);
background: linear-gradient(
180deg,
rgba(25, 40, 52, 0.6) 0%,
rgba(28, 50, 60, 0.6) 100%
);
border-right: 0;
border-bottom: 0;
@ -125,7 +181,7 @@ export default {
div {
width: 100%;
height: 100%;
transition: all .15s linear;
transition: all 0.15s linear;
background-size: 100% 100%;
background-repeat: no-repeat;
background-position: center;
@ -133,8 +189,18 @@ export default {
}
.active {
background: linear-gradient(180deg, #005C79 0%, #009BCC 100%);
background: linear-gradient(180deg, #005c79 0%, #009bcc 100%);
}
}
}
.weather span {
margin-right: 20px;
display: inline-block;
}
.statistics {
width: 300px;
height: 180px;
display: flex;
margin: 5px;
}
</style>

51
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/DeviceControl/index.vue

@ -0,0 +1,51 @@
<template>
<Card class='DeviceControl' title="设备管控">
TODO
</Card>
</template>
<script>
import Card from "@screen/components/Card2/Card.vue";;
import { merge } from "lodash";
import { provideMixin } from "./../../mixin"
export default {
name: 'DeviceControl',
mixins: [provideMixin],
components: {
Card
},
data() {
return {
data: null
}
},
watch: {
data() {
}
},
mounted() {
},
methods: {
},
}
</script>
<style lang='scss' scoped>
.DeviceControl {
::v-deep {
.content {
display: flex;
flex-direction: column;
}
}
}
</style>

207
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/DeviceControl/utils.js

@ -0,0 +1,207 @@
export function getQuadrant(angle) {
angle %= 360.0;
if (angle < 0) angle += 360.0;
var quadrant = Math.floor(angle / 90) + 1;
return quadrant;
}
export function calcPoint(x0, y0, width, height, angle) {
const parseAngle = parseFloat(angle);
angle = (Math.PI / 180) * parseFloat(parseAngle);
x0 += width / 2;
y0 += height / 2;
const hypotenuse = width / 2 > height / 2 ? width / 2 : height / 2;
// 对边
let oppositeEdge = Math.abs(hypotenuse * Math.sin(angle));
// 邻边
let adjacentEdge = Math.abs(hypotenuse * Math.cos(angle));
let point0 = [],
point1 = [];
switch (parseFloat(parseAngle) % 360.0) {
case 180:
oppositeEdge = adjacentEdge;
adjacentEdge = 0;
break;
case 90:
case 270:
adjacentEdge = oppositeEdge;
oppositeEdge = 0;
break;
}
switch (getQuadrant(parseAngle)) {
case 1:
point0 = [x0 - adjacentEdge, y0 + oppositeEdge];
point1 = [x0 + adjacentEdge, y0 - oppositeEdge];
break;
case 2:
point0 = [x0 - adjacentEdge, y0 - oppositeEdge];
point1 = [x0 + adjacentEdge, y0 + oppositeEdge];
break;
case 3:
point0 = [x0 + adjacentEdge, y0 - oppositeEdge];
point1 = [x0 - adjacentEdge, y0 + oppositeEdge];
break;
case 4:
point0 = [x0 + adjacentEdge, y0 + oppositeEdge];
point1 = [x0 - adjacentEdge, y0 - oppositeEdge];
break;
}
return angle > 0 ? [point0, point1] : [point1, point0];
}
export class CanvasFlow {
canvas = null;
context = null;
zoom = 3;
constructor(canvas) {
this.canvas = canvas;
this.context = canvas.getContext("2d");
this.canvas.width = canvas.clientWidth * this.zoom;
this.canvas.height = canvas.clientHeight * this.zoom;
}
drawRectangle({
x,
y,
width,
height,
borderWidth,
borderColor,
backgroundColor,
radius,
}) {
this.context.beginPath();
if (backgroundColor) this.context.fillStyle = backgroundColor;
x *= this.zoom;
y *= this.zoom;
width *= this.zoom;
height *= this.zoom;
if (radius) {
this.context.roundRect(x, y, width, height, radius);
this.context.fill();
} else {
this.context.fillRect(x, y, width, height);
}
if (typeof borderWidth === "number") {
borderWidth *= this.zoom;
this.context.lineWidth = borderWidth;
this.context.strokeStyle = borderColor;
this.context.stroke();
}
}
transformCssLinearGradient(x, y, width, height, linearGradient) {
const [_, deg, ...stopStrings] = linearGradient.match(/[^,()]+/g);
const stops = stopStrings.map((stop) => {
const [color, offset] = stop.trim().split(" ");
return {
color,
offset: parseFloat(offset) * 0.01,
};
});
const [point0, point1] = calcPoint(x, y, width, height, deg);
return [
{
x0: point0[0],
y0: point0[1],
x1: point1[0],
y1: point1[1],
},
stops,
];
}
setLinearGradient({ x0, y0, x1, y1 }, stops) {
x0 *= this.zoom;
x1 *= this.zoom;
y0 *= this.zoom;
y1 *= this.zoom;
const gradient = this.context.createLinearGradient(x0, y0, x1, y1);
stops.forEach(({ offset, color }) => {
gradient.addColorStop(offset, color);
});
this.context.fillStyle = gradient;
}
drawLine({ x, y, color, lineWidth = 1 }, vertices) {
x *= this.zoom;
y *= this.zoom;
this.context.beginPath();
this.context.moveTo(x, y);
vertices.forEach(({ x, y }) => {
this.context.lineTo(x * this.zoom, y * this.zoom);
});
this.context.strokeStyle = color;
this.context.lineWidth = lineWidth;
this.context.stroke();
}
fillText(
text,
{
x,
y,
fontSize = 15,
color,
align = "center",
fontWeight = "normal",
fontFamily = "微软雅黑",
} = {},
{ width: rectWidth = 0, height: rectHeight = 0 } = {}
) {
this.context.beginPath();
this.context.font = `${fontWeight} ${fontSize * this.zoom}px ${fontFamily}`;
this.context.textBaseline = "middle";
const textWidth = this.context.measureText(text).width;
x *= this.zoom;
switch (align) {
case "center":
x += (rectWidth * this.zoom - textWidth) / 2;
y += rectHeight / 2;
break;
}
this.context.fillStyle = color;
this.context.fillText(text, x, y * this.zoom);
}
clear() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}

187
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/DispatchLiaison/StatsDialogVisible/index.vue

@ -0,0 +1,187 @@
<template>
<Dialog v-model="modelVisible" title="智能调度" width="1200px">
<div class="StatsDetail">
<el-form ref="form" :model="form">
<el-table :data="data">
<el-table-column
prop="organizationName"
label="路管驻点"
width="180"
align="center"
>
</el-table-column>
<el-table-column
prop="difference"
label="距离"
width="180"
align="center"
>
<template slot-scope="scope">
<span>{{ scope.row.difference }}公里</span>
</template>
</el-table-column>
<el-table-column label="人员" align="center">
<template slot-scope="scope">
<el-checkbox-group v-model="form.personnel">
<el-checkbox
v-for="item in scope.row.shiftsMap"
:label="item.shiftsName"
></el-checkbox>
</el-checkbox-group>
<!-- <span v-for="item in scope.row.shiftsMap">
{{ item.shiftsName }}</span
> -->
</template>
</el-table-column>
<el-table-column label="车辆" align="center">
<template slot-scope="scope">
<el-checkbox-group v-model="form.vehicle">
<el-checkbox
v-for="item in scope.row.vehiclesMap"
:label="item.vehiclePlate + ' -' + item.vehicleText"
></el-checkbox>
</el-checkbox-group>
<!-- <span v-for="item in scope.row.vehiclesMap">
{{ item.vehiclePlate }}</span
> -->
</template>
</el-table-column>
</el-table>
</el-form>
</div>
<template #footer>
<Button
style="background-color: rgba(0, 179, 204, 0.3)"
@click.native="(modelVisible = false), (submitting = false)"
>
取消
</Button>
<Button @click.native="handleSubmit" :loading="submitting"> 确定 </Button>
</template>
</Dialog>
</template>
<script>
import Dialog from "@screen/components/Dialog/index";
import InputSearch from "@screen/components/InputSearch/index.vue";
import Table from "@screen/components/Table.vue";
import { selectTollStation } from "@/api/event/governanceAnalysis";
import request from "@/utils/request";
import Button from "@screen/components/Buttons/Button.vue";
export default {
name: "StatsDetail",
components: {
Dialog,
InputSearch,
Table,
Button,
},
model: {
prop: "visible",
event: "update:value",
},
props: {
visible: Boolean,
data: Array,
},
data() {
return {
form: { personnel: [], vehicle: [] },
};
},
computed: {
modelVisible: {
get() {
return this.visible;
},
set(val) {
this.$emit("update:value", val);
},
},
},
methods: {
handleSubmit() {},
},
mounted() {},
};
</script>
<style lang="scss" scoped>
.dialog {
min-height: 800px;
}
.search {
display: flex;
flex-direction: row-reverse;
}
::v-deep .is-scrolling-none {
background: #0b6581;
}
::v-deep .el-table__empty-text {
color: #3ae0f8;
}
.StatsDetail {
// height: 770px;
margin: 20px 0;
width: 100%;
display: flex;
gap: 5px;
}
.el-table {
border: 1px solid #07aec6;
background: #0b6581;
}
::v-deep .el-table .el-table__header-wrapper th,
.el-table .el-table__fixed-header-wrapper th {
background: #0b6581;
border: 1px solid #07aec6;
}
::v-deep .el-table th.el-table__cell > .cell {
color: #3de8ff;
}
::v-deep .el-table thead.is-group th.el-table__cell {
background: #0b6581;
border: 1px solid #07aec6;
}
// ::v-deep .el-table th.el-table__cell.is-leaf,
::v-deep .el-table td.el-table__cell {
border: 1px solid #07aec6;
background: #1b586d;
color: #fff;
}
::v-deep .el-table tr {
border: 1px solid #07aec6;
background-color: #0b6581;
}
::v-deep
.el-table--enable-row-hover
.el-table__body
tr:hover
> td.el-table__cell {
background: #1b586d;
border: 1px solid #07aec6;
}
::v-deep .el-table::before,
.el-table--group::after,
.el-table--border::after {
background: none;
}
.footer {
margin-top: 20px;
display: flex;
}
</style>

201
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/DispatchLiaison/index.vue

@ -1,95 +1,170 @@
<template>
<Card class='DispatchLiaison' title="调度联络">
<Card class="DispatchLiaison" title="调度联络">
<template #title-suffix>
<ButtonGradient class="title-button" @click.native="handleClick">
调度
</ButtonGradient>
</template>
<Descriptions :list="list" style="gap: 24px; flex: 1;">
<template #content-phone1="{ data: { text } }">
{{ text }}
<img src="./images/phone.svg" />
</template>
<template #content-phone2="{ data: { text } }">
{{ text }}
<img src="./images/phone.svg" />
<img src="./images/camera.svg" />
</template>
<template #content-rescueVehicles="{ data: { text } }">
{{ text }}
<img src="./images/rescueVehicles.svg" />
</template>
</Descriptions>
<div class="dispatch-content">
<div class="dispatch-item">
<span class="label">业务单位</span>
<span class="value">{{
dispatchData.organizationName ? dispatchData.organizationName : "-"
}}</span>
</div>
<div class="dispatch-item">
<div>
<span class="label">值班领导</span>
<span class="value">{{
dispatchData.shiftsName ? dispatchData.shiftsName : "-"
}}</span>
</div>
<div>
<span class="label">手机号</span>
<span class="value">{{
dispatchData.shiftsNumber ? dispatchData.shiftsNumber : "-"
}}</span>
</div>
</div>
<div class="dispatch-item">
<span class="label">调度人员</span>
<span class="value" v-for="item in dispatchData.shiftsMap">{{
dispatchData.shiftsMap.length > 0 ? item.shiftsName : "-"
}}</span>
</div>
<div class="dispatch-item">
<div>
<span class="label">路管人员</span>
<span class="value">{{
dispatchData.name ? dispatchData.name : "-"
}}</span>
</div>
<div>
<span class="label">手机号</span>
<span class="value">{{
dispatchData.contactNumber ? dispatchData.contactNumber : "-"
}}</span>
</div>
</div>
<div class="dispatch-item">
<span class="label">救援车辆 </span>
<span class="value" v-for="item in dispatchData.vehiclesMap">{{
dispatchData.vehiclesMap.length > 0
? item.vehiclePlate + item.vehicleText
: "-"
}}</span>
</div>
</div>
<StatsDialogVisible
:visible="statsDialogVisible"
:data="dispatchList"
@update:value="handleClose"
/>
</Card>
</template>
<script>
import Card from "@screen/components/Card2/Card.vue";
import ButtonGradient from '@screen/components/Buttons/ButtonGradient.vue';
import Descriptions from '@screen/components/Descriptions.vue';
import { provideMixin } from "./../../mixin"
import ButtonGradient from "@screen/components/Buttons/ButtonGradient.vue";
import Descriptions from "@screen/components/Descriptions.vue";
import { provideMixin } from "./../../mixin";
import { commandDispatch } from "@/api/commandDispatch";
import StatsDialogVisible from "./StatsDialogVisible/index.vue";
import request from "@/utils/request";
export default {
name: 'DispatchLiaison',
name: "DispatchLiaison",
mixins: [provideMixin],
components: {
Card,
ButtonGradient,
Descriptions
Descriptions,
StatsDialogVisible,
},
data() {
return {
list: [
{
label: '业务单位',
text: '山东正晨科技股份有限公司',
gridColumn: 2
},
{
label: '值班领导',
text: '张亮亮',
},
{
label: '手机',
key: "phone1",
text: '18888888888',
},
{
label: '调度人员',
text: '',
gridColumn: 2
},
{
label: '路管人员',
text: '王一博',
},
{
label: '手机',
key: "phone2",
text: '16666666666',
},
{
label: '救援车辆',
key: "rescueVehicles",
text: '鲁A34567 (大型清障车)',
gridColumn: 2
},
]
}
dispatchData: [],
dispatchList: [],
vehicleTypeList: [],
statsDialogVisible: false,
};
},
methods: {
handleClick() {
}
}
}
console.log("-------");
this.statsDialogVisible = true;
},
handleClose() {
this.statsDialogVisible = false;
},
getCommandDispatch() {
let data = {
stakeMark: "K099+750",
};
commandDispatch(data).then((res) => {
res.data.forEach((element) => {
if (element.vehiclesMap.length > 0) {
element.vehiclesMap.forEach((item) => {
this.vehicleTypeList.forEach((i) => {
if (item.vehicleType == i.dictValue) {
item.vehicleText = "(" + i.dictLabel + ")";
}
});
});
}
});
this.dispatchList = res.data;
this.dispatchData = res.data[0];
});
},
getVehicleTypeList() {
if (this.vehicleTypeList.length) return;
request({
url: `/system/dict/data/type/sys_vehicle_type`,
method: "GET",
params: {},
})
.then(({ data, code }) => {
if (code != 200) return; //Message.error("");
this.vehicleTypeList = data;
})
.catch((err) => {});
},
},
mounted() {
this.getCommandDispatch();
this.getVehicleTypeList();
},
};
</script>
<style lang='scss' scoped>
<style lang="scss" scoped>
.DispatchLiaison {
::v-deep {
.content {
display: flex;
align-items: center;
align-items: flex-start;
}
}
.dispatch-content {
width: 100%;
.dispatch-item {
display: flex;
width: 100%;
align-items: flex-start;
margin: 15px 0;
div {
width: 50%;
}
.label {
font-size: 14px;
color: #38d2e7;
}
.value {
font-size: 14px;
color: #fff;
}
}
}
}

237
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/DisposalProcess/index.vue

@ -1,38 +1,69 @@
<template>
<Card class='DisposalProcess' title="处置过程">
<Card class="DisposalProcess" title="处置过程">
<template #title-suffix>
<ButtonGradient class="title-button">
事件解除
</ButtonGradient>
<ButtonGradient class="title-button">
协同调度
</ButtonGradient>
<ButtonGradient class="title-button">
无需清障
</ButtonGradient>
<ButtonGradient class="title-button">
重要事件上报
</ButtonGradient>
<ButtonGradient class="title-button">
路赔
</ButtonGradient>
<ButtonGradient class="title-button special-button" @click.native="handleFullHeight">
<ButtonGradient class="title-button"> 事件解除 </ButtonGradient>
<ButtonGradient class="title-button"> 协同调度 </ButtonGradient>
<ButtonGradient class="title-button"> 无需清障 </ButtonGradient>
<ButtonGradient class="title-button"> 重要事件上报 </ButtonGradient>
<ButtonGradient class="title-button"> 路赔 </ButtonGradient>
<ButtonGradient
class="title-button special-button"
@click.native="handleFullHeight"
>
<template #prefix>
<div class="icon"
:style="{ backgroundImage: `url(${require(`./images/${isFullHeight ? 'reduce' : 'zoom'}.svg`)})` }" />
<div
class="icon"
:style="{
backgroundImage: `url(${require(`./images/${
isFullHeight ? 'reduce' : 'zoom'
}.svg`)})`,
}"
/>
</template>
</ButtonGradient>
</template>
<TimeLine1 :data="timeLine1List" :filterDistance="filterDistance" />
<TimeLine2 :data="timeLine2List" style="flex: 1;" />
<TimeLine2 :data="timeLine2List" style="flex: 1" />
<div v-if="!timeLine2List.length" class="no-data">暂无数据</div>
<div class="bottom">
<ElSelect value="" placeholder="请选择关键点" />
<RadioGroup :options="[{ key: 'input', label: '输入' }, { key: 'upload', label: '上传' }]" v-model="testRadio"
type="button" />
<ElInput v-model="test" placeholder="请输入调度指令" />
<ButtonGradient class="title-button special-button">
<ElSelect v-model="processId" placeholder="请选择关键点">
<ElOption
v-for="item in options"
:key="item.nodeNode"
:label="item.processNode"
:value="item.nodeNode"
>
</ElOption>
</ElSelect>
<RadioGroup
:options="[
{ key: 'input', label: '输入' },
{ key: 'upload', label: '上传' },
]"
v-model="testRadio"
type="button"
/>
<ElInput
v-if="testRadio == 'input'"
class="input"
v-model="context"
placeholder="请输入调度指令"
/>
<el-upload
v-if="testRadio == 'upload'"
class="input"
:headers="headers"
:action="uploadImgUrl"
:file-list="fileList"
:show-file-list="false"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
accept=".jpg,.jpeg,.png,.mp4"
>
<el-button class="input">点击上传</el-button>
</el-upload>
<ButtonGradient class="title-button special-button" @click="onSubmit">
发送
</ButtonGradient>
<ButtonGradient class="title-button special-button">
@ -43,19 +74,19 @@
</template>
<script>
import Card from "@screen/components/Card2/Card.vue";;
import ButtonGradient from '@screen/components/Buttons/ButtonGradient.vue';
import RadioGroup from '@screen/components/FormConfig/components/RadioGroup/index.vue';
import Card from "@screen/components/Card2/Card.vue";
import ButtonGradient from "@screen/components/Buttons/ButtonGradient.vue";
import RadioGroup from "@screen/components/FormConfig/components/RadioGroup/index.vue";
import { getToken } from "@/utils/auth";
import TimeLine1 from "@screen/components/TimeLine/TimeLine1/index";
import TimeLine2 from "@screen/components/TimeLine/TimeLine2/index";
import { provideMixin } from "./../../mixin"
import { provideMixin } from "./../../mixin";
import { timeLine2List } from "./data";
import request from "@/utils/request";
export default {
name: 'DisposalProcess',
name: "DisposalProcess",
inject: ["adpScale"],
mixins: [provideMixin],
components: {
@ -65,12 +96,13 @@ export default {
TimeLine2,
RadioGroup,
},
emit: ['fullHeight'],
emit: ["fullHeight"],
data() {
return {
test: null,
testRadio: "input",
timeLine1List: [],
// timeLine2List: Array.from({ length: 6 }).map(() => ({
// title: "",
// time: "2023-12-21 16:35:44",
@ -79,66 +111,136 @@ export default {
// posts: ''
// })),
timeLine2List: [],
isFullHeight: false
}
isFullHeight: false,
options: [],
processId: "",
context: "",
headers: {
Authorization: "Bearer " + getToken(),
},
uploadImgUrl: process.env.VUE_APP_BASE_API + "/common/upload", //
fileList: [],
fileType: ["bmp", "gif", "jpg", "jpeg", "png", "mp4", "avi", "xmvb"],
imageUrl: "",
};
},
methods: {
filterDistance(distance) {
return 1 / this.adpScale.scaleX * distance
return (1 / this.adpScale.scaleX) * distance;
},
handleFullHeight() {
this.isFullHeight = !this.isFullHeight;
this.$emit('fullHeight', 'CrowdnessIndicatorRankings')
this.$emit("fullHeight", "CrowdnessIndicatorRankings");
},
async detailChange(data) {
const timelineData = await this.getDetail(data);
let processIdMap = {}
async detailChange(id) {
const timelineData = await this.getDetail(id);
let processIdMap = {};
this.timeLine2List = timelineData.map(item => {
processIdMap[item.processId] = new Date(item.operationTime).toLocaleTimeString();
this.timeLine2List = timelineData.map((item) => {
processIdMap[item.processId] = new Date(
item.operationTime
).toLocaleTimeString();
return {
// title: "",
time: item.operationTime ? new Date(item.operationTime).toLocaleString() : null,
time: item.operationTime
? new Date(item.operationTime).toLocaleString()
: null,
name: item.operator,
desc: item.context,
// posts: ''
}
});
this.timeLine1List = data.processConfigList.map((item) => {
return {
time: processIdMap[item.nodeNode],
label: item.processNode,
isActive: !!processIdMap[item.nodeNode],
};
});
// this.timeLine1List = data.processConfigList.map((item) => {
// return {
// time: processIdMap[item.nodeNode],
// label: item.processNode,
// isActive: !!processIdMap[item.nodeNode],
// };
// });
},
getDetail(data) {
getDetail(id) {
return request({
url: `/system/process/list`,
method: "GET",
params: {
eventId: data.id
}
eventId: id,
},
})
.then((result) => {
return result || [];
})
.catch((err) => []);
},
//
getProcessNode(eventId) {
return request({
url:
`/dc/system/event/getProcessNode/` +
"1a91d65cc31f4a9d90122888edb31043",
method: "GET",
})
.then((result) => {
if (result.code != 200) return [];
return result.rows || []
this.timeLine1List = result.data.map((item) => {
return {
time: item.operationTime,
label: item.processNode,
isActive: item.status == 1 ? true : false,
};
});
this.options = result.data.filter((item) => {
item.status == 0;
return item;
});
})
.catch((err) => []);
},
}
}
//
handleUploadSuccess(res, file) {
this.$message.success("上传成功");
this.imageUrl = res.url;
},
//
handleUploadError() {
this.$message.error("上传失败");
},
containsArrayElement(str, arr) {
return arr.filter((element) => str.includes(element));
},
onSubmit() {
let type = this.containsArrayElement(this.imageUrl, this.fileType);
console.log(type);
request({
url: `/system/process`,
method: "POST",
data: {
eventId: "1a91d65cc31f4a9d90122888edb31043",
processId: this.processId,
context: this.context,
file: this.imageUrl,
type: type.join(","),
},
})
.then((result) => {
if (result.code != 200) return [];
this.getProcessNode();
this.detailChange("1a91d65cc31f4a9d90122888edb31043");
})
.catch((err) => []);
},
},
async mounted() {
// this.getProcessNode();
// await this.detailChange("1a91d65cc31f4a9d90122888edb31043");
},
};
</script>
<style lang='scss' scoped>
<style lang="scss" scoped>
.DisposalProcess {
::v-deep {
.content {
display: flex;
@ -153,15 +255,24 @@ export default {
background-size: 100% 100%;
width: 20px;
height: 20px;
transition: all .3s linear;
transition: all 0.3s linear;
}
}
.bottom {
display: grid;
grid-template-columns: .5fr auto 1fr 90px 90px;
grid-template-columns: 0.5fr auto 1fr 90px 90px;
width: 100%;
gap: 6px;
}
.input {
background-color: #0d5f79;
color: #f4f4f4;
border-radius: 2px;
line-height: 5px;
height: 26px;
font-size: 14px;
border: 0;
}
}
</style>

BIN
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/EventInformation/images/lane.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

79
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/EventInformation/images/lane.svg

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 2.6 MiB

After

Width:  |  Height:  |  Size: 2.6 KiB

15
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/EventInformation/index.vue

@ -13,12 +13,13 @@
<div class="road-lane">
<div v-for="i in 11">
<img v-if="i != 6" src="./images/normal.svg">
<!-- <img v-if="i != 6" src="./images/congestion.svg"> -->
<!-- <img v-if="i != 6" :src="'./images/'+(detailData.lang.indexOf((i+1).toString())===-1?'normal.svg':'congestion.svg')"> -->
<img v-if="i != 6 && !detailData.lang.includes((i).toString()) " src="./images/normal.svg">
<img v-if="i != 6 && detailData.lang.includes((i).toString())" src="./images/congestion.svg">
</div>
</div>
<div class="bottom-info">
<div class="tag">拥堵五级事件</div>
<div class="tag" >{{ detailData.eventLevel?`${detailData.eventCause+['','一','二','三','四','五'][detailData.eventLevel]}级事件` : '暂无事件等级' }}</div>
</div>
<EditEventInformationDialog v-model="editEventInformationDialogVisible" />
@ -69,8 +70,7 @@ export default {
{
label: '道路方向',
// text: '',
key: 'direction',
enum: 'CameraDirectionEnum'
key: 'direction'
},
{
label: '桩号',
@ -96,12 +96,13 @@ export default {
}
.road-lane {
background-image: url(./images/lane.svg);
background-image: url(./images/lane.jpg);
flex: 1;
background-repeat: no-repeat;
background-size: 100% 100%;
// background-size: auto auto;
display: grid;
grid-template-columns: 1.2fr 1fr 1fr 1fr 1fr 0.6fr 1fr 1fr 1fr 1fr 1.2fr;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr .5fr 1fr 1fr 1fr 1fr 1fr;
>div {
display: flex;

13
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/RealTimeVideo/index.vue

@ -1,19 +1,19 @@
<template>
<Card class='RealTimeVideo' title="实时视频">
<div class="item">
<Video class="item-video" :pileNum="detailData.stakeMark" />
<span>处置现场</span>
<Video v-if="detailData.stakeMark" class="item-video" :pileNum="detailData.stakeMark" rangeIndex="upCamera" :showHeader="false" />
<span>济南方向</span>
</div>
<div class="item">
<Video class="item-video" />
<span>感知视频</span>
<Video v-if="detailData.stakeMark" class="item-video" :pileNum="detailData.stakeMark" rangeIndex="downCamera" :showHeader="false" />
<span>菏泽方向</span>
</div>
</Card>
</template>
<script>
import Card from "@screen/components/Card2/Card.vue";;
import Video from "@screen/components/Video"
import Video from "@screen/components/Video2"
import { provideMixin } from "./../../mixin"
export default {
@ -32,7 +32,7 @@ export default {
::v-deep {
.content {
display: flex;
align-items: center;
align-items: flex-start;
justify-content: space-between;
gap: 9px;
}
@ -43,6 +43,7 @@ export default {
display: flex;
flex-direction: column;
height: 100%;
max-height: 250px;
align-items: center;
gap: 6px;

0
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/ReleaseInformation/images/weChat.svg → ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/ReleaseInformation/images/1.svg

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

0
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/ReleaseInformation/images/message.svg → ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/ReleaseInformation/images/2.svg

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

0
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/ReleaseInformation/images/website.svg → ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/ReleaseInformation/images/3.svg

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

0
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/ReleaseInformation/images/weibo.svg → ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/ReleaseInformation/images/4.svg

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

39
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/Cards/ReleaseInformation/index.vue

@ -1,7 +1,7 @@
<template>
<Card class='ReleaseInformation' title="信息发布">
<Form :formList="formList" column="1">
<template #platform="{ formData, data }">
<Form :formList="formList" column="1" ref="FormMsgRef" >
<template #type="{ formData, data }">
<CheckboxGroup :options="checkboxList" v-model="formData[data.key]">
<template v-for="item in checkboxList" #[item.key]="{ data }">
<div class="checkbox-content">
@ -13,7 +13,7 @@
</template>
</Form>
<div class="line"></div>
<!-- <div class="line"></div>
<Descriptions :list="list" style="flex: 1;" column="1" titleStyle="align-self: flex-start;">
<template #content-informationBoard>
@ -22,15 +22,15 @@
<img src="./images/add.svg" />
</div>
</template>
</Descriptions>
</Descriptions> -->
<div class="bottom">
<ButtonGradient class="title-button special-button">
<ButtonGradient @click="confirm" class="title-button special-button">
一键发布
</ButtonGradient>
<ButtonGradient class="title-button special-button">
<!-- <ButtonGradient class="title-button special-button">
发布预案
</ButtonGradient>
</ButtonGradient> -->
</div>
</Card>
</template>
@ -42,6 +42,7 @@ import Form from '@screen/components/FormConfig';
import CheckboxGroup from '@screen/components/FormConfig/components/ElCheckboxGroup.vue';
import Descriptions from '@screen/components/Descriptions.vue';
import { provideMixin } from "./../../mixin"
import { method, result } from "lodash";
export default {
name: 'ReleaseInformation',
@ -64,10 +65,10 @@ export default {
],
checkboxValues: [],
checkboxList: [
{ key: 'weChat', label: '微信' },
{ key: 'message', label: '短信' },
{ key: 'website', label: '网站' },
{ key: 'weibo', label: '新浪' },
{ key: '1', label: '微信' },
{ key: '2', label: '短信' },
{ key: '3', label: '网站' },
{ key: '4', label: '新浪' },
],
formList: [{
label: "发布内容:",
@ -76,18 +77,28 @@ export default {
options: {
type: "textarea",
autosize: true,
maxlength: 200,
autosize: { minRows: 9, maxRows: 9 },
autosize: { minRows: 4, maxRows: 4 },
showWordLimit: true
}
},
{
label: "发布平台:",
key: "platform",
key: "type",
type: "input",
default: []
}],
}
},
methods: {
async confirm(){
await this.$refs.FormMsgRef.validate()
.then((res) => {
console.log(res)
})
.catch((err) => {
console.log("catch");
});
}
}
}
</script>

105
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/commandDispatch/index.vue

@ -1,14 +1,20 @@
<template>
<div class='CommandDispatch' :style="{ '--row': row }">
<component :is="key" v-for="(_, key) in gridAreaMap" :key="key" :ref="key" :style="{ gridArea: gridAreaMap[key] }"
@fullHeight="opacityKey => handleFullHeight(key, opacityKey)" />
<div class="CommandDispatch" :style="{ '--row': row, height: '100%' }">
<component
:is="key"
v-for="(_, key) in gridAreaMap"
:key="key"
:ref="key"
:style="{ gridArea: gridAreaMap[key] }"
@fullHeight="(opacityKey) => handleFullHeight(key, opacityKey)"
/>
</div>
</template>
<script>
import request from "@/utils/request";
const files = require.context('./Cards', true, /[^/]+\/index\.vue$/);
const files = require.context("./Cards", true, /[^/]+\/index\.vue$/);
const components = files.keys().reduce((components, key) => {
components[key.match(/[^/]+/g)[1]] = files(key).default;
@ -17,58 +23,77 @@ const components = files.keys().reduce((components, key) => {
}, {});
const originGridArea = {
EventInformation: "1 / 1 / span 9 / 1",
DispatchLiaison: "10 / 1 / span 7 / 2",
DisposalPlan: "17 / 1 / span 9 / 2",
CrowdnessIndicatorRankings: "1 / 2 / span 11 / 2",
DisposalProcess: "12 / 2 / span 14 / 2",
RealTimeVideo: "1 / 3 / span 6 / 3",
ReleaseInformation: "7 / 3 / span 14 / 3",
TrafficControl: "21 / 3 / span 5 / 3",
}
EventInformation: "1 / 1 / span 12 / 1", // 1
DispatchLiaison: "13 / 1 / span 8 / 2", // 213 / 1 / span 11 / 2
TrafficControl: "21 / 1 / span 13 / 2", //324 / 1 / span 10 / 2
CrowdnessIndicatorRankings: "1 / 2 / span 12 / 2", // / 1
DisposalProcess: "13 / 2 / span 21 / 2", //2
RealTimeVideo: "1 / 3 / span 9 / 3", //1
ReleaseInformation: "10 / 3 / span 7 / 3", //2
DeviceControl: "17 / 3 / span 17 / 3", //3
// DisposalPlan: "17 / 1 / span 9 / 2", //
};
export default {
name: 'CommandDispatch',
name: "CommandDispatch",
components: {
...components,
},
props: {
detailId: {
type: [String, Number],
default: 2
}
default: "96b9918efc01488cb22fa1d9d3236dfd",
},
},
provide() {
return {
provideData: this.provideData,
}
};
},
data() {
return {
gridAreaMap: {
...originGridArea
...originGridArea,
},
row: 24,
provideData: {
detail: null
}
}
detail: null,
},
};
},
created() {
this.getDetail();
},
methods: {
getDetail() {
// -
request({
url: `/dc/system/event/${this.detailId}`,
method: "GET",
})
.then((result) => {
if (result.code != 200) return;
console.log(result.data);
this.provideData.detail = result.data;
if (
["设备设施隐患", "非法上路", "施工建设", "服务区异常"].includes(
result.data.eventName
)
) {
const gridArea = { ...originGridArea };
//
gridArea["DispatchLiaison"] = "10 / 1 / span 20 / 2";
delete gridArea.TrafficControl;
if (["设备设施隐患", "非法上路"].includes(result.data.eventName)) {
//
gridArea["RealTimeVideo"] = "1 / 3 / span 17 / 3";
delete gridArea.ReleaseInformation;
}
this.provideData.detail = result.data
this.gridAreaMap = gridArea;
}
})
.catch((err) => { });
.catch((err) => {});
},
handleFullHeight(key, opacityKey) {
if (this.gridAreaMap[key] !== originGridArea[key]) {
@ -76,30 +101,32 @@ export default {
if (this.$refs[opacityKey]?.[0]) {
this.$refs[opacityKey][0].$el.style.opacity = 1;
this.$refs[opacityKey][0].$el.style.pointerEvents = "auto";
};
}
} else {
let index = 0;
if (this.$refs[opacityKey]?.[0]) {
this.$refs[opacityKey][0].$el.style.opacity = 0;
this.$refs[opacityKey][0].$el.style.pointerEvents = "none";
};
this.gridAreaMap[key] = originGridArea[key].replace(/[0-9]+/g, (num) => {
try {
return { 0: '1', 2: this.row + 1 }[index] || num;
} catch (error) {
}
} finally {
index++;
this.gridAreaMap[key] = originGridArea[key].replace(
/[0-9]+/g,
(num) => {
try {
return { 0: "1", 2: this.row + 1 }[index] || num;
} catch (error) {
} finally {
index++;
}
}
});
);
}
}
}
}
},
},
};
</script>
<style lang='scss' scoped>
<style lang="scss" scoped>
.CommandDispatch {
padding: 24px 12px 21px 12px;
display: grid;
@ -107,8 +134,8 @@ export default {
grid-template-rows: repeat(var(--row), 1fr);
gap: 18px;
>div {
transition: all .18s linear;
> div {
transition: all 0.18s linear;
}
::v-deep {

13
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/event/EventDetailDialog/Carousel/index.vue

@ -11,6 +11,12 @@
</div>
</VueSlickCarousel>
<VueSlickCarousel v-if="videos.length > 0" v-bind="settings" ref="CarouselRef" class="vueSlickCarousel">
<div v-for="(item, index) in videos " :key="index" class="item">
<Video style="height: 100%;" :showHeader="false" :url="item || ''" videoType="mp4" />
</div>
</VueSlickCarousel>
<img src="./images/arrow.svg" @click="nextSlide" class="arrow" />
</div>
</template>
@ -21,10 +27,11 @@ import VueSlickCarousel from 'vue-slick-carousel'
import 'vue-slick-carousel/dist/vue-slick-carousel.css'
// optional style for arrows & dots
import 'vue-slick-carousel/dist/vue-slick-carousel-theme.css'
import Video from "@screen/components/Video";
export default {
name: "Carousel",
components: { VueSlickCarousel },
components: { VueSlickCarousel,Video },
props: {
pictures: {
type: Array,
@ -39,6 +46,10 @@ export default {
// "require('@screen/images/shareWith/weChat-active.svg')"
]
},
videos: {
type: Array,
default: () => []
},
},
data() {
return {

104
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/event/EventDetailDialog/index.vue

@ -1,89 +1,44 @@
<template>
<Dialog v-model="modelVisible" title="事件详情" width="890px" top="11%">
<div
class="EventDetail"
:style="{
height: activeName == '-1' || activeName == '0' ? '380px' : '598px',
}"
>
<div class="EventDetail" :style="{
height: activeName == '-1' || activeName == '0' ? '380px' : '598px',
}">
<Form :formList="formList" :dFormData="formData" label-width="100px" />
<div class="video-pic">
<Video
style="height: 100%; width: 380px"
:showHeader="false"
:url="
formData.videoList && formData.videoList.length > 0
? formData.videoList[0]
: ''
"
:camId="formData.upCamId"
:pileNum="formData.stakeMark"
rangeIndex="upCamera"
:videoType="formData.videoType"
/>
<Video
v-if="activeName != '-1'"
style="height: 100%; width: 380px"
:showHeader="false"
:url="
formData.videoList && formData.videoList.length > 0
? formData.videoList[1]
: ''
"
:camId="formData.downCamId"
:pileNum="formData.stakeMark"
rangeIndex="downCamera"
:videoType="formData.videoType"
/>
<Carousel v-else style="flex: 1" :pictures="formData.pictures" />
<Video v-if="activeName != '-1'" style="height: 100%; width: 380px" :showHeader="false" :url="formData.videoList && formData.videoList.length > 0
? formData.videoList[0] : ''" :camId="formData.upCamId" :pileNum="formData.stakeMark" rangeIndex="upCamera"
:videoType="formData.videoType" />
<Video v-if="activeName != '-1'" style="height: 100%; width: 380px" :showHeader="false" :url="formData.videoList && formData.videoList.length > 0
? formData.videoList[1] : ''" :camId="formData.downCamId" :pileNum="formData.stakeMark" rangeIndex="downCamera"
:videoType="formData.videoType" />
<Carousel v-if="activeName == '-1'" style="flex: 1" :videos="formData.videoList" :pictures="[]" />
<Carousel v-if="activeName == '-1'" style="flex: 1" :pictures="formData.pictures" :videos="[]" />
</div>
<!-- <div>{{ formData.videoList[0] }}</div> -->
<TimeLine1
v-if="activeName == '1' || activeName == '2'"
:data="timeLine1List"
/>
<TimeLine2
v-if="activeName == '1' || activeName == '2'"
:data="timeLine2List"
style="flex: 1"
/>
<TimeLine1 v-if="activeName == '1' || activeName == '2'" :data="timeLine1List" />
<TimeLine2 v-if="activeName == '1' || activeName == '2'" :data="timeLine2List" style="flex: 1" />
</div>
<!-- 确认弹窗 -->
<EventPlanDialog
:visible="isShowDialog"
:info="info"
:eventFormData="formData"
:activeName="activeName"
@reInitData="
() => {
this.$emit('update:value', false);
this.$emit('queryData', true);
}
"
@close="onCloseAddNew"
/>
<EventPlanDialog :visible="isShowDialog" :info="info" :eventFormData="formData" :activeName="activeName"
@reInitData="() => {
this.$emit('update:value', false);
this.$emit('queryData', true);
}
" @close="onCloseAddNew" />
<template #footer>
<Button
v-if="activeName == '-1' || activeName == '0'"
style="padding: 0 24px"
@click.native="onDelete"
>误报</Button
>
<Button
:style="{ backgroundColor: '#C9C9C9', padding: '0 24px' }"
@click.native="modelVisible = false"
>取消</Button
>
<Button
v-if="activeName == '-1' || activeName == '0'"
style="padding: 0 24px"
@click.native="onSubmit"
>确认</Button
>
<Button v-if="activeName == '-1' || activeName == '0'" style="padding: 0 24px"
@click.native="onDelete">误报</Button>
<Button :style="{ backgroundColor: '#C9C9C9', padding: '0 24px' }"
@click.native="modelVisible = false">取消</Button>
<Button v-if="activeName == '-1' || activeName == '0'" style="padding: 0 24px"
@click.native="onSubmit">确认</Button>
</template>
</Dialog>
</template>
@ -124,7 +79,7 @@ export default {
activeName: String,
formData: {
type: Object,
default: () => {},
default: () => { },
},
},
data() {
@ -251,6 +206,7 @@ export default {
}
},
onSubmit() {
return;
let url = "/business/plans/list/warning/type";
if (this.activeName == "-1") {
url = "/business/plans/list/warning/type";

2
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/event/FormEvent/data.js

@ -842,6 +842,7 @@ export const tabConfigList = [
required: true,
options: {
options: [],
multiple: true,
},
visible: (data) => {
if (data?.eventSubclass == "3-2") {
@ -858,6 +859,7 @@ export const tabConfigList = [
required: true,
options: {
options: [],
multiple: true,
},
visible: (data) => {
if (data?.eventSubclass == "3-3") {

39
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/event/FormEvent/index.vue

@ -2,37 +2,18 @@
<Dialog v-model="modelVisible" title="交通事故录入" width="1100px" top="14%">
<div class="EventDetailDialog">
<ElTabs v-model="activeName" @tab-click="handleChange">
<ElTabPane
v-for="(item, index) in tabConfigList"
:key="index"
:label="item.label"
:name="item.key"
/>
<ElTabPane v-for="(item, index) in tabConfigList" :key="index" :label="item.label" :name="item.key" />
</ElTabs>
<Transition name="fade">
<Form
class="form"
ref="FormConfigRef"
:formList="formConfig.list"
v-bind="getFormOptions"
labelWidth="120px"
/>
<Form class="form" ref="FormConfigRef" :formList="formConfig.list" v-bind="getFormOptions" labelWidth="120px" />
</Transition>
</div>
<template #footer>
<Button
style="background: #c9c9c9; padding: 0 24px"
@click.native="(modelVisible = false), (submitting = false)"
>取消</Button
>
<Button
style="padding: 0 24px"
@click.native="handleSubmit"
:loading="submitting"
>保存</Button
>
<Button style="background: #c9c9c9; padding: 0 24px"
@click.native="(modelVisible = false), (submitting = false)">取消</Button>
<Button style="padding: 0 24px" @click.native="handleSubmit" :loading="submitting">保存</Button>
</template>
</Dialog>
</template>
@ -180,12 +161,14 @@ export default {
} else {
formData.lang = "";
}
if (this.index == 2 && formData.dcEventTrafficControl.facilityId instanceof Array) {
let ids = formData.dcEventTrafficControl.facilityId.join(',');
formData.dcEventTrafficControl.facilityIds = ids
formData.dcEventTrafficControl.facilityId = ''
}
if (formData.endStakeMark && formData.endStakeMark[0] != null) {
let endStakeMark = formData.endStakeMark;
let strMark =
endStakeMark && endStakeMark.length > 0
? "K" + endStakeMark[0] + "+" + endStakeMark[1]
: "";
let strMark = endStakeMark && endStakeMark.length > 0 ? "K" + endStakeMark[0] + "+" + endStakeMark[1] : "";
if (this.index == 3) {
formData.dcEventTrafficCongestion.endStakeMark = strMark;
}

5
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/event/index.vue

@ -294,8 +294,9 @@ export default {
data.visibility = otherConfig.visibility;
data.pictures = otherConfig.pictures || [];
data.videoList = otherConfig.videoList || []
// data.videoList = otherConfig.videoList || ['https://sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-480p.mp4']
data.videoList = otherConfig.videoList || [];
// data.videoList = ['https://sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-480p.mp4',
// 'https://sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-480p.mp4']
}
data.videoType = "mp4";
this.detailDialogFormData = data;

413
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/plan/addAndEditDialog/ScopeTable.vue

@ -0,0 +1,413 @@
<template>
<div class="ScopeTable">
<el-row>
<el-col :span="2">
<p>设备资源:</p>
</el-col>
<el-col :span="4">
<!-- 设备类型 -->
<el-select v-model="tableInfo.deviceType" placeholder="" @change="changeDeviceType" transfer="true"
:popper-append-to-body="false">
<el-option v-for="item in deviceOptions" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
</el-col>
<el-col :span="4">
<!-- 规则条件 -->
<el-select v-model="tableInfo.searchRule" placeholder="">
<el-option v-for="item in zyOptions" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
</el-col>
<el-col :span="4">
<!-- 设备列表 -->
<el-select v-if="tableInfo.searchRule == 1" v-model="tableInfo.devList" placeholder="请选择设备" multiple
collapse-tags>
<el-option v-for="item in sbOptions" :key="item.id" :label="item.deviceName" :value="item.id">
</el-option>
</el-select>
<!-- -->
<el-input-number v-if="tableInfo.searchRule == 2 || tableInfo.searchRule == 3"
v-model="tableInfo.number" :min="0" :max="9999" style="width: 130px;"></el-input-number>
<span v-if="tableInfo.searchRule == 2 || tableInfo.searchRule == 3"></span>
<!-- 公里 -->
<el-input-number v-if="tableInfo.searchRule == 4" v-model="tableInfo.number" :min="0" :max="9999"
style="width: 130px;"></el-input-number>
<span v-if="tableInfo.searchRule == 4" style="width: 56px;">公里</span>
</el-col>
</el-row>
<el-row>
<el-col :span="2">
<p>执行操作:</p>
</el-col>
<!-- 可变信息标识 -->
<el-col v-if="tableInfo.deviceType == 2" :span="4">
<el-select v-model="tableInfo.zx_operationType" placeholder="请选择">
<el-option label="自定义发布" :value="1"></el-option>
<el-option label="智能发布" :value="2"></el-option>
</el-select>
</el-col>
<el-col v-if="tableInfo.deviceType == 2 && tableInfo.zx_operationType == 1" :span="4">
<el-input @click.native="clickQbb('zx_content')" placeholder="请选择" v-model="tableInfo.zx_content" readonly>
<i slot="suffix" class="el-input__icon el-icon-search"></i>
</el-input>
</el-col>
<!-- 疲劳唤醒 -->
<el-col v-if="tableInfo.deviceType == 10" :span="4">
<el-select v-model="tableInfo.zx_name" placeholder="工作模式">
<el-option v-for="item in gzmsOptions" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-col>
<el-col v-if="tableInfo.deviceType == 10" :span="4">
<el-input-number placeholder="" v-model="tableInfo.zx_operationDuration" :min="0"
:max="999"></el-input-number>
</el-col>
<el-col v-if="tableInfo.deviceType == 10" :span="2">
<span>时长(分钟)</span>
</el-col>
<!-- 行车诱导 -->
<el-col v-if="tableInfo.deviceType == 12" :span="4">
<el-select v-model="tableInfo.zx_controlModel" placeholder="请选择模式" transfer="true"
:popper-append-to-body="false">
<el-option label="手动模式" value="00"></el-option>
<el-option label="自动模式" value="01"></el-option>
<el-option label="万年历" value="02"></el-option>
</el-select>
</el-col>
<el-col v-if="tableInfo.deviceType == 12 && tableInfo.zx_controlModel == '01'" :span="4">
<el-time-picker v-model="tableInfo.zx_time" is-range style="" range-separator="-" placeholder="选择时间"
value-format="HH:mm" format="HH:mm">
</el-time-picker>
</el-col>
<el-col v-if="tableInfo.deviceType == 12" :span="4">
<el-select v-model="tableInfo.zx_state" placeholder="工作状态">
<el-option v-for="item in gzztOptions" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-col>
<!-- 语音广播 -->
<el-col v-if="tableInfo.deviceType == 5" :span="4">
<el-select v-model="tableInfo.zx_operationType" placeholder="请选择">
<el-option label="自定义发布" :value="1"></el-option>
<el-option label="智能发布" :value="2"></el-option>
</el-select>
</el-col>
<el-col v-if="tableInfo.deviceType == 5 && tableInfo.zx_operationType == 1" :span="4">
<el-input v-model="tableInfo.zx_content" placeholder="请输入发布内容"></el-input>
</el-col>
</el-row>
<el-row>
<el-col :span="2">
<p>恢复操作:</p>
</el-col>
<!-- 可变信息标识 -->
<el-col v-if="tableInfo.deviceType == 2" :span="4">
<el-select v-model="tableInfo.hf_operationType" placeholder="请选择">
<el-option label="自定义发布" :value="1"></el-option>
<el-option label="还原上次" :value="2"></el-option>
</el-select>
</el-col>
<el-col v-if="tableInfo.deviceType == 2 && tableInfo.hf_operationType == 1" :span="4">
<el-input @click.native="clickQbb('hf_content')" placeholder="请选择" v-model="tableInfo.hf_content" readonly>
<i slot="suffix" class="el-input__icon el-icon-search"></i>
</el-input>
</el-col>
<!-- 疲劳唤醒 -->
<el-col v-if="tableInfo.deviceType == 10" :span="4">
<el-select v-model="tableInfo.hf_name" placeholder="工作模式">
<el-option v-for="item in gzmsOptions" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-col>
<el-col v-if="tableInfo.deviceType == 10" :span="4">
<el-input-number placeholder="" v-model="tableInfo.hf_operationDuration" :min="0"
:max="999"></el-input-number>
</el-col>
<el-col v-if="tableInfo.deviceType == 10" :span="2">
<span>时长(分钟)</span>
</el-col>
<!-- 行车诱导 -->
<el-col v-if="tableInfo.deviceType == 12" :span="4">
<el-select v-model="tableInfo.hf_controlModel" placeholder="请选择模式" transfer="true"
:popper-append-to-body="false">
<el-option label="手动模式" value="00"></el-option>
<el-option label="自动模式" value="01"></el-option>
<el-option label="万年历" value="02"></el-option>
</el-select>
</el-col>
<el-col v-if="tableInfo.deviceType == 12 && tableInfo.hf_controlModel == '01'" :span="4">
<el-time-picker v-model="tableInfo.hf_time" is-range style="" range-separator="-" placeholder="选择时间"
value-format="HH:mm" format="HH:mm">
</el-time-picker>
</el-col>
<el-col v-if="tableInfo.deviceType == 12" :span="4">
<el-select v-model="tableInfo.hf_state" placeholder="工作状态">
<el-option v-for="item in gzztOptions" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-col>
<!-- 语音广播 -->
<!-- <el-col v-if="tableInfo.deviceType == 5" :span="4">
<el-select v-model="tableInfo.hf_operationType" placeholder="请选择">
<el-option label="自定义发布" :value="1"></el-option>
<el-option label="智能发布" :value="2"></el-option>
</el-select>
</el-col> -->
<el-col v-if="tableInfo.deviceType == 5 && tableInfo.hf_operationType == 1" :span="4">
<el-input v-model="tableInfo.hf_content" placeholder="请输入发布内容"></el-input>
</el-col>
</el-row>
<!-- 情报板弹窗 -->
<QbbDialog :visible="isShowDialog" :info="qbbData" @close="onCloseDialog" @dialogSubmit="dialogSubmit" />
</div>
</template>
<script>
import Table from '@screen/components/Table.vue';
import Button from '@screen/components/Buttons/Button.vue';
import request from "@/utils/request";
import QbbDialog from "../qbbDialog/index.vue";
import { Message } from 'element-ui'
import { planDeviceOptions } from "@screen/utils/enum.js";
import { defaultTableInfo } from "../data";
export default {
name: 'ScopeTable',
components: {
Button,
Table,
QbbDialog
},
model: {
prop: 'visible',
event: 'update:value'
},
inject: ['loadData'],
props: {
visible: Boolean,
eventType: Number,
type: Number,
tableInfo: {
type: Object,
default: () => ({...defaultTableInfo})
}
},
data() {
return {
isShowDialog: false,
deviceOptions: planDeviceOptions,
zyOptions: [
{
value: 1,
label: '指定设备资源'
},
{
value: 2,
label: '事发上游最近'
},
{
value: 3,
label: '事发下游最近'
},
{
value: 4,
label: '最近公里数'
},
],
gzztOptions: [
{
value: "01",
label: "常亮"
},
{
value: "02",
label: "流水"
},
{
value: "03",
label: "闪烁"
},
{
value: "04",
label: "关闭",
}
],
gzmsOptions: [
{
value: "激光关闭",
label: "激光关闭"
},
{
value: "常亮模式",
label: "常亮模式"
},
{
value: "间隔100ms闪烁模式",
label: "间隔100ms闪烁模式"
},
{
value: "间隔200ms闪烁模式",
label: "间隔200ms闪烁模式",
},
{
value: "间隔500ms闪烁模式",
label: "间隔500ms闪烁模式",
},
{
value: "2次闪烁模式",
label: "2次闪烁模式"
},
{
value: "SOS模式",
label: "SOS模式"
},
{
value: "自定义模式1",
label: "自定义模式1",
},
{
value: "自定义模式2",
label: "自定义模式2",
},
{
value: "自定义模式3",
label: "自定义模式3",
}
],
qbbData: {},
sbOptions: [],
deviceType: 1,
clickQbbName: 1
}
},
watch: {
// async tableData(newValue) {
// console.log('newValue', newValue)
// if (newValue) {
// const item = this.tableData.find(it => it.searchRule == 1);
// console.log('item', item);
// if (item && item.deviceType) {
// let loadData = await this.loadData(item.deviceType);
// console.log('aa', loadData)
// this.sbOptions = loadData;
// }
// }
// }
},
async mounted() {
let loadData = await this.loadData(2);
// console.log('aa', loadData)
this.sbOptions = loadData;
},
methods: {
initData() {
},
async changeDeviceType(value) {
this.deviceType = value;
console.log('value', value)
this.sbOptions = await this.loadData(value);
},
onAdd(id) {
this.tableData.push({
deviceType: 1,
searchRule: 1,
qbb: ''
})
},
onDel(index) {
if (this.tableData.length <= 1) {
return Message.warning('最后一项不可删除!');
}
this.tableData.splice(index, 1)
},
clickQbb(name) {
this.clickQbbName = name;
// this.qbbData = this.tableData[index].dcInfoBoardTemplate;
this.isShowDialog = true;
},
onCloseDialog() {
this.isShowDialog = false;
},
dialogSubmit(data) {
console.log('qbbData',data)
this.tableInfo[this.clickQbbName] = data.content;
// this.tableData[this.index].otherConfig = JSON.stringify(data);
}
}
}
</script>
<style lang="scss" scoped>
.ScopeTable {
display: flex;
gap: 9px;
width: 100%;
// height: 768px;
min-height: 50px;
margin-top: 5px;
flex-direction: column;
p {
color: aqua;
}
::v-deep {
.el-table .el-table__cell {
padding: 0 5px;
}
.el-col-4 {
margin-left: 6px;
}
}
.mjs {
display: flex;
>div {
margin-right: 10px;
}
}
.ms {
width: 160px;
}
.plhx {
display: flex;
}
::v-deep {
.el-tag.el-tag--info {
max-width: 100px;
}
.el-range-editor--medium .el-range__icon,
.el-range-editor--medium .el-range__close-icon {
display: none;
}
}
}
.elButton {
background: #2ba8c3;
border-radius: 2px 2px 2px 2px;
color: #FFFFFF;
}
.elButton:hover,
.elButton:focus {
background: #2ba8c3;
border-radius: 2px 2px 2px 2px;
border-color: #FFFFFF;
color: #FFFFFF;
}
</style>

472
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/plan/addAndEditDialog/index copy.vue

@ -0,0 +1,472 @@
<template>
<Dialog v-model="modelVisible" :title="title" width="1330px">
<div class="EventAddPlanDialog">
<ElForm :model="formData" inline :rules="rules" ref="ruleForm">
<div class="first">
<el-form-item prop="eventCategory">
<el-radio-group v-model="formData.eventCategory" @input="changeRadio">
<el-radio-button :label="1">交通事件</el-radio-button>
<el-radio-button :label="2">感知事件</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item required label="预案名称:" prop="planName">
<el-input v-model="formData.planName" placeholder="请输入预案名称"></el-input>
</el-form-item>
<el-form-item required label="事件类型:" prop="eventType">
<el-select v-model="formData.eventType" placeholder="请选择事件类型" @change="changeEventType">
<el-option v-for="item in eventOptions" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="触发类型:" prop="triggerMechanism">
<el-select v-model="formData.triggerMechanism" placeholder="请选择触发类型">
<el-option v-for="item in mechanismOptions" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
</el-form-item>
</div>
<div class="second">
<el-row>
<el-col :span="2">
<div class="text"><i style="color: red">*</i>执行操作:</div>
</el-col>
<el-col :span="22">
<FormTable ref="secondFormTable" :tableData="secondFormData" :type="1"></FormTable>
</el-col>
</el-row>
</div>
<div class="third">
<el-row>
<el-col :span="2">
<div class="text"><i style="color: red">*</i>恢复操作:</div>
</el-col>
<el-col :span="22">
<FormTable ref="thirdFormTable" :tableData="thirdFormData" :type="2"></FormTable>
</el-col>
</el-row>
</div>
</ElForm>
</div>
<template #footer>
<Button style="background: #c9c9c9; padding: 0 24px"
@click.native="(modelVisible = false), (submitting = false)">取消</Button>
<Button style="padding: 0 24px" @click.native="handleSubmit" :loading="submitting">保存</Button>
</template>
</Dialog>
</template>
<script>
import Dialog from "@screen/components/Dialog/index";
import Form from "@screen/components/FormConfig";
import FormTable from "../formTable/index";
import Button from "@screen/components/Buttons/Button.vue";
import request from "@/utils/request";
import { Message } from "element-ui";
import { throttle } from "lodash";
import {
controlModelMap,
gzztMap,
gzmsMap,
eventSubClassMap,
trafficKV,
WarningTypeList as perceptionKV,
} from "@screen/utils/enum.js";
const typeMap = {
1: trafficKV,
2: perceptionKV,
};
export default {
name: "addAndEditDialog",
components: {
Dialog,
Form,
Button,
FormTable,
},
model: {
prop: "visible",
event: "close",
},
provide() {
return {
// loadData: throttle(this.loadData, 100)
loadData: this.loadData,
};
},
props: {
visible: Boolean,
detail: {
type: Object,
default: () => { },
},
},
data() {
return {
title: "新增预案",
dialogType: 1,
planId: 0,
submitting: false,
formData: {
eventCategory: 1,
eventType: 1,
triggerMechanism: "",
},
secondFormData: [
{
deviceType: 1,
searchRule: 1,
qbb: "",
},
],
thirdFormData: [
{
deviceType: 1,
searchRule: 1,
qbb: "",
},
],
deviceData: [],
eventOptions: trafficKV,
mechanismOptions: [
{
value: "1-1",
label: "追尾",
},
{
value: "1-2",
label: "侧翻",
},
{
value: "1-3",
label: "撞护栏",
},
{
value: "1-4",
label: "自然",
},
{
value: "1-5",
label: "其他事故",
},
],
rules: {
planName: [{ required: true, message: "请输入预案名称" }],
eventType: [
{ required: true, message: "请选择事件类型", trigger: "change" },
],
},
};
},
mounted() { },
computed: {
modelVisible: {
get() {
if (this.visible) {
if (Object.keys(this.detail).length > 0) {
this.title = "修改预案";
this.dialogType = 2;
this.eventOptions = typeMap[this.detail.eventCategory];
this.mechanismOptions =
eventSubClassMap[this.detail.eventCategory || 1][
this.detail.eventType
];
this.initData(this.detail.id);
} else {
this.title = "新增预案";
this.dialogType = 1;
this.formData = {
eventCategory: 1,
eventType: 1,
triggerMechanism: "",
};
this.secondFormData = [
{
deviceType: 1,
searchRule: 1,
qbb: "",
},
];
this.thirdFormData = [
{
deviceType: 1,
searchRule: 1,
qbb: "",
},
];
}
}
return this.visible;
},
set(val) {
this.$emit("close", val);
},
},
},
methods: {
initData(id = 1) {
request({
url: `/business/plans/list/${id}`,
method: "get",
})
.then((result) => {
if (result.code != 200) return Message.error(result?.msg);
let data = result.data;
let dcExecuteAction = result.data.dcExecuteAction;
this.planId = data.id;
this.formData = {
eventCategory: data.eventCategory,
planName: data.planName,
eventType: data.eventType,
triggerMechanism: data.triggerMechanism,
};
this.secondFormData = [];
this.thirdFormData = [];
dcExecuteAction.forEach((it) => {
let action = {};
if (it.otherConfig) {
let config = JSON.parse(it.otherConfig);
let qbb = "";
if (config.id) {
qbb = config.content;
config = { dcInfoBoardTemplate: config };
}
// if (config.state) {
// config.gzms = config.state
// }
action = { ...it, ...config, qbb: qbb };
}
if (it.deviceList) {
action.deviceList = it.deviceList
.split(",")
.map((str) => Number(str));
}
if (it.actionType == 1) {
this.secondFormData.push(action);
} else if (it.actionType == 2) {
this.thirdFormData.push(action);
}
});
console.log("secondFormData", this.secondFormData);
})
// console.log('secondFormData', this.secondFormData)
// }).catch((err) => {
// console.log(err)
// Message.error("", err);
// })
.catch((err) => {
console.log(err);
Message.error("查询事件预案列表失败", err);
});
},
async loadData(deviceType = 1) {
this.deviceData = [];
let result = await request({
url: `business/device/query?deviceType=${deviceType}`,
method: "get",
});
if (result.code != 200) return Message.error(result?.msg);
if (deviceType == 1) {
this.deviceData = result.data.filter((it) => it.childType !== "1-1");
} else {
this.deviceData = result.data;
}
return this.deviceData;
},
changeEventType(value = 1) {
this.mechanismOptions =
eventSubClassMap[this.formData.eventCategory || 1][value];
},
changeRadio(value = 1) {
this.formData.triggerMechanism = "";
this.eventOptions = typeMap[value];
this.changeEventType(1);
},
handleChange() { },
formatData(it, value = 1, id = "") {
let data = { ...it, actionType: value, emergencyPlansId: id };
if (
it.deviceList &&
typeof it.deviceList !== "string" &&
it.deviceList.length > 0
) {
data.deviceList = it.deviceList.join(",");
} else {
data.deviceList = "";
}
if (it.content) {
data.otherConfig = JSON.stringify({ content: it.content });
}
if (it.controlModel) {
let other = {
controlModel: it.controlModel,
controlModelName: controlModelMap[it.controlModel],
state: it.state,
name: gzztMap[it.state],
};
if (it.time && it?.time[0]) {
other = {
controlModel: it.controlModel,
controlModelName: controlModelMap[it.controlModel],
state: it.state,
name: gzztMap[it.state],
startTime: it.time[0],
endTime: it.time[1],
};
}
data.otherConfig = JSON.stringify(other);
}
if (it.gzms) {
data.otherConfig = JSON.stringify({
state: it.gzms,
name: gzmsMap[it.gzms],
operationDuration: it.operationDuration,
});
}
return data;
},
handleSubmit() {
this.$refs["ruleForm"].validate((valid) => {
if (valid) {
// this.submitting = false;
let secondFormTable = this.$refs["secondFormTable"].tableData || [];
let thirdFormTable = this.$refs["thirdFormTable"].tableData || [];
// let flg = false;
// for (let item of secondFormTable) {
// if (this.areAllValuesEmpty(item)) { flg = true; break };
// }
// if (flg) return Message.warning('');
// for (let item of thirdFormTable) {
// if (this.areAllValuesEmpty(item)) { flg = true; break };
// }
// if (flg) return Message.warning('');
// console.log('12', secondFormTable);
// console.log('34', thirdFormTable);
let dcArr = [];
let id = "";
if (this.dialogType == 2) id = this.planId;
secondFormTable.forEach((it) => {
dcArr.push(this.formatData(it, 1, id));
});
thirdFormTable.forEach((it) => {
dcArr.push(this.formatData(it, 2, id));
});
console.log({
...this.formData,
dcExecuteAction: dcArr,
});
// return;
if (this.dialogType == 1) {
//
request({
url: `/business/plans`,
method: "post",
data: {
...this.formData,
dcExecuteAction: dcArr,
},
})
.then((result) => {
if (result.code != 200) return Message.error(result?.msg);
Message.success("提交成功");
this.modelVisible = false;
})
.catch(() => {
Message.error("提交失败");
})
.finally(() => {
this.submitting = false;
this.$emit("reInitData", true);
});
} else if (this.dialogType == 2) {
//
request({
url: `/business/plans`,
method: "put",
data: {
...this.formData,
id: this.planId,
dcExecuteAction: dcArr,
},
})
.then((result) => {
if (result.code != 200) return Message.error(result?.msg);
Message.success("提交成功");
this.modelVisible = false;
})
.catch(() => {
Message.error("提交失败");
})
.finally(() => {
this.submitting = false;
this.$emit("reInitData", true);
});
}
}
});
},
areAllValuesEmpty(obj) {
console.log("ass", obj);
return Object.keys(obj).every(function (key) {
const value = obj[key];
return (
value == null ||
value === "" ||
(Array.isArray(value) && value.length === 0) ||
(value instanceof Object && Object.keys(value).length === 0)
);
});
},
},
};
</script>
<style lang="scss" scoped>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.24s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.EventAddPlanDialog {
gap: 9px;
width: 1280px;
height: 310px;
display: flex;
flex-direction: column;
.first,
.second,
.third {
padding: 5px 10px 8px;
background-color: #296887;
margin-bottom: 15px;
.text {
margin-top: 12px;
}
}
.form {
flex: 1;
overflow-y: auto;
}
.footer {
display: flex;
justify-content: end;
gap: 15px;
}
}
</style>

316
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/plan/addAndEditDialog/index.vue

@ -3,96 +3,42 @@
<div class="EventAddPlanDialog">
<ElForm :model="formData" inline :rules="rules" ref="ruleForm">
<div class="first">
<el-form-item prop="eventCategory">
<el-radio-group
v-model="formData.eventCategory"
@input="changeRadio"
>
<el-radio-button :label="1">交通事件</el-radio-button>
<el-radio-button :label="2">感知事件</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item required label="预案名称:" prop="planName">
<el-input
v-model="formData.planName"
placeholder="请输入预案名称"
></el-input>
<el-input v-model="formData.planName" placeholder="请输入预案名称"></el-input>
</el-form-item>
<el-form-item required label="事件类型:" prop="eventType">
<el-select
v-model="formData.eventType"
placeholder="请选择事件类型"
@change="changeEventType"
>
<el-option
v-for="item in eventOptions"
:key="item.value"
:label="item.label"
:value="item.value"
>
<el-select v-model="formData.eventType" placeholder="请选择事件类型" @change="changeEventType">
<el-option v-for="item in eventOptions" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="触发类型:" prop="triggerMechanism">
<el-select
v-model="formData.triggerMechanism"
placeholder="请选择触发类型"
>
<el-option
v-for="item in mechanismOptions"
:key="item.value"
:label="item.label"
:value="item.value"
>
<el-form-item label="细分类型:" prop="triggerMechanism">
<el-select v-model="formData.triggerMechanism" placeholder="请选择细分类型">
<el-option v-for="item in mechanismOptions" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
</el-form-item>
</div>
<div class="second">
<el-row>
<el-col :span="2">
<div class="text"><i style="color: red">*</i>执行操作:</div>
</el-col>
<div>
<el-row v-for="(item, index) in tableData" :key="item.id" class="rowBlock">
<el-col :span="22">
<FormTable
ref="secondFormTable"
:tableData="secondFormData"
:type="1"
></FormTable>
<ScopeTable :tableInfo="item"></ScopeTable>
</el-col>
</el-row>
</div>
<div class="third">
<el-row>
<el-col :span="2">
<div class="text"><i style="color: red">*</i>恢复操作:</div>
</el-col>
<el-col :span="22">
<FormTable
ref="thirdFormTable"
:tableData="thirdFormData"
:type="2"
></FormTable>
<ElButton class="elButton" icon="el-icon-plus" plain size="mini" @click.native="onAdd()" />
<ElButton class="elButton" icon="el-icon-delete" plain size="mini" @click.native="onDel(index)" />
</el-col>
</el-row>
</div>
</ElForm>
</div>
<template #footer>
<Button
style="background: #c9c9c9; padding: 0 24px"
@click.native="(modelVisible = false), (submitting = false)"
>取消</Button
>
<Button
style="padding: 0 24px"
@click.native="handleSubmit"
:loading="submitting"
>保存</Button
>
<Button style="background: #c9c9c9; padding: 0 24px"
@click.native="(modelVisible = false), (submitting = false)">取消</Button>
<Button style="padding: 0 24px" @click.native="handleSubmit" :loading="submitting">保存</Button>
</template>
</Dialog>
</template>
@ -104,21 +50,17 @@ import FormTable from "../formTable/index";
import Button from "@screen/components/Buttons/Button.vue";
import request from "@/utils/request";
import { Message } from "element-ui";
import { throttle } from "lodash";
import ScopeTable from "./ScopeTable.vue";
import { defaultTableInfo } from "../data";
import Table from '@screen/components/Table.vue';
import {
controlModelMap,
gzztMap,
gzmsMap,
eventSubClassMap,
trafficKV,
perceptionKV,
trafficKV
} from "@screen/utils/enum.js";
const typeMap = {
1: trafficKV,
2: perceptionKV,
};
export default {
name: "addAndEditDialog",
components: {
@ -126,6 +68,8 @@ export default {
Form,
Button,
FormTable,
Table,
ScopeTable
},
model: {
prop: "visible",
@ -141,7 +85,7 @@ export default {
visible: Boolean,
detail: {
type: Object,
default: () => {},
default: () => { },
},
},
data() {
@ -151,24 +95,10 @@ export default {
planId: 0,
submitting: false,
formData: {
eventCategory: 1,
eventType: 1,
triggerMechanism: "",
},
secondFormData: [
{
deviceType: 1,
searchRule: 1,
qbb: "",
},
],
thirdFormData: [
{
deviceType: 1,
searchRule: 1,
qbb: "",
},
],
tableData: [],
deviceData: [],
eventOptions: trafficKV,
mechanismOptions: [
@ -201,7 +131,7 @@ export default {
},
};
},
mounted() {},
mounted() { },
computed: {
modelVisible: {
get() {
@ -209,34 +139,17 @@ export default {
if (Object.keys(this.detail).length > 0) {
this.title = "修改预案";
this.dialogType = 2;
this.eventOptions = typeMap[this.detail.eventCategory];
this.mechanismOptions =
eventSubClassMap[this.detail.eventCategory || 1][
this.detail.eventType
];
this.mechanismOptions = eventSubClassMap[1][this.detail.eventType];
this.initData(this.detail.id);
} else {
this.title = "新增预案";
this.dialogType = 1;
this.formData = {
eventCategory: 1,
eventType: 1,
triggerMechanism: "",
};
this.secondFormData = [
{
deviceType: 1,
searchRule: 1,
qbb: "",
},
];
this.thirdFormData = [
{
deviceType: 1,
searchRule: 1,
qbb: "",
},
];
console.log('defaultTableInfo', { ...defaultTableInfo })
this.tableData = [{ ...defaultTableInfo }]
}
}
return this.visible;
@ -255,43 +168,38 @@ export default {
.then((result) => {
if (result.code != 200) return Message.error(result?.msg);
let data = result.data;
let dcExecuteAction = result.data.dcExecuteAction;
let dcExecuteAction = data.dcExecuteAction;
this.planId = data.id;
this.formData = {
eventCategory: data.eventCategory,
planName: data.planName,
eventType: data.eventType,
triggerMechanism: data.triggerMechanism,
};
this.secondFormData = [];
this.thirdFormData = [];
let dcArr = [];
dcExecuteAction.forEach((it) => {
let action = {};
if (it.otherConfig) {
let config = JSON.parse(it.otherConfig);
let qbb = "";
if (config.id) {
qbb = config.content;
config = { dcInfoBoardTemplate: config };
}
// if (config.state) {
// config.gzms = config.state
// }
action = { ...it, ...config, qbb: qbb };
let action = { ...it };
if (it.executeConfig) {
let executeConfig = JSON.parse(it.executeConfig);
let execute = this.mapKeys(executeConfig, 'zx_');
action = { ...action, ...execute };
}
if (it.recoverConfig) {
let recoverConfig = JSON.parse(it.recoverConfig);
let recover = this.mapKeys(recoverConfig, 'hf_');
action = { ...action, ...recover };
}
if (it.deviceList) {
action.deviceList = it.deviceList
action.devList = it.deviceList
.split(",")
.map((str) => Number(str));
}
if (it.actionType == 1) {
this.secondFormData.push(action);
} else if (it.actionType == 2) {
this.thirdFormData.push(action);
}
dcArr.push(action);
});
console.log("secondFormData", this.secondFormData);
this.tableData = dcArr;
console.log("tableData", this.tableData);
})
// console.log('secondFormData', this.secondFormData)
// }).catch((err) => {
@ -304,6 +212,12 @@ export default {
Message.error("查询事件预案列表失败", err);
});
},
mapKeys(obj, prefix) {
return Object.keys(obj).reduce((result, key) => {
result[`${prefix}${key}`] = obj[key];
return result;
}, {});
},
async loadData(deviceType = 1) {
this.deviceData = [];
let result = await request({
@ -319,87 +233,80 @@ export default {
return this.deviceData;
},
changeEventType(value = 1) {
this.mechanismOptions =
eventSubClassMap[this.formData.eventCategory || 1][value];
console.log('changeEventType', value)
this.mechanismOptions = eventSubClassMap[1][value];
this.formData.triggerMechanism = ''
},
changeRadio(value = 1) {
this.formData.triggerMechanism = "";
this.eventOptions = typeMap[value];
this.changeEventType(1);
handleChange() { },
onAdd() {
this.tableData.push({ ...defaultTableInfo })
},
handleChange() {},
formatData(it, value = 1, id = "") {
let data = { ...it, actionType: value, emergencyPlansId: id };
if (
it.deviceList &&
typeof it.deviceList !== "string" &&
it.deviceList.length > 0
) {
data.deviceList = it.deviceList.join(",");
} else {
data.deviceList = "";
}
if (it.content) {
data.otherConfig = JSON.stringify({ content: it.content });
onDel(index) {
if (this.tableData.length <= 1) {
return Message.warning('最后一项不可删除!');
}
this.tableData.splice(index, 1)
},
formatData(it) {
let data = {};
if (it.controlModel) {
let other = {
data = {
controlModel: it.controlModel,
controlModelName: controlModelMap[it.controlModel],
state: it.state,
name: gzztMap[it.state],
};
if (it.time && it?.time[0]) {
other = {
controlModel: it.controlModel,
controlModelName: controlModelMap[it.controlModel],
state: it.state,
name: gzztMap[it.state],
data = {
...data,
startTime: it.time[0],
endTime: it.time[1],
};
}
data.otherConfig = JSON.stringify(other);
}
if (it.gzms) {
data.otherConfig = JSON.stringify({
state: it.gzms,
name: gzmsMap[it.gzms],
operationDuration: it.operationDuration,
});
}
return data;
},
handleSubmit() {
this.$refs["ruleForm"].validate((valid) => {
if (valid) {
// this.submitting = false;
let secondFormTable = this.$refs["secondFormTable"].tableData || [];
let thirdFormTable = this.$refs["thirdFormTable"].tableData || [];
// let flg = false;
// for (let item of secondFormTable) {
// if (this.areAllValuesEmpty(item)) { flg = true; break };
// }
// if (flg) return Message.warning('');
// for (let item of thirdFormTable) {
// if (this.areAllValuesEmpty(item)) { flg = true; break };
// }
// if (flg) return Message.warning('');
// console.log('12', secondFormTable);
// console.log('34', thirdFormTable);
let dcArr = [];
let id = "";
if (this.dialogType == 2) id = this.planId;
secondFormTable.forEach((it) => {
dcArr.push(this.formatData(it, 1, id));
});
thirdFormTable.forEach((it) => {
dcArr.push(this.formatData(it, 2, id));
});
this.tableData.forEach(item => {
let dcData = {
id: item.id || '',
deviceType: item.deviceType,
searchRule: item.searchRule,
number: item.number
}
if (this.dialogType == 2) {
dcData.emergencyPlansId = this.planId;
}
if (item.devList && item.devList.length > 0) {
dcData.deviceList = item.devList.join(",");
}
let zxData = {}, hfData = {};
Object.keys(item).forEach(key => {
if (/^zx_/.test(key)) {
let keyName = key.substring(3);
zxData[keyName] = item[key];
}
if (/^hf_/.test(key)) {
let keyName = key.substring(3);
hfData[keyName] = item[key];
}
})
if (item.deviceType == 12) {
zxData = this.formatData(zxData);
hfData = this.formatData(hfData);
}
dcData.executeConfig = JSON.stringify(zxData);
dcData.recoverConfig = JSON.stringify(hfData);
dcArr.push(dcData);
})
console.log({
...this.formData,
dcExecuteAction: dcArr,
dcExecuteAction: dcArr
});
// return;
if (this.dialogType == 1) {
@ -507,5 +414,26 @@ export default {
justify-content: end;
gap: 15px;
}
.rowBlock {
background-color: #296887;
padding: 5px 10px;
margin-bottom: 10px;
}
}
.elButton {
background: #2ba8c3;
border-radius: 2px 2px 2px 2px;
color: #FFFFFF;
}
.elButton:hover,
.elButton:focus {
background: #2ba8c3;
border-radius: 2px 2px 2px 2px;
border-color: #FFFFFF;
color: #FFFFFF;
}
</style>

58
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/plan/data.js

@ -1,5 +1,4 @@
import * as PresetFormItems from "@screen/pages/control/event/event/FormEvent/PresetFormItems.js";
import { merge, cloneDeep } from "lodash";
import { planDeviceOptions } from "@screen/utils/enum.js";
export const searchFormList = [
{
@ -7,50 +6,19 @@ export const searchFormList = [
key: "planName",
},
{
label: "事件分类:",
key: "eventCategory",
type: "RadioGroup",
label: "设备类型:",
key: "deviceType",
type: "select",
options: {
options: [
{
key: "1",
label: "交通事件",
},
{
key: "2",
label: "感知事件",
},
],
options: planDeviceOptions,
},
},
// {
// label: "时间范围:",
// key: "daterange",
// required: false,
// type: "datePicker",
// options: {
// type: "daterange",
// format: "yyyy-MM-dd HH:mm:ss",
// valueFormat: "yyyy-MM-dd HH:mm:ss",
// },
// },
// {
// ...PresetFormItems.station,
// label: "开始桩号:",
// required: false,
// },
// merge(cloneDeep(PresetFormItems.station), {
// options: {
// options: [
// {
// key: "endStakeMark[0]",
// },
// {
// key: "endStakeMark[1]",
// },
// ],
// },
// label: "结束桩号:",
// required: false,
// }),
];
export const defaultTableInfo = {
deviceType: 2,
searchRule: 1,
number: 0,
// zx_operationType: 2,
// hf_operationType: 2,
};

14
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/plan/index.vue

@ -24,9 +24,9 @@
<!-- 内容 -->
<div class="body">
<Table :data="tableData">
<ElTableColumn prop="strEventCategory" label="事件分类" width="100" align="center" />
<ElTableColumn prop="strEventType" label="事件类型" width="100" align="center" />
<!-- <ElTableColumn prop="strEventCategory" label="事件分类" width="100" align="center" /> -->
<ElTableColumn prop="planName" label="预案名称" width="140" align="center" />
<ElTableColumn prop="strEventType" label="事件类型" width="140" align="center" />
<ElTableColumn prop="triggeringCondition" label="检索条件" width="140" />
<ElTableColumn prop="deviceType" label="设备类型" />
<ElTableColumn prop="controllableDeviceName" label="可控设备" />
@ -63,12 +63,7 @@ import request from "@/utils/request";
import { confirm } from "@screen/utils/common";
import { Message } from 'element-ui'
import { searchFormList } from './data';
import { WarningType, trafficType } from "@screen/utils/enum.js";
const eventTypeMap = {
1: trafficType,
2: WarningType,
}
import { trafficType } from "@screen/utils/enum.js";
export default {
name: 'controlEventPlan',
@ -106,8 +101,7 @@ export default {
}).then((result) => {
if (result.code != 200) return Message.error(result?.msg);
result.rows.forEach(it => {
it.strEventCategory = it.eventCategory == 1 ? '交通事件' : '感知事件';
it.strEventType = eventTypeMap[it.eventCategory || 0][it.eventType];
it.strEventType = trafficType[it.eventType];
// it.strDeviceType = deviceMap[it.deviceType];
})
this.tableData = result.rows;

34
ruoyi-ui/src/views/JiHeExpressway/pages/control/event/plan/qbbDialog/index.vue

@ -1,25 +1,17 @@
<template>
<Dialog v-model="modelVisible" title="选择情报板" width="550px">
<div class="EventAddPlanDialog">
<Dialog v-model="modelVisible" title="选择情报板" width="700px">
<div class="PlanQbbDialog">
<vuescroll :ops="scrollOptions" class="listBox">
<div v-for="item in templateAvailable" :key="item.dictValue">
<!-- 原来是<el-collapse v-model="activeNames"> -->
<h3>{{ item.dictLabel }}</h3>
<div v-for="(itm, indx) in item.list" :key="indx" class="tplItem">
<!-- 模板内容 -->
<BoardTplPreview
class="boardPreview"
boardWH="1400*200"
:tpl="itm"
></BoardTplPreview>
<BoardTplPreview class="boardPreview" boardWH="1400*200" :tpl="itm"></BoardTplPreview>
<!-- 操作按钮 -->
<div class="infoBtnBox infoBtnBoxSm">
<p class="btn">
<el-radio
v-model="radio1"
:label="itm.id"
@input="changeRadio(itm)"
/>
<el-radio v-model="radio1" :label="itm.id" @input="changeRadio(itm)" />
</p>
</div>
</div>
@ -28,17 +20,9 @@
</div>
<template #footer>
<Button
style="background: #c9c9c9; padding: 0 24px"
@click.native="(modelVisible = false), (submitting = false)"
>取消</Button
>
<Button
style="padding: 0 24px"
@click.native="handleSubmit"
:loading="submitting"
>确认</Button
>
<Button style="background: #c9c9c9; padding: 0 24px"
@click.native="(modelVisible = false), (submitting = false)">取消</Button>
<Button style="padding: 0 24px" @click.native="handleSubmit" :loading="submitting">确认</Button>
</template>
</Dialog>
</template>
@ -67,7 +51,7 @@ export default {
visible: Boolean,
info: {
type: Object,
default: () => {},
default: () => { },
},
},
data() {
@ -255,7 +239,7 @@ export default {
opacity: 0;
}
.EventAddPlanDialog {
.PlanQbbDialog {
gap: 9px;
width: 650px;
height: 510px;

4
ruoyi-ui/src/views/JiHeExpressway/pages/control/manual/statistic/components/Eventfiltering/trafficIncidentsCharts.js

@ -50,6 +50,7 @@ var options = {
fontSize: 18,
align: "center",
},
minInterval: 1,
type: "value",
axisLine: {
show: false,
@ -70,6 +71,7 @@ var options = {
color: "#fff",
fontSize: "18px",
formatter: (value) => {
console.log(789789, value);
return value;
},
},
@ -78,7 +80,7 @@ var options = {
series: [
{
// name: '审限内结案率',
data: [5, 10, 20, 30],
data: [],
type: "pictorialBar",
symbol: "roundRect",
symbolRepeat: true,

3
ruoyi-ui/src/views/JiHeExpressway/pages/control/manual/statistic/components/Sitefiltering/trafficIncidentsCharts.js

@ -50,6 +50,7 @@ var options = {
fontSize: 18,
align: "center",
},
minInterval: 1,
type: "value",
axisLine: {
show: false,
@ -78,7 +79,7 @@ var options = {
series: [
{
// name: '审限内结案率',
data: [5, 10, 20, 30],
data: [],
type: "pictorialBar",
symbol: "roundRect",
symbolRepeat: true,

3
ruoyi-ui/src/views/JiHeExpressway/pages/control/manual/statistic/components/Timefiltering/trafficIncidentsCharts.js

@ -50,6 +50,7 @@ var options = {
fontSize: 18,
align: "center",
},
minInterval: 1,
type: "value",
axisLine: {
show: false,
@ -78,7 +79,7 @@ var options = {
series: [
{
// name: '审限内结案率',
data: [5, 10, 20, 30],
data: [],
type: "pictorialBar",
symbol: "roundRect",
symbolRepeat: true,

4
ruoyi-ui/src/views/JiHeExpressway/pages/perception/trafficFlow/components/classification/index.vue

@ -1,6 +1,6 @@
<template>
<div class="classification">
<WgtTitle :title="'全路车流量状况'"></WgtTitle>
<WgtTitle :title="'在途车流量车型分类'"></WgtTitle>
<div class="board">
<!-- <div v-for="(item, index) in list" class="list">
<div class="title">{{ item.label }}</div>
@ -134,7 +134,7 @@ export default {
flex-direction: column;
.board {
flex:1;
flex: 1;
width: 100%;
padding: 0px 30px;
background: linear-gradient(180deg, rgba(6, 66, 88, 0.2) 0%, #064258 100%);

2
ruoyi-ui/src/views/JiHeExpressway/pages/perception/trafficFlow/components/flowstate/index.vue

@ -1,6 +1,6 @@
<template>
<div class="flowstate">
<WgtTitle title="在途车流量车型分类"></WgtTitle>
<WgtTitle title="全路车流量状况"></WgtTitle>
<div class="board">
<div class="tag1">方向济南</div>
<div class="tag2">方向菏泽</div>

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

@ -250,6 +250,11 @@ export const WarningTypeList = Object.keys(WarningType).map((key) => {
};
});
// 交通事件主类 {1: "交通事故" } 格式
export const trafficType = Object.keys(EventTopics).reduce((prev, now) => {
return { ...prev, [EventTopics[now]]: now };
}, {});
// 感知事件主类的子类(上方) warningSubclass
export const WarningSubclass = {
1: {
@ -327,104 +332,14 @@ export const WarningSubclass = {
},
};
//========= 交通事件 主类(key vulue)=========
export const trafficKV = [
{
value: 1,
label: "交通事故",
},
{
value: 2,
label: "车辆故障",
},
{
value: 3,
label: "交通管制",
},
{
value: 4,
label: "交通拥堵",
},
{
value: 5,
label: "非法上路",
},
{
value: 6,
label: "路障清除",
},
{
value: 7,
label: "施工建设",
},
{
value: 8,
label: "服务区异常",
},
{
value: 9,
label: "设施设备隐患",
},
{
value: 10,
label: "异常天气",
},
{
value: 11,
label: "其他事件",
},
];
//========= 感知事件 主类(key vulue)=========
export const perceptionKV = [
{
value: 1,
label: "交通拥堵",
},
{
value: 2,
label: "行人",
},
{
value: 3,
label: "非机动车",
},
{
value: 4,
label: "停车",
},
{
value: 5,
label: "违规驾驶",
},
{
value: 6,
label: "路障",
},
{
value: 7,
label: "道路施工",
},
{
value: 8,
label: "异常天气",
},
{
value: 9,
label: "护栏碰撞",
},
{
value: 10,
label: "交通事故",
},
{
value: 11,
label: "车辆故障",
},
{
value: 99,
label: "其他事件",
},
];
//========= 交通事件 主类(key vulue) =========
export const trafficKV = Object.keys(trafficType).map((key) => {
return {
value: key * 1,
label: trafficType[key],
};
});
//交通事件、感知事件 子类(key vulue)预案使用
export const eventSubClassMap = {
1: {
@ -452,49 +367,49 @@ export const eventSubClassMap = {
],
2: [
{
value: 1,
value: '1',
label: "高速主线",
},
{
value: 2,
value: '2',
label: "服务区",
},
{
value: 3,
value: '3',
label: "立交桥",
},
{
value: 4,
value: '4',
label: "收费站",
},
],
3: [
{
value: 1,
value: '1',
label: "主线关闭",
},
{
value: 2,
value: '2',
label: "主线限行",
},
{
value: 6,
value: '6',
label: "收费站关闭",
},
{
value: 7,
value: '7',
label: "收费站限行",
},
{
value: 10,
value: '10',
label: "匝道立交关闭",
},
{
value: 12,
value: '12',
label: "匝道立交限行",
},
{
value: 14,
value: '14',
label: "服务区关闭",
},
],
@ -558,43 +473,43 @@ export const eventSubClassMap = {
],
7: [
{
value: 1,
value: '1',
label: "道路养护施工",
},
{
value: 2,
value: '2',
label: "收费站养护施工",
},
{
value: 3,
value: '3',
label: "服务区养护施工",
},
{
value: 4,
value: '4',
label: "枢纽立交匝道养护施工",
},
{
value: 5,
value: '5',
label: "地方道路养护施工",
},
{
value: 6,
value: '6',
label: "道路工程建设施工",
},
{
value: 7,
value: '7',
label: "收费站工程建设施工",
},
{
value: 8,
value: '8',
label: "服务区工程建设施工",
},
{
value: 9,
value: '9',
label: "枢纽立交匝道工程建设施工",
},
{
value: 10,
value: '10',
label: "地方道路工程建设施工",
},
],
@ -614,27 +529,27 @@ export const eventSubClassMap = {
],
9: [
{
value: 1,
value: '1',
label: "摄像机",
},
{
value: 2,
value: '2',
label: "护栏",
},
{
value: 3,
value: '3',
label: "隔离栅",
},
{
value: 4,
value: '4',
label: "情报板",
},
{
value: 5,
value: '5',
label: "防炫板",
},
{
value: 6,
value: '6',
label: "其他",
},
],
@ -678,7 +593,7 @@ export const eventSubClassMap = {
],
11: [
{
value: 1,
value: '1',
label: "其他事件",
},
],
@ -927,10 +842,10 @@ export const planDeviceMap = {
};
//预案设备Options
export const planDeviceOptions = [
{
value: 1,
label: "摄像机",
},
// {
// value: 1,
// label: "摄像机",
// },
{
value: 2,
label: "可变信息标志",
@ -945,7 +860,7 @@ export const planDeviceOptions = [
// },
{
value: 5,
label: "路段语音广播",
label: "语音广播",
},
// {
// value: 6,
@ -955,17 +870,17 @@ export const planDeviceOptions = [
// value: 7,
// label: "毫米波雷达",
// },
{
value: 8,
label: "合流区预警",
},
// {
// value: 8,
// label: "合流区预警",
// },
// {
// value: 9,
// label: "智慧锥桶",
// },
{
value: 10,
label: "激光疲劳唤醒",
label: "疲劳唤醒",
},
// {
// value: 11,
@ -977,12 +892,12 @@ export const planDeviceOptions = [
},
{
value: 13,
label: "智能设备箱",
},
{
value: 14,
label: "光线在线监测",
label: "设备箱",
},
// {
// value: 14,
// label: "光线在线监测",
// },
];
// 非机预警事件主类

2
ruoyi-ui/vue.config.js

@ -31,7 +31,7 @@ module.exports = {
devServer: {
host: "0.0.0.0",
port: port,
// https: true,
https: true,
open: true,
proxy: {
// detail: https://cli.vuejs.org/config/#devserver-proxy

Loading…
Cancel
Save