济菏高速业务端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

907 lines
28 KiB

<template>
<div class="HomeFrameControl">
<ElPopover trigger="manual" :value="activeIcon === 'FrameControl'" :visibleArrow="false" placement="left"
popper-class="global-input-search-popover" @hide="onHide()">
<el-tooltip slot="reference" effect="light" content="批量控制" placement="left">
<Button :class="['btn', { 'btn-active': activeIcon }]" @click.native="handleClick('FrameControl')">
<img src="@screen/images/home-FrameControl/FrameControl.svg" />
</Button>
</el-tooltip>
<el-tabs v-model="tabAction" @tab-click="tabClick">
<el-tab-pane label="批量管控" name="1"></el-tab-pane>
<el-tab-pane label="定时管控" name="2"></el-tab-pane>
<el-tab-pane label="管控记录" name="3"></el-tab-pane>
</el-tabs>
<div class="body2" >
<div class="title">批量控制</div>
<span class="close" @click="() => { this.activeIcon = null; }">
<i class="el-icon-close" />
</span>
<!-- 批量管控 -->
<div v-if="tabAction === '1'" style="width:1000px; min-height: 350px;">
<!-- 通用表单 -->
<Form v-model="data" labelWidth="90px" column="2" class="form" ref="FormConfigRef" :formList="formList" />
<!-- 设备管控表单 -->
<component ref="ControlComponent"
style="width: 100% !important;"
:is="componentMap[DeviceTopics[data.deviceType]]"
:isMultiControl="true"
:visible="true"
:selectItems="data.childType"
:selectedSizeMutl="data.screenSizeName"
@update:activeIcon="(val) => { this.activeIcon = val }"
@update:submitting="(val) => { submitting = val }"></component>
<!-- 部分设备手动确认 -->
<div v-if="hiddenDevices.indexOf(componentMap[DeviceTopics[data.deviceType]]) == -1" class="footer">
<Button @click.native="submitClick" :loading="submitting">
确认
</Button>
<Button style="background-color: rgba(0, 179, 204, 0.3)" @click.native="cancelClick"> 取消 </Button>
</div>
</div>
<!-- 定时管控 -->
<div v-if="tabAction === '2'" style="width:1000px; height: 350px; overflow-y:auto" class="cardPanel">
<Empty v-if="!data2.length" class="no-data" style="position: absolute">暂无数据</Empty>
<div v-else style="display: flex;width:100%;flex-direction: column;">
<div style="height:300px;width:100%;overflow-y: auto;display: flex;flex-wrap: wrap;">
<div class="cardBox" v-for="(item, index) in data2" :key="index">
<Card :buttonIcon="null" :keyMap="keyMap" :cardData="item" class="card" buttonText="详情">
<template #form-groupName="{ data }">
<el-tooltip effect="dark" :content="data.groupName" placement="top-start">
<div class="groupName">
{{ data.groupName }}
</div>
</el-tooltip>
</template>
<template #form-remark="{ data }">
<el-tooltip effect="dark" :content="data.remark" placement="top-start">
<div class="remark">
{{ data.remark }}
</div>
</el-tooltip>
</template>
<template #button>
<el-switch v-model="item.status" active-color="#0BD" inactive-color="#999" active-value="0"
inactive-value="1" @change="(value) => handleSwitcherChange(value, item)">
</el-switch>
<Button @click.native="() => goStrategy(item)">
管理
</Button>
</template>
</Card>
</div>
</div>
<!-- 分页 -->
<div class="footer2">
<ElPagination @current-change="bindTimeing" @size-change="onSizeChange2" width="'100%'"
:page-sizes="[10, 20, 30, 40, 50]" :page-size="searchData2.pageSize"
:current-page.sync="searchData2.pageNum" layout="total, sizes, prev, pager, next" :total="tableTotal2"
class="Pagination">
</ElPagination>
</div>
</div>
</div>
<!-- 管控记录 -->
<div v-if="tabAction === '3'"
style="width:1000px;height: 350px;display: flex;flex-direction: column; justify-content: center;align-items: flex-start;">
<Form v-model="dataRecord" style="width:100%;" labelWidth="90px" column="2" class="form" ref="FormRecordRef"
:formList="formRecord" />
<Table :data="tableData" height="500px" style="margin: 20px 20px 0px 20px;width:95%">
<ElTableColumn label="管控时间" prop="operTime" width="120" />
<ElTableColumn label="管控方式" prop="operType" width="80" />
<ElTableColumn label="操作人" prop="operName" width="80" />
<ElTableColumn label="设备类型" width="120">
<template slot-scope="scope">
<div>
<p v-for="item in scope.row.arydcDeviceType">{{ item }}</p>
</div>
</template>
</ElTableColumn>
<ElTableColumn label="设备名称">
<template slot-scope="scope">
<div>
<p v-for="item in scope.row.arydcDeviceName">{{ item }}</p>
</div>
</template>
</ElTableColumn>
<ElTableColumn label="执行内容" width="250" prop="remark" :show-overflow-tooltip="true" >
<template slot-scope="scope">
<div>
<p v-for="item in scope.row.aryRemark">{{ item }}</p>
</div>
</template>
</ElTableColumn>
<ElTableColumn label="结果" prop="status" width="80" align="center" header-align="center">
<template slot-scope="scope">
<div>
<p v-for="item in scope.row.aryState">
<i class="el-icon-success" style="font-size: 20px; color:#0c0;" v-if="item === 200"></i>
<i class="el-icon-error" style="font-size: 20px; color:#BBB;" v-else></i>
</p>
</div>
</template>
</ElTableColumn>
</Table>
<!-- 分页 -->
<div class="footer" style="width:100%">
<ElPagination @current-change="bindRecord" @size-change="onSizeChange" width="'100%'"
:page-sizes="[10, 20, 30, 40, 50]" :page-size="searchData.pageSize"
:current-page.sync="searchData.pageNum" layout="total, sizes, prev, pager, next" :total="tableTotal"
class="Pagination">
</ElPagination>
</div>
</div>
</div>
</ElPopover>
<AddNEditDialog v-model="isShowDialog" :propData="dialogData" @onSuccess="onSizeChange2" />
</div>
</template>
<script>
import AddNEditDialog from "@screen/pages/control/device/strategy/components/AddNEditDialog.vue";
import Button from "@screen/components/Buttons/Button.vue";
import Form from "@screen/components/FormConfig";
import Card from "@screen/components/Card1/index.vue";
import * as PresetFormItems from "@screen/common/PresetFormItems.js";
import { merge, cloneDeep } from "lodash";
import { markerClusterIns } from "@screen/pages/Home/components/RoadAndEvents/utils/map.js"
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 SmartDeviceParams from "@screen/pages/Home/components/Dialogs/SmartDevice/components/DeviceParamsMulti.vue";
import BroadcastParam from "@screen/pages/Home/components/Dialogs/Broadcast/components/BroadcastParamMulti.vue";
import FatigueWakesUpParam from "@screen/pages/Home/components/Dialogs/FatigueWakesUp/components/DeviceParam.vue";
import DrivingGuidanceParam from "@screen/pages/Home/components/Dialogs/DrivingGuidance/components/DeviceParam.vue";
import InfoBoardParam from "@screen/pages/Home/components/InfoBoard/InfoBoard.vue";
import request from "@/utils/request";
import { DeviceForMap } from "@screen/pages/Home/components/RoadAndEvents/utils/buttonEvent";
import { Message } from "element-ui";
import Table from '@screen/components/Table.vue';
import moment from "moment";
import { getDicts } from "@/api/system/dict/data";
import _ from "lodash";
// "设备箱": "SmartDeviceParams", 2024-07-03 删除
const componentMap = {
"语音广播": "BroadcastParam", "疲劳唤醒": "FatigueWakesUpParam",
"行车诱导": "DrivingGuidanceParam", "情报板": "InfoBoardParam",
}
const hiddenDevices = ["SmartDeviceParams", "InfoBoardParam"];
const controlMulti = Object.keys(componentMap);//6种批量控制
const DeviceTopics = {};//6种批量控制 事件专题 {key:label}
Object.keys(DeviceForMap).forEach(DeviceLabel => {
controlMulti.indexOf(DeviceLabel) !== -1 && (DeviceTopics[DeviceForMap[DeviceLabel].deviceType] = DeviceLabel);
});
const deviceTypeDefault = Object.keys(DeviceTopics)[1];
function changeHandle(data, formList) {
if (data.deviceType !== "2") { //情报板单独处理
const filterData = {};
data.direction && (filterData.direction = data.direction);
data.startStakeMark && (filterData.startStakeMark = data.startStakeMark);
data.endStakeMark && (filterData.endStakeMark = data.endStakeMark);
setDeviceOptions({ deviceType: data.deviceType }, filterData, formList);
data.childType = undefined;
data.screenSizeName = '';
} else {
setBoardSize(formList,data);
}
}
function changeHandleScreenSize( data,formList){
const filterData = {};
data.direction && (filterData.direction = data.direction);
data.startStakeMark && (filterData.startStakeMark = data.startStakeMark);
data.endStakeMark && (filterData.endStakeMark = data.endStakeMark);
if(data.screenSize && data.screenSize !== ''){
var arySize = data.screenSize.split('_')
filterData.childType = arySize[0];
data.screenSizeName = arySize[1];
}
setDeviceOptions({ deviceType: data.deviceType }, filterData, formList);
data.childType = undefined;
}
async function setBoardSize(formList,data){
// if(formList[4].options.options.length === 0){
const res = await getDicts("iot_board_pixel");
let boardSizeArr = []
res.data.forEach((item) => {
let size = item.dictLabel.substr(item.dictLabel.search(/\d/))
boardSizeArr.push({
label: item.dictLabel,
value: item.dictValue+'_'+size
});
});
formList[4].options.options = boardSizeArr;
data.screenSize = boardSizeArr[0].value;
var arySize = boardSizeArr[0].value.split('_')
setTimeout(() => {
data.screenSizeName = arySize[1];
}, 600);
const filterData = {childType:arySize[0]};
data.direction && (filterData.direction = data.direction);
data.startStakeMark && (filterData.startStakeMark = data.startStakeMark);
data.endStakeMark && (filterData.endStakeMark = data.endStakeMark);
setDeviceOptions({ deviceType: data.deviceType }, filterData, formList);
data.childType = undefined;
// }
}
async function setDeviceOptions(config, filterData, formList) {
const data = await getDeviceList(config.deviceType, filterData).then(async (data) => {
await delay(600);
return data;
});
formList[5].options.options = data.map(item => {
return {
label: `${item.deviceName}`,
value: JSON.stringify(
{
id: item.id,
iotDeviceId: item.iotDeviceId,
deviceType: item.deviceType,
otherConfig: item.otherConfig
}),
disabled: item.deviceState != 1
}
})
};
export default {
name: "HomeFrameControl",//设备控制
components: {
Button,
Form,
Card,
Table,
SmartDeviceParams,
BroadcastParam,
FatigueWakesUpParam,
DrivingGuidanceParam,
InfoBoardParam,
AddNEditDialog
},
watch: {
dataRecord(e) {
this.bindRecord()
},
},
data() {
return {
Enum_ControlType: [
{ key: "0", label: "手动控制" },
{ key: "1", label: "定时控制" },
{ key: "2", label: "批量控制" },
{ key: "3", label: "预案控制" }
],
activeIcon: null,
data: {
screenSizeName:""
},
hiddenDevices,
submitting: false,
formList: [
{
label: "设备类型:",
key: "deviceType",
type: "select",
default: deviceTypeDefault,
options: {
clearable: true,
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为传入的配置项
if (data.deviceType) {
changeHandle(data, formList);
} else {
data.childType = undefined;
}
}
},
},
{
label: "设备方向:",
key: "direction",
type: "select",
options: {
options: [
{ key: "1", label: "菏泽方向" },
{ key: "3", label: "济南方向" },
{ key: "2", label: "双向" },
],
},
ons: { //on监听 element事件
change(value, ...args) {
const { data, formList } = args.slice(-1)[0]; //data 为数据 formList为传入的配置项
if (data.deviceType) {
changeHandle(data, formList);
}
}
},
},
merge({}, PresetFormItems.startStation,
{
options: {
options: [
{
prefix: {
text: "K",
style: {
color: "#3DE8FF",
},
},
key: "startStakeMark[0]",
default: initSearch.startStakeMark[0],
rules: [
{
message: "请补全桩号",
callback(value, data) {
console.log(value, data.startStakeMark);
if (
!((value + "")?.trim() && (data.startStakeMark[1] + "")?.trim())
)
return false;
else return true;
},
},
],
ons: { //on监听 element事件
change(value, ...args) {
const { data, formList } = args.slice(-1)[0]; //data 为数据 formList为传入的配置项
data.deviceType && changeHandle(data, formList);
}
},
},
{
prefix: {
text: "+",
style: {
color: "#3DE8FF",
},
},
default: initSearch.startStakeMark[1],
key: "startStakeMark[1]",
ons: { //on监听 element事件
change(value, ...args) {
const { data, formList } = args.slice(-1)[0]; //data 为数据 formList为传入的配置项
data.deviceType && changeHandle(data, formList);
}
},
},
],
}
}),
merge({}, PresetFormItems.endStation, {
options: {
options: [
{
prefix: {
text: "K",
style: {
color: "#3DE8FF",
},
},
key: "endStakeMark[0]",
default: initSearch.endStakeMark[0],
rules: [
{
message: "请补全桩号",
callback(value, data) {
if (
!((value + "")?.trim() && (data.endStakeMark[1] + "")?.trim())
)
return false;
else return true;
},
},
],
ons: { //on监听 element事件
change(value, ...args) {
const { data, formList } = args.slice(-1)[0]; //data 为数据 formList为传入的配置项
data.deviceType && changeHandle(data, formList);
}
},
},
{
prefix: {
text: "+",
style: {
color: "#3DE8FF",
},
},
default: initSearch.endStakeMark[1],
key: "endStakeMark[1]",
ons: { //on监听 element事件
change(value, ...args) {
const { data, formList } = args.slice(-1)[0]; //data 为数据 formList为传入的配置项
data.deviceType && changeHandle(data, formList);
}
},
},
],
}
}),
{
label: "分辨率:",
key: "screenSize",
type: "select",
isAlone: true,
width: '100%',
options: {
clearable: true,
options: [],
["collapse-tags"]: true
},ons: { //on监听 element事件
change(value, ...args) {
const { data, formList } = args.slice(-1)[0]; //data 为数据 formList为传入的配置项
changeHandleScreenSize( data, formList);
}
},
visible: data => {
return data.deviceType === '2';
},
},
{
label: "设备名称:",
key: "childType",
type: "select",
isAlone: true,
width: '100%',
options: {
clearable: true,
options: [],
multiple: true,
["collapse-tags"]: true
},
visible: data => {
return true;
},
},
],
DeviceTopics,
componentMap,
tabAction: "1",
keyMap: [
{
key: "groupName",
label: "标题",
},
{
key: "remark",
label: "描述",
},
],
data2: [], //批量管控
tableTotal2: 0,
searchData2: {
pageSize: 20,
pageNum: 1,
},
dataRecord: { //管控记录
operType: "0",
operTime: []
},
formRecord: [
{
label: "管控方式:",
key: "operType",
type: "select",
options: {
options: [
{ key: "0", label: "手动控制" },
{ key: "1", label: "定时控制" },
{ key: "2", label: "批量控制" },
{ key: "3", label: "预案控制" }
],
},
},
{
label: "管控时间:",
key: "operTime",
required: false,
type: "datePicker",
options: {
style: "width: 96%",
type: "datetimerange",
format: "yyyy-MM-dd HH:mm:ss",
valueFormat: "yyyy-MM-dd HH:mm:ss",
},
},
],
tableTotal: 0,
tableData: [],
searchData: {
pageSize: 20,
pageNum: 1,
},
digResultVisible: false,
dialogData: {},
isShowDialog: false,
}
},
mounted() {
const self = this;
setTimeout(() => {
changeHandle(self.data, self.formList);
}, 500);
},
methods: {
onHide() {
this.tabAction = -1;
},
submitClick() {
this.$refs.ControlComponent?.handleSubmit();
},
cancelClick() {
this.activeIcon = null;
},
tabClick() {
if (this.tabAction === '2') {
this.bindTimeing();
} else if (this.tabAction === '3') {
this.dataRecord.operTime = [moment().startOf('month').format('YYYY-MM-DD 00:00:00'), moment().endOf('month').format('YYYY-MM-DD 23:59:59'),]
// this.bindRecord();
} else if (this.tabAction === '1') {
}
},
handleClick(type) {
this.activeIcon = this.activeIcon === type ? null : type;
if (this.activeIcon) {
this.tabAction = 0;
this.data['deviceType'] = deviceTypeDefault;
changeHandle(this.data, this.formList);
this.tabAction = '1';
// 解决弹窗冲突问题
setTimeout(() => {
let pop = document.getElementsByClassName('el-popover')
for(let i of pop){
if(i.getAttribute('aria-hidden') === 'false'){
i.style['z-index'] = '2001'
}
}
}, 100);
} else {
this.tabAction = -1;
}
},
//
filterEnd(data) {
this.activeIcon = null;
// this.filterData = data;
this.$parent.$refs.RoadAndEventsRef?.setFilterData?.(data);
},
goStrategy(data) {
this.isShowDialog = true;
this.dialogData = data;
// this.$router.push(`/control/device/strategy?id=${item.id}`);
},
async handleSwitcherChange(value, item) {
request({
url: `/business/dcBatchFunctionsJobGroup`,
method: "PUT",
data: {
id: item.id,
status: value
},
})
.then((result) => {
item.status = result.data.status;
if (result.code != 200) return;
Message.success(`操作成功`);
})
},
bindTimeing() {
request({
url: `/business/dcBatchFunctionsJobGroup/list`,
method: "get",
params: { ...this.searchData2 },
})
.then((result) => {
if (result.code != 200) return;
this.tableTotal2 = result.total;
this.data2 = result.rows;
})
.finally(() => {
});
},
onSizeChange2(pageSize = 20) {
this.data2 = [];
this.searchData2.pageSize = pageSize;
this.searchData2.pageNum = 1;
this.bindTimeing();
},
bindRecord() {
request({
url: `/business/dcOperLog/list`,
method: "get",
params: {
...this.searchData,
operType: this.dataRecord.operType,
startTime: this.dataRecord.operTime[0],
endTime: this.dataRecord.operTime[1]
}
})
.then((result) => {
const enum_deviceType = {
2: "可变信息标志",
5: "语音广播",
10: "疲劳唤醒",
12: "行车诱导",
13: "设备箱",
16: "远端机"
}
if (result.code != 200) return;
result.rows.forEach(e => {
const resJson = JSON.parse(e.jsonResult)
const resCodes = [];
const resCodesDeivce = [];
for(let i of resJson){
if(resCodesDeivce.indexOf(i.device) === -1){
resCodesDeivce.push(i.device)
resCodes.push(i.result.code)
}
}
const resParam = JSON.parse(e.operParam)[0].devices
const aryRemark = [];
e.remark.split(',').filter(x=>{
x.split('、').filter(y=>{
aryRemark.push(y)
})
})
e.operTime= moment(e.operTime).format('YYYY-MM-DD HH:mm:ss')
e.operType= _.find(this.Enum_ControlType, { key: e.operType }).label
e.arydcDeviceName = e.dcDeviceName.replace(/[\[\]]/g,'').split(',');
e.aryRemark = aryRemark;
e.aryState = resCodes
e.arydcDeviceType = resParam.map(x=>enum_deviceType[x.deviceType])
});
this.tableData = result.rows;
this.tableTotal = result.total;
})
.finally(() => {
});
},
onSizeChange(pageSize) {
this.tableData = [];
this.searchData.pageSize = pageSize;
this.searchData.pageNum = 1;
this.bindRecord();
},
showResult(res) {
this.digResultVisible = true
}
},
// 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;
// },
// immediate: true,
// deep: true
// }
// },
};
</script>
<style lang='scss' scoped>
::v-deep .el-table .cell {
text-overflow: clip !important;
-webkit-line-clamp: 100;
}
</style>
<style lang="scss">
div.el-popper.global-input-search-popover {
background: rgba(6, 66, 88, 0.8);
border: 1px solid rgba(42, 217, 253, 0.6);
position: relative;
padding-top: 36px;
transform: translateY(24px);
// margin-top: 6vh;
.body2 {
width: 1000px;
.title {
background: linear-gradient(90deg,
#237e9b 0%,
rgba(23, 145, 184, 0) 100%);
padding: 3px 9px;
position: absolute;
top: 0;
left: 0;
width: 100%;
}
.close {
padding: 3px 9px;
cursor: pointer;
position: absolute;
top: 0;
right: 0;
width: fit-content;
}
}
}
</style>
<style lang="scss" scoped>
.image {
width: 50vw;
height: 65vh;
}
.HomeFrameControl {
.btn {
padding: 9px;
background: linear-gradient(180deg, #152e3c 0%, #163a45 100%);
border-radius: 4px;
overflow: hidden;
height: unset;
border: 1px solid rgba(40, 144, 167, 1);
}
.btn-active {
background: linear-gradient(180deg, #005c79 0%, #009bcc 100%);
}
}
.cardPanel {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
}
.cardBox {
padding-right: 10px;
padding-bottom: 10px;
overflow-y: auto;
.card{
width: 187px;
}
}
.footer2 {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.groupName{
width: 100px;
white-space: nowrap; /* 确保文本在一行内显示 */
overflow: hidden; /* 超出容器部分的文本隐藏 */
text-overflow: ellipsis; /* 使用省略符号表示超出的文本 */
}
.remark {
width: 100px;
font-size: 14px;
line-height: 20px;
height: 40px;
overflow: hidden;
color: #00b3cc;
}
.btnResult {
background-color: #00b3cc;
color: white;
border: none;
padding: 5px 20px;
}
</style>
<style lang="scss" scoped>
.Pagination {
// color: #fff;
::v-deep {
>button,
>ul li {
background: #064258;
border-radius: 2px 2px 2px 2px;
opacity: 1;
color: #fff;
}
>button {
padding: 0 3px;
border: 1px solid #00b3cc;
}
>ul li {
background: linear-gradient(180deg, #004960 0%, #004b62 100%);
margin: 0 1.5px;
&.active {
background: linear-gradient(180deg, #005c79 0%, #009bcc 100%);
}
}
.el-pagination__total {
color: #fff;
}
.el-pagination__sizes {
.el-select {
.el-input {
width: 81px;
.el-input__inner {
font-size: 12px;
height: 23px;
background-color: #064258 !important;
}
}
.el-input__suffix {
.el-input__icon {
line-height: 23px;
}
}
}
}
.el-pagination__jump {
margin-left: 10px;
color: #fff;
font-size: 12px;
.el-input {
width: 27px;
margin: 0 3px;
.el-input__inner {
font-size: 12px;
background-color: #064258 !important;
border-radius: 2px 2px 2px 2px;
}
}
}
.btn-next {
margin-left: 6px;
}
.btn-prev {
margin-right: 6px;
}
}
::v-deep .el-pagination__total {
margin-left: 10px;
}
}
</style>