| @ -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; | |||
| } | |||
| @ -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> | |||
| @ -0,0 +1,238 @@ | |||
| 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] || "其他错误"}` | |||
|       ); | |||
|       this.initLiveVideo(); | |||
|     }); | |||
| 
 | |||
|     // 视频断流
 | |||
|     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; | |||
| } | |||
| @ -0,0 +1,220 @@ | |||
| <template> | |||
|   <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"> | |||
|         <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> | |||
| </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, | |||
|   }, | |||
|   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> | |||
| Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB | 
| Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB | 
| Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB | 
| Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB | 
| @ -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> | |||
| @ -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> | |||