<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>