Browse Source

reactor:预警列表字段更改+新增巡查任务校验

liu_w601 3 months ago
parent
commit
e64b13e65e

+ 14 - 0
src/assets/scss/index.scss

@@ -279,4 +279,18 @@ html#baidu-map-hack {
   .el-select-dropdown__item.selected{
     color: #ff6a6c;
  }
+}
+
+.marsGreenGradientPnl{
+    height: 20px;
+    background: #172537;
+    border-radius: 4px;
+    color: #e8f2fe;
+    font-size: 12px;
+    display: flex;
+    align-items: center;
+    padding: 0 6px;
+}
+.art-video-player{
+  width: 100%!important;
 }

+ 7 - 1
src/components/base-panel/base-panel-right.vue

@@ -11,9 +11,15 @@
 <script>
 export default {
   name: 'BasePanelRight',
+  props:{
+    isHidePanel:{
+      type:Boolean,
+      default:false
+    }
+  },
   data() {
     return {
-      isHide: false
+      isHide: this.isHidePanel
     }
   },
   methods: {

+ 2 - 0
src/views/components/layerList/planDialog.vue

@@ -118,6 +118,8 @@ export default {
       text-overflow: ellipsis;
       height: px-to-rem(36);
       line-height: px-to-rem(36);
+      font-weight: 400;
+      font-size: px-to-rem(16);
     }
     :deep(.el-tabs__item.is-active) {
       background: #498ee3;

+ 23 - 12
src/views/components/tools/index.vue

@@ -7,8 +7,8 @@
         <div class="tool" :class="{ active: isClActive }" @click="toCl"><i class="iconfont icon-icon_xiaogongju_20_s" style="font-size: 20px"></i></div>
       </div>
       <div class="measuring-tool" :class="{ clToolHide: !measureShow }">
-        <div class="ranging" :class="{ active: measureActive == 0 }" @click="measureRanging">测距</div>
-        <div class="measureArea" :class="{ active: measureActive == 1 }" @click="measureArea">测面</div>
+        <div class="ranging" :class="{ active: rangingActive }" @click="measureRanging">测距</div>
+        <div class="measureArea" :class="{ active: measureActive }" @click="measureArea">测面</div>
       </div>
     </div>
     <Controls v-if="isMapActive" />
@@ -27,7 +27,8 @@ export default {
     return {
       toolsShow: false,
       isMapActive: false,
-      measureActive: null,
+      measureActive: false,
+      rangingActive: false,
       isClActive: false,
       measureShow: false,
       distanceMeasure: null, // 测距工具实例
@@ -70,13 +71,20 @@ export default {
       }
     },
     measureRanging() {
-      this.rangingActive = this.rangingActive == 0 ? null : 0
-      if (this.rangingActive == 0) {
+      this.rangingActive = !this.rangingActive
+      if (this.rangingActive) {
+        this.measureActive = false
         const graphic = this.distanceMeasure.distance({
+          style: {
+            color: '#4487D7',
+            width: 20,
+            clampToGround: false //是否贴地
+          },
           showAddText: true,
           label: {
             // 自定义显示label的graphic类型
             type: 'div',
+            color: '#ffffff',
             updateText: function (text, graphic) {
               graphic.html = `<div class="marsGreenGradientPnl" >${text}</div>`
             },
@@ -91,15 +99,18 @@ export default {
       }
     },
     measureArea() {
-      this.rangingActive = this.rangingActive == 1 ? null : 1
-      if (this.rangingActive == 1) {
+      this.measureActive = !this.measureActive
+      if (this.measureActive) {
+        this.rangingActive = false
         const graphic = this.distanceMeasure.area({
           style: {
-            color: '#00fff2',
-            opacity: 0.4,
-            outline: true,
-            outlineColor: '#fafa5a',
-            outlineWidth: 1,
+            color: '#4F9FFF',
+            fill: true,
+            materialType:"Color",
+            fillColor: 'rgba(73,142,227,0.2)',
+            outline:true,
+            outlineColor: '#4F9FFF',
+            outlineWidth: 10,
             clampToGround: false //贴地
           }
         })

+ 1 - 1
src/views/hydrologic-info/left.vue

@@ -194,7 +194,7 @@ export default {
        window
         .requestSDK('/order/air/now', {adcode:'610481'}, {}, 'post')
         .then(async (res) => {
-          console.info(res)
+          
         })
     }
   }

+ 131 - 78
src/views/safety-inspection/addInspectionTask.vue

@@ -5,14 +5,14 @@
       <img src="@/assets/image/common/close.png" style="cursor: pointer" alt="" @click="closeModal" />
     </div>
     <div class="add-inspection-task-content">
-      <el-form ref="addTaskFormRef" :model="addTaskFrom" label-width="0.8rem" size="mini">
-        <el-form-item label="任务名称">
+      <el-form ref="addTaskFormRef" :rules="rules" :model="addTaskFrom" label-width="0.8rem" size="mini">
+        <el-form-item label="任务名称" prop="securityPatrolName">
           <el-input v-model="addTaskFrom.securityPatrolName" placeholder="请输入"></el-input>
         </el-form-item>
         <el-form-item label="任务内容">
           <el-input type="textarea" rows="4" v-model="addTaskFrom.securityPatrolContext" placeholder="请输入"></el-input>
         </el-form-item>
-        <el-form-item label="巡查点">
+        <el-form-item label="巡查点" prop="inspectionPoint">
           <div v-for="(item, index) in addTaskFrom.inspectionPoint" :key="index" class="ins-point">
             <el-select v-model="item.value" placeholder="请选择巡查点" :popper-append-to-body="false" popper-class="u-popper-select" @change="onPointSelect">
               <el-option :label="opt.devName" :value="opt.deviceCode" :disabled="opt.disabled" v-for="opt in inspectionOptions" :key="opt.deviceCode"></el-option>
@@ -21,9 +21,9 @@
             <img src="@/assets/image/safety-inspection/add.png" alt="" v-if="isLastItem(index)" @click="addOption" />
           </div>
         </el-form-item>
-        <el-form-item label="责任人">
+        <el-form-item label="责任人" prop="responsiblePerson">
           <el-select v-model="addTaskFrom.responsiblePerson" placeholder="请选择责任人" style="width: 100%" :popper-append-to-body="false" popper-class="u-popper-select">
-            <el-option :label="p.name" :value="p.name" v-for="(p,i) in personList" :key="i"></el-option>
+            <el-option :label="p.name" :value="p.name" v-for="(p, i) in personList" :key="i"></el-option>
           </el-select>
         </el-form-item>
         <el-form-item>
@@ -42,16 +42,16 @@
 import 'mars3d/mars3d.css'
 import * as mars3d from 'mars3d'
 import Vue from 'vue'
-import { toAddSecurityPatrol,endPatrol,startPatrol } from '@/api/securityPatrolApi'
+import { toAddSecurityPatrol, endPatrol, startPatrol } from '@/api/securityPatrolApi'
 import moment from 'moment'
 // 为了方便使用,绑定到原型链,在其他vue文件,直接this.mars3d 来使用
 Vue.prototype.mars3d = mars3d
 Vue.prototype.Cesium = mars3d.Cesium
 export default {
   name: 'addInspectionTask',
-  props:{
-    visible:Boolean,
-    inspectionOptions:Array
+  props: {
+    visible: Boolean,
+    inspectionOptions: Array
   },
   data() {
     return {
@@ -61,55 +61,98 @@ export default {
         inspectionPoint: [{ value: '' }],
         responsiblePerson: ''
       },
-      personList:[
+      rules: {
+        securityPatrolName: [{ required: true, message: '请输入任务名称', trigger: 'blur' }],
+        inspectionPoint: [{ required: true, validator: this.validateInspectionPoints, trigger: 'change' }],
+        responsiblePerson: [{ required: true, message: '请选择责任人', trigger: 'change' }]
+      },
+      personList: [
         {
-          name:'杨英'
+          name: '杨英'
         },
         {
-          name:'马刚'
+          name: '马刚'
         },
         {
-          name:'谢智宇'
+          name: '谢智宇'
         }
       ],
+      fieldErrors: {},
       isPatrolling: false,
       currentPatrolIndex: 0,
-      currentTime:"",
+      currentTime: '',
       patrolInterval: null,
-      xcId:''
+      xcId: ''
     }
   },
-  watch:{
-    visible(val){
-      if(val){
-        this.addTaskFrom={
-         securityPatrolName: '',
-         securityPatrolContext: '',
-         inspectionPoint: [{ value: '' }],
-         responsiblePerson: ''
-       }
+  watch: {
+    visible(val) {
+      if (val) {
+        this.addTaskFrom = {
+          securityPatrolName: '',
+          securityPatrolContext: '',
+          inspectionPoint: [{ value: '' }],
+          responsiblePerson: ''
+        }
       }
     }
-  },  
-  mounted(){
-     this.$globalEventBus.$on('closeVideoPlayAdd',()=>{
-        this.stopPatrol()
-     })
-     this.$globalEventBus.$on('toPlayNextVideoAdd',()=>{
-        this.goToNextPatrolPoint()
-     })
+  },
+  mounted() {
+    this.$globalEventBus.$on('closeVideoPlayAdd', () => {
+      this.stopPatrol()
+    })
+    this.$globalEventBus.$on('toPlayNextVideoAdd', () => {
+      this.goToNextPatrolPoint()
+    })
   },
   methods: {
-    closeModal(){
+    // 验证巡查点
+    validateInspectionPoints(rule, value, callback) {
+      this.fieldErrors = {}
+      let isValid = true
+      let hasDuplicate = false
+
+      // 检查是否所有巡查点都已选择
+      this.addTaskFrom.inspectionPoint.forEach((item, index) => {
+        if (!item.value) {
+          isValid = false
+          this.$set(this.fieldErrors, index, '请选择巡查点')
+        }
+      })
+
+      // 检查是否有重复选择
+      const selectedValues = this.addTaskFrom.inspectionPoint.map((item) => item.value).filter((v) => v)
+
+      const uniqueValues = new Set(selectedValues)
+      if (selectedValues.length !== uniqueValues.size) {
+        isValid = false
+        hasDuplicate = true
+      }
+
+      if (!isValid) {
+        if (hasDuplicate) {
+          callback(new Error('不能重复选择相同的巡查点'))
+        } else {
+          callback(new Error('请确保所有巡查点都已选择'))
+        }
+      } else {
+        callback()
+      }
+    },
+    closeModal() {
       this.$emit('closeAddTask')
     },
-    toSave(){
-      toAddSecurityPatrol(this.addTaskFrom).then((res)=>{
-        if(res.data){
-          this.$message.success('添加成功')
-          this.closeModal()
-        }else{
-          this.$message.error('添加失败')
+    toSave() {
+      this.$refs.addTaskFormRef.validate((valid) => {
+        if (valid) {
+          toAddSecurityPatrol(this.addTaskFrom).then((res) => {
+            if (res.data) {
+              this.$message.success('添加成功')
+              this.closeModal()
+            } else {
+              this.$message.error('添加失败')
+            }
+          })
         }
       })
     },
@@ -122,31 +165,40 @@ export default {
       })
     },
     startInspection() {
-      startPatrol(this.addTaskFrom).then((res)=>{
-        this.xcId = res.data.newSercurity.id
-      })
-      this.$emit('closeAddTask')
-      this.currentTime = moment().format('YYYY-MM-DD HH:mm:ss'); 
-      if (this.addTaskFrom.inspectionPoint === 0) {
-        this.$message.warning('请先添加至少一个巡查点')
-        return
-      }
-      this.addTaskFrom.inspectionPoint.forEach((item, index) => {
-        this.fetchUrl(item).then((res) => {
-          let url = ''
-           if(res.code == 200){
-           url = res.data.streamUrl
+      this.$refs.addTaskFormRef.validate((valid) => {
+        if (valid) {
+          startPatrol(this.addTaskFrom).then((res) => {
+            this.xcId = res.data.newSercurity.id
+          })
+          this.$emit('closeAddTask')
+          this.currentTime = moment().format('YYYY-MM-DD HH:mm:ss')
+          if (this.addTaskFrom.inspectionPoint === 0) {
+            this.$message.warning('请先添加至少一个巡查点')
+            return
           }
-          this.$set(this.addTaskFrom.inspectionPoint[index], 'url', url)
-        })
+          this.addTaskFrom.inspectionPoint.forEach((item, index) => {
+            this.fetchUrl(item).then((res) => {
+              let url = ''
+              if (res.code == 200) {
+                url = res.data.streamUrl
+              } else if (res.code == 400) {
+                this.$message.error(res.msg)
+              } else if (res.code == 4001) {
+                this.$message.warning('设备离线')
+              }
+
+              this.$set(this.addTaskFrom.inspectionPoint[index], 'url', url)
+            })
+          })
+          this.isPatrolling = true
+          this.currentPatrolIndex = 0
+          this.goToNextPatrolPoint()
+          // 设置定时器,每20秒切换到下一个点
+          this.patrolInterval = setInterval(() => {
+            this.goToNextPatrolPoint()
+          }, 20000)
+        }
       })
-      this.isPatrolling = true
-      this.currentPatrolIndex = 0
-      this.goToNextPatrolPoint()
-      // 设置定时器,每20秒切换到下一个点
-      this.patrolInterval = setInterval(() => {
-        this.goToNextPatrolPoint()
-      }, 20000)
     },
     fetchUrl(item) {
       return new Promise((resolve, reject) => {
@@ -173,23 +225,23 @@ export default {
       if (this.currentPatrolIndex >= this.addTaskFrom.inspectionPoint.length) {
         const endTime = moment().format('YYYY-MM-DD HH:mm:ss')
         this.stopPatrol()
-        this.$globalEventBus.$emit('clickVideoPlay', { visible: false,type:'add' })
-        endPatrol({id:this.xcId,endTime:endTime,startTime:this.currentTime}).then((res)=>{ 
+        this.$globalEventBus.$emit('clickVideoPlay', { visible: false, type: 'add' })
+        endPatrol({ id: this.xcId, endTime: endTime, startTime: this.currentTime }).then((res) => {
           this.$emit('refreshData')
         })
         return
       }
-      this.$globalEventBus.$emit('clickVideoPlay', { visible: false,type:"add" })
-      setTimeout(()=>{
+      this.$globalEventBus.$emit('clickVideoPlay', { visible: false, type: 'add' })
+      setTimeout(() => {
         const point = this.addTaskFrom.inspectionPoint[this.currentPatrolIndex]
         this.flyToPoint(point)
         // 播放视频
         this.playVideo(point)
         this.currentPatrolIndex++
-      },100)
+      }, 100)
     },
     playVideo(point) {
-        this.$globalEventBus.$emit('clickVideoPlay', { point: point, visible: true ,type:'add'})
+      this.$globalEventBus.$emit('clickVideoPlay', { point: point, visible: true, type: 'add' })
     },
     stopPatrol() {
       this.isPatrolling = false
@@ -214,12 +266,13 @@ export default {
     removeOption(index) {
       if (this.addTaskFrom.inspectionPoint.length > 1) {
         this.addTaskFrom.inspectionPoint.splice(index, 1)
+        this.$refs.addTaskFormRef.validateField('inspectionPoint');
       }
     }
   },
   destroyed() {
     this.stopPatrol()
-    this.$globalEventBus.$off('closeVideoPlayAdd');
+    this.$globalEventBus.$off('closeVideoPlayAdd')
   }
 }
 </script>
@@ -276,15 +329,15 @@ export default {
     .ins-point:last-child {
       margin-bottom: 0;
     }
-    :deep(.el-button){
-      line-height:initial;
-      background:rgba(79,159,255,0.8);
-      padding:px-to-rem(3) px-to-rem(7);
+    :deep(.el-button) {
+      line-height: initial;
+      background: rgba(79, 159, 255, 0.8);
+      padding: px-to-rem(3) px-to-rem(7);
     }
-    .cancelBtn{
-        border: 1px solid #4F9FFF;
-        color:#FFF;
-        background-color:transparent;
+    .cancelBtn {
+      border: 1px solid #4f9fff;
+      color: #fff;
+      background-color: transparent;
     }
   }
 }

+ 2 - 0
src/views/safety-inspection/index.vue

@@ -126,6 +126,8 @@ export default {
          that.fetchUrl(pointData).then((res) => {
           if(res.code == 4001){
             that.$message.warning('设备离线')
+          }else if(res.code == 400){
+            that.$message.error(res.msg)
           }else{
             const url = res.data.streamUrl
             that.$set(pointData, 'url', url)

+ 99 - 42
src/views/safety-inspection/left.vue

@@ -5,31 +5,43 @@
       <div class="card-item">
         <ul class="record-list">
           <template v-if="recordList.length > 0">
-            <li class="record-list-item" v-for="(item, index) in recordList" :key="index">
-              <div class="record-title">{{ item.securityPatrolName }}</div>
-              <div class="record-content">
-                <div class="record-item">
-                  <span>巡查开始时间:</span><span>{{ formatDate(item.startTime) }}</span>
-                </div>
-                <div class="record-item">
-                  <span>巡查结束时间:</span><span>{{ formatDate(item.endTime) }}</span>
-                </div>
-                <div class="record-item" style="display: flex; justify-content: space-between">
-                  <div>
-                    <span>巡查点位数:</span><span>{{ item.countPatrolPoints }}个</span>
-                  </div>
-                  <div>
-                     <span>巡查责任人:</span><span>{{ item.responsiblePerson }}</span>
-                  </div>
-                </div>
-              </div>
-            </li>
+            <el-collapse v-model="activeNames" class="v-collapse">
+              <template v-for="(item, index) in listItem">
+                <el-collapse-item :title="item.responsiblePerson" :name="index" :key="index">
+                  <template slot="title">
+                    <div class="title-num">
+                      <div class="left">{{ item.responsiblePerson }}</div>
+                      <div class="right">{{ item.lists.length }}条</div>
+                    </div>
+                  </template>
+                  <li class="record-list-item" v-for="(obj, i) in item.lists" :key="i">
+                    <div class="record-title">{{ obj.securityPatrolName }}</div>
+                    <div class="record-content">
+                      <div class="record-item">
+                        <span>巡查开始时间:</span><span>{{ formatDate(obj.startTime) }}</span>
+                      </div>
+                      <div class="record-item">
+                        <span>巡查结束时间:</span><span>{{ formatDate(obj.endTime) }}</span>
+                      </div>
+                      <div class="record-item" style="display: flex; justify-content: space-between">
+                        <div>
+                          <span>巡查点位数:</span><span>{{ obj.countPatrolPoints }}个</span>
+                        </div>
+                        <div>
+                          <span>巡查责任人:</span><span>{{ obj.responsiblePerson }}</span>
+                        </div>
+                      </div>
+                    </div>
+                  </li>
+                </el-collapse-item>
+              </template>
+            </el-collapse>
           </template>
           <template v-else>
             <div class="noData">
-                  <img src="@/assets/image/comprehensive/noData.png" alt="">
-                  <div class="txt">暂无巡查记录</div>
-                </div>
+              <img src="@/assets/image/comprehensive/noData.png" alt="" />
+              <div class="txt">暂无巡查记录</div>
+            </div>
           </template>
         </ul>
       </div>
@@ -46,15 +58,33 @@ export default {
   components: { BasePanelLeft, BaseHeader },
   data() {
     return {
-      recordList: []
+      recordList: [],
+      activeNames:[0,1,2,3]
     }
   },
-  mounted(){
+  mounted() {
     this.getSecurityPatrolList()
   },
+  computed: {
+    // 将数据按 responsiblePerson 分组
+    listItem() {
+      const grouped = {}
+      this.recordList.forEach((item) => {
+        const person = item.responsiblePerson
+        if (!grouped[person]) {
+          grouped[person] = {
+            responsiblePerson: person,
+            lists: []
+          }
+        }
+        grouped[person].lists.push(item)
+      })
+      return Object.values(grouped)
+    },
+  },
   methods: {
-     getSecurityPatrolList() {
-      getSecurityPatrolList({  finishedState: 1 }).then((res) => {
+    getSecurityPatrolList() {
+      getSecurityPatrolList({ finishedState: 1 }).then((res) => {
         this.recordList = res.data.records
       })
     },
@@ -70,19 +100,19 @@ export default {
   position: relative;
   height: 100%;
   z-index: 1;
-  .noData{
-      width: 100%;
-      height: 100%;
-      text-align: center;
-      img{
-        margin-top: px-to-rem(70);
-        margin-bottom: px-to-rem(20);
-      }
-      .txt{
-        color: #fff;
-        font-size: px-to-rem(14);
-      }
+  .noData {
+    width: 100%;
+    height: 100%;
+    text-align: center;
+    img {
+      margin-top: px-to-rem(70);
+      margin-bottom: px-to-rem(20);
     }
+    .txt {
+      color: #fff;
+      font-size: px-to-rem(14);
+    }
+  }
   .card-item {
     position: relative;
     width: 3.68rem;
@@ -95,11 +125,38 @@ export default {
     display: flex;
     flex-direction: column;
     padding: px-to-rem(10);
-    height:100%;
+    height: 100%;
     overflow: auto;
+    .v-collapse {
+      border-top: 0;
+      border-bottom: 0;
+      margin-top: px-to-rem(11);
+      :deep(.el-collapse-item__header) {
+        background: #2c5789;
+        font-weight: 500;
+        font-size: px-to-rem(14);
+        color: #ffffff;
+        padding: px-to-rem(5) px-to-rem(11);
+        height: auto;
+        line-height: initial;
+        border-bottom: 0;
+      }
+      :deep(.el-collapse-item__wrap) {
+        background-color: transparent;
+        border-bottom: 0;
+      }
+      :deep(.el-collapse-item__content) {
+        padding: px-to-rem(12) px-to-rem(0);
+      }
+      .title-num {
+        width: 90%;
+        display: flex;
+        justify-content: space-between;
+      }
+    }
     &::-webkit-scrollbar {
-          display: none;
-        }
+      display: none;
+    }
     .record-list-item {
       display: flex;
       flex-direction: column;
@@ -114,7 +171,7 @@ export default {
         display: flex;
         flex-direction: column;
         border-bottom: 1px solid #5a80b4;
-        padding-bottom:px-to-rem(20);
+        padding-bottom: px-to-rem(20);
         .record-item {
           font-weight: 400;
           font-size: px-to-rem(14);

+ 12 - 2
src/views/safety-inspection/right.vue

@@ -59,7 +59,7 @@
       </div>
     </div>
   </base-panel-right>
-   <AddInspectionTask :visible="addTaskShow" :inspectionOptions="inspectionOptions" @closeAddTask="closeAddTask"/>
+   <AddInspectionTask :visible="addTaskShow" :inspectionOptions="inspectionOptions" @closeAddTask="closeAddTask" @refreshData="toLoadLeftList"/>
 </div>
 </template>
 <script>
@@ -133,13 +133,23 @@ export default {
         this.getSecurityPatrolList()
         this.$emit('refreshLeftList')
     },
+    toLoadLeftList(){
+      this.$emit('refreshLeftList')
+    },
     toXc(item) {
       this.currentTime = moment().format('YYYY-MM-DD HH:mm:ss'); 
       this.xcId = item.id
       this.inspectionPoint = item.inspectionPoints
       this.inspectionPoint.forEach((item, index) => {
         this.fetchUrl(item).then((res) => {
-          const url = res.data.streamUrl
+          let url = ''
+           if(res.code == 4001){
+            this.$message.warning('设备离线')
+          }else if(res.code == 400){
+            this.$message.error(res.msg)
+          }else{
+             url = res.data.streamUrl
+          }
           this.$set(this.inspectionPoint[index], 'url', url)
         })
       })

+ 12 - 2
src/views/smart-early-warning/index.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="container">
-    <Left></Left>
-    <Right></Right>
+    <Left @toLoadDetail="toLoadDetail"></Left>
+    <Right :info="detailInfo"></Right>
   </div>
 </template>
 
@@ -11,6 +11,16 @@ import Right from './right.vue'
 export default {
     components:{
         Left,Right
+    },
+    data(){
+      return{
+        detailInfo:{}
+      }
+    },
+    methods:{
+      toLoadDetail(data){
+        this.detailInfo = data
+      }
     }
 }
 </script>

+ 28 - 18
src/views/smart-early-warning/left.vue

@@ -19,7 +19,7 @@
                     </div>
                   </template>
                   <div class="list-content">
-                    <div class="item" v-for="(item, index) in item.lists" :key="index">
+                    <div class="item" :class="{active:activeIndex == index+i}" v-for="(item, i) in item.lists" :key="i" @click="toLoadDetail(item,index,i)">
                       <div class="left">
                         <img :src="item.imgUrl" alt="" />
                       </div>
@@ -27,7 +27,7 @@
                         <div class="title">{{ item.alarmBody }}</div>
                         <div class="txt">{{ item.address }}</div>
                         <div class="txt">{{ item.alarmTime }}</div>
-                        <div class="jzDate">{{ item.deadline }}</div>
+                        <div class="jzDate">{{ item.duration }}</div>
                       </div>
                     </div>
                   </div>
@@ -56,8 +56,9 @@ import BaseMain from '@/components/base-main/base-main.vue'
 import ImgUrl from '@/assets/image/smart-early-warning/img1.png'
 import moment from 'moment'
 export default {
-  data() {
+  data() {    
     return {
+      activeIndex:null,
       searchVal: '',
       currentPage: 1,
       pageSize: 6,
@@ -81,7 +82,8 @@ export default {
               warningSourceName: 'AI预警',
               warningTypeName: '四乱预警',
               emergencyLevelName:'一级',
-              alarmTime:'2025-08-25 09:36:31'
+              alarmTime:'2025-08-25 09:36:31',
+              duration:'0小时0分11秒'
             }
           ]
         },
@@ -89,8 +91,7 @@ export default {
           name: '乱采预警',
           lists: [
              {
-              img: ImgUrl,
-              title: '乱建预警',
+              imgUrl: ImgUrl,
               deviceCode:'43030400831327000100',
               deviceName:'咸宁阳兴平仪空村综合机房',
               channelName:'咸宁阳兴平仪空村综合机房梯次LY',
@@ -99,11 +100,12 @@ export default {
               address: '咸宁阳兴平仪空村综合机房',
               confidenceLevel:'0.80615',
               orderId:'202508251210241338766566',
-              alarmBody:'流域乱预警',
+              alarmBody:'流域乱预警',
               warningSourceName: 'AI预警',
               warningTypeName: '四乱预警',
               emergencyLevelName:'一级',
-              alarmTime:'2025-08-25 09:36:31'
+              alarmTime:'2025-08-25 09:36:31',
+              duration:'0小时0分11秒'
             }
           ]
         },
@@ -111,8 +113,7 @@ export default {
           name: '乱堆预警',
           lists: [
             {
-              img: ImgUrl,
-              title: '乱建预警',
+              imgUrl: ImgUrl,
               deviceCode:'43030400831327000100',
               deviceName:'咸宁阳兴平仪空村综合机房',
               channelName:'咸宁阳兴平仪空村综合机房梯次LY',
@@ -121,11 +122,12 @@ export default {
               address: '咸宁阳兴平仪空村综合机房',
               confidenceLevel:'0.80615',
               orderId:'202508251210241338766566',
-              alarmBody:'流域乱预警',
+              alarmBody:'流域乱预警',
               warningSourceName: 'AI预警',
               warningTypeName: '四乱预警',
               emergencyLevelName:'一级',
-              alarmTime:'2025-08-25 09:36:31'
+              alarmTime:'2025-08-25 09:36:31',
+              duration:'0小时0分11秒'
             }
           ]
         },
@@ -133,8 +135,7 @@ export default {
           name: '乱占预警',
           lists: [
              {
-              img: ImgUrl,
-              title: '乱建预警',
+              imgUrl: ImgUrl,
               deviceCode:'43030400831327000100',
               deviceName:'咸宁阳兴平仪空村综合机房',
               channelName:'咸宁阳兴平仪空村综合机房梯次LY',
@@ -143,11 +144,12 @@ export default {
               address: '咸宁阳兴平仪空村综合机房',
               confidenceLevel:'0.80615',
               orderId:'202508251210241338766566',
-              alarmBody:'流域乱预警',
+              alarmBody:'流域乱预警',
               warningSourceName: 'AI预警',
               warningTypeName: '四乱预警',
               emergencyLevelName:'一级',
-              alarmTime:'2025-08-25 09:36:31'
+              alarmTime:'2025-08-25 09:36:31',
+              duration:'0小时0分11秒'
             }
           ]
         }
@@ -183,6 +185,10 @@ export default {
           // this.listItem = data.rows
         })
     },
+    toLoadDetail(data,index,i){
+      this.activeIndex = index+i;
+      this.$emit('toLoadDetail',data)
+    },
     handleSizeChange() {},
     handleCurrentChange() {}
   }
@@ -269,9 +275,9 @@ export default {
         display: flex;
         justify-content: flex-start;
         gap: px-to-rem(10);
-        background: rgba(79, 159, 255, 0.2);
+        background: rgba(60,90,135,0.2);
         border-radius: px-to-rem(4);
-        border: px-to-rem(1) solid #4f9fff;
+        border: px-to-rem(1) solid #2D4057;
         padding: px-to-rem(7);
         .left {
           img {
@@ -301,6 +307,10 @@ export default {
           }
         }
       }
+      .item.active{
+        border: px-to-rem(1) solid #4f9fff;
+        background: rgba(79,159,255,0.2);
+      }
     }
     :deep(.el-pagination) {
       margin-top: px-to-rem(22);

+ 35 - 59
src/views/smart-early-warning/right.vue

@@ -1,5 +1,5 @@
 <template>
-  <BasePanelRight>
+  <BasePanelRight :isHidePanel="isHide">
     <div class="num-area">
       <div class="txt-num"><img src="@/assets/image/smart-early-warning/yjNum.png" alt="">今日预警数量:3,258</div>
       <div class="txt-num"><img src="@/assets/image/smart-early-warning/jrDcl.png" alt="">今日待处理:3,258</div>
@@ -9,97 +9,97 @@
         <template v-slot:mainArea>
           <div class="detail-container">
             <Carousel :items="items" />
-            <el-collapse v-model="activeNames" @change="handleChange" class="v-collapse">
+            <el-collapse v-model="activeNames" class="v-collapse">
               <el-collapse-item title="预警来源" name="1">
                 <div class="form-content">
                   <div class="form-item">
                     <div class="label">摄像机编号:</div>
-                    <div class="value">{{ sourceObj.params1 }}</div>
+                    <div class="value">{{ info.deviceCode }}</div>
                   </div>
                   <div class="form-item">
                     <div class="label">摄像机名称:</div>
-                    <div class="value">{{ sourceObj.params2 }}</div>
+                    <div class="value">{{ info.deviceName }}</div>
                   </div>
                   <div class="form-item">
                     <div class="label">通道名称:</div>
-                    <div class="value">{{ sourceObj.params3 }}</div>
+                    <div class="value">{{ info.channelName }}</div>
                   </div>
-                  <div class="form-item">
+                  <!-- <div class="form-item">
                     <div class="label">摄像机厂家:</div>
-                    <div class="value">{{ sourceObj.params4 }}</div>
+                    <div class="value">{{ info.params4 }}</div>
                   </div>
                   <div class="form-item">
                     <div class="label">摄像机挂高:</div>
-                    <div class="value">{{ sourceObj.params5 }}</div>
-                  </div>
+                    <div class="value">{{ info.params5 }}</div>
+                  </div> -->
                   <div class="form-item">
                     <div class="label">摄像机经纬度:</div>
-                    <div class="value">{{ sourceObj.params6 }}</div>
+                    <div class="value">{{ info.longitude }},{{ info.latitude }}</div>
                   </div>
                   <div class="form-item">
                     <div class="label">摄像机地址:</div>
-                    <div class="value">{{ sourceObj.params7 }}</div>
+                    <div class="value">{{ info.address }}</div>
                   </div>
                   <div class="form-item">
                     <div class="label">置信度:</div>
-                    <div class="value">{{ sourceObj.params8 }}</div>
+                    <div class="value">{{ info.confidenceLevel }}</div>
                   </div>
-                  <div class="form-item">
+                  <!-- <div class="form-item">
                     <div class="label">水平方位角:</div>
-                    <div class="value">{{ sourceObj.params9 }}</div>
-                  </div>
+                    <div class="value">{{ info.params9 }}</div>
+                  </div> -->
                 </div>
               </el-collapse-item>
               <el-collapse-item title="事件信息" name="2">
                 <div class="form-content">
                   <div class="form-item">
                     <div class="label">事件编号:</div>
-                    <div class="value">{{ eventInfo.params1 }}</div>
+                    <div class="value">{{ info.orderId }}</div>
                   </div>
                   <div class="form-item">
                     <div class="label">事件标题:</div>
-                    <div class="value">{{ eventInfo.params2 }}</div>
+                    <div class="value">{{ info.alarmBody }}</div>
                   </div>
                   <div class="form-item">
                     <div class="label">预警来源:</div>
-                    <div class="value">{{ eventInfo.params3 }}</div>
+                    <div class="value">{{ info.warningSourceName }}</div>
                   </div>
                   <div class="form-item">
                     <div class="label">预警类型:</div>
-                    <div class="value">{{ eventInfo.params4 }}</div>
+                    <div class="value">{{ info.warningTypeName }}</div>
                   </div>
-                  <div class="form-item">
+                  <!-- <div class="form-item">
                     <div class="label">预警原因:</div>
-                    <div class="value">{{ eventInfo.params5 }}</div>
-                  </div>
+                    <div class="value">{{ info.params5 }}</div>
+                  </div> -->
                   <div class="form-item">
                     <div class="label">重要等级:</div>
-                    <div class="value">{{ eventInfo.params6 }}</div>
+                    <div class="value">{{ info.emergencyLevelName }}</div>
                   </div>
                   <div class="form-item">
                     <div class="label">预警时间:</div>
-                    <div class="value">{{ eventInfo.params7 }}</div>
+                    <div class="value">{{ info.alarmTime }}</div>
                   </div>
-                  <div class="form-item">
+                  <!-- <div class="form-item">
                     <div class="label">所属网格:</div>
-                    <div class="value">{{ eventInfo.params8 }}</div>
+                    <div class="value">{{ info.params8 }}</div>
                   </div>
                   <div class="form-item">
                     <div class="label">所属网格员:</div>
-                    <div class="value">{{ eventInfo.params9 }}</div>
+                    <div class="value">{{ info.params9 }}</div>
                   </div>
                   <div class="form-item">
                     <div class="label">事发地点:</div>
-                    <div class="value">{{ eventInfo.params10 }}</div>
+                    <div class="value">{{ info.params10 }}</div>
                   </div>
                   <div class="form-item">
                     <div class="label">经纬度:</div>
-                    <div class="value">{{ eventInfo.params11 }}</div>
+                    <div class="value">{{ info.params11 }}</div>
                   </div>
                   <div class="form-item">
                     <div class="label">事件描述:</div>
-                    <div class="value">{{ eventInfo.params12 }}</div>
-                  </div>
+                    <div class="value">{{ info.params12 }}</div>
+                  </div> -->
                 </div>
               </el-collapse-item>
             </el-collapse>
@@ -116,8 +116,12 @@ import BaseMain from '@/components/base-main/base-main.vue'
 import Carousel from './carousel.vue'
 import ImgUrl from '@/assets/image/smart-early-warning/banner.png'
 export default {
+  props:{
+    info:Object
+  },
   data() {
     return {
+      isHide:true,
       items: [
         {
           image: ImgUrl,
@@ -132,31 +136,6 @@ export default {
           thumbnail: ImgUrl
         }
       ],
-      sourceObj: {
-        params1: '43030400831327000100',
-        params2: '咸宁阳兴平仪空村综合机房',
-        params3: '咸宁阳兴平仪空村综合机房梯次LY',
-        params4: '大华',
-        params5: '35.0米',
-        params6: '112.985866,27.814898',
-        params7: '咸宁阳兴平仪空村综合机房',
-        params8: '0.80615',
-        params9: 'N8.37°E'
-      },
-      eventInfo: {
-        params1: '202508251210241338766566',
-        params2: '流域乱建预警',
-        params3: 'AI预警',
-        params4: '四乱预警',
-        params5: '暂无',
-        params6: '一级',
-        params7: '2025-08-25 09:36:31',
-        params8: '双马街道',
-        params9: '戴永强',
-        params10: '咸宁阳兴平仪空村综合机房',
-        params11: '112.986374,27.817957',
-        params12: '暂无'
-      },
       activeNames: ['1', '2']
     }
   },
@@ -164,9 +143,6 @@ export default {
     BasePanelRight,
     BaseMain,
     Carousel
-  },
-  methods: {
-    handleChange() {}
   }
 }
 </script>

+ 134 - 136
src/views/water-station-popup/index.vue

@@ -2,7 +2,7 @@
   <div class="water-station-popup-container" v-if="visible">
     <div class="water-station-popup-title">
       <span class="title-text">水文监测点</span>
-      <img src="@/assets/image/common/close.png" style="cursor: pointer" alt="" @click="visible = false" />
+      <img src="@/assets/image/common/close.png" style="cursor: pointer" alt="" @click="closeModal" />
     </div>
     <div class="water-station-popup-content">
       <el-radio-group v-model="typeVal" size="mini">
@@ -10,67 +10,93 @@
         <el-radio-button label="2">周边监控查询</el-radio-button>
       </el-radio-group>
       <div class="monitoring-info-container" v-if="typeVal === '1'">
-        <div class="info-panel">
-          <el-row :gutter="10">
-            <el-col :span="8" class="label-col">
-              <div class="label">实时高程:</div>
-              <div class="value"><span>{{swInfo.realTimeElevation}}</span><span>m</span></div>
-            </el-col>
-            <el-col :span="8" class="label-col">
-              <div class="label">1小时雨量:</div>
-              <div class="value"><span>{{swInfo.precipitationOneH}}</span><span>mm</span></div>
-            </el-col>
-            <el-col :span="8" class="label-col">
-              <div class="label">实时流量:</div>
-              <div class="value"><span>{{swInfo.realTimeDischarge}}</span><span>m³/s</span></div>
-            </el-col>
-          </el-row>
-          <el-row :gutter="10">
-            <el-col :span="8" class="label-col">
-              <div class="label">昨日最高:</div>
-              <div class="value"><span>{{swInfo.maxElevationYesterday}}</span><span>m</span></div>
-            </el-col>
-            <el-col :span="8" class="label-col">
-              <div class="label">当天雨量:</div>
-              <div class="value"><span>{{swInfo.precipitationToday}}</span><span>mm</span></div>
-            </el-col>
-            <el-col :span="8" class="label-col">
-              <div class="label">实时流速:</div>
-              <div class="value"><span>{{swInfo.realTimeVelocity}}</span><span>m³/s</span></div>
-            </el-col>
-          </el-row>
-        </div>
-        <div class="info-chart">
-          <LineChart style="height: 2.05rem" :options="swOption" />
-        </div>
+        <template v-if="swInfo.realTimeElevation">
+          <div class="info-panel">
+            <el-row :gutter="10">
+              <el-col :span="8" class="label-col">
+                <div class="label">实时高程:</div>
+                <div class="value">
+                  <span>{{ swInfo.realTimeElevation }}</span
+                  ><span>m</span>
+                </div>
+              </el-col>
+              <el-col :span="8" class="label-col">
+                <div class="label">1小时雨量:</div>
+                <div class="value">
+                  <span>{{ swInfo.precipitationOneH }}</span
+                  ><span>mm</span>
+                </div>
+              </el-col>
+              <el-col :span="8" class="label-col">
+                <div class="label">实时流量:</div>
+                <div class="value">
+                  <span>{{ swInfo.realTimeDischarge }}</span
+                  ><span>m³/s</span>
+                </div>
+              </el-col>
+            </el-row>
+            <el-row :gutter="10">
+              <el-col :span="8" class="label-col">
+                <div class="label">昨日最高:</div>
+                <div class="value">
+                  <span>{{ swInfo.maxElevationYesterday }}</span
+                  ><span>m</span>
+                </div>
+              </el-col>
+              <el-col :span="8" class="label-col">
+                <div class="label">当天雨量:</div>
+                <div class="value">
+                  <span>{{ swInfo.precipitationToday }}</span
+                  ><span>mm</span>
+                </div>
+              </el-col>
+              <el-col :span="8" class="label-col">
+                <div class="label">实时流速:</div>
+                <div class="value">
+                  <span>{{ swInfo.realTimeVelocity }}</span
+                  ><span>m³/s</span>
+                </div>
+              </el-col>
+            </el-row>
+          </div>
+          <div class="info-chart">
+            <LineChart style="height: 2.05rem" :options="swOption" v-if="chartShow"/>
+          </div>
+        </template>
+        <template>
+          <div class="noData">
+            <img src="@/assets/image/comprehensive/noData.png" alt="" />
+            <div class="txt">暂无实时监测信息</div>
+          </div>
+        </template>
       </div>
       <div class="monitor-container" v-else>
         <div class="monitor-search">
           <span>查询半径</span>
           <div class="input-bg">
-            <el-input v-model="radius" size="mini" placeholder="输入半径"></el-input>
+            <el-input v-model="radius" size="mini" type="number" min="0" placeholder="输入半径"></el-input>
           </div>
           <span>&nbsp;km&nbsp;</span>
           <el-button type="primary" size="mini" @click="search">查询</el-button>
         </div>
-        <template v-if="monitorList.length > 0">
-          <div class="monitor-list">
+        <div class="monitor-list">
+            <template v-if="monitorList.length > 0">
             <ul>
-              <li v-for="(item, index) in monitorList" :key="index" :class="{active:activeIndex == index}">
-                <span  @click="toLoadVideo(item,index)">{{item.name}}</span>
+              <li v-for="(item, index) in monitorList" :key="index" :class="{ active: activeIndex == index }">
+                <span @click="toLoadVideo(item, index)">{{ item.name }}</span>
               </li>
             </ul>
             <div class="monitor-video">
-             <Artplayer :option="videoOption" :style="style" v-if="artShow"/>
+              <Artplayer :option="videoOption" :style="style" v-if="artShow" />
             </div>
+          </template>
+          <template v-else>
+            <div class="noData">
+              <img src="@/assets/image/comprehensive/noData.png" alt="" />
+              <div class="txt">暂无摄像头</div>
+            </div>
+          </template>
           </div>
-        </template>
-        <template v-else>
-          <div class="noData">
-            <img src="@/assets/image/comprehensive/noData.png" alt="" />
-            <div class="txt">暂无摄像头</div>
-          </div>
-        </template>
       </div>
     </div>
   </div>
@@ -87,14 +113,15 @@ import moment from 'moment'
 let graphicsLayer = null
 export default {
   name: 'WaterStationPopup',
-  components: { LineChart,Artplayer },
+  components: { LineChart, Artplayer },
   data() {
     return {
       style: {
         width: '480px',
         height: '270px'
       },
-      artShow:false,
+      chartShow:false,
+      artShow: false,
       videoOption: {
         url: '',
         isLive: true, //使用直播模式,会隐藏进度条和播放时间
@@ -118,60 +145,20 @@ export default {
       visible: false,
       typeVal: '1',
       radius: '',
-      activeIndex:0,
+      activeIndex: 0,
       monitorList: [],
       stationGraphic: {},
-      swOption: {
-        backgroundColor: 'transparent',
-        color: ['#66DC95'],
-        grid: {
-          left: 16,
-          right: 16,
-          top: 30,
-          bottom: 0,
-          containLabel: true
-        },
-        textStyle: { color: '#fff' },
-        tooltip: {
-          trigger: 'axis',
-          axisPointer: { type: 'none' },
-          textStyle: { color: '#fff' },
-          extraCssText: 'background:#2F5481; border-radius: 8px;border:none ',
-          formatter: (params) => {
-            return `${params[0].axisValue} 高程${params[0].data}m`
-          }
-        },
-        xAxis: [{ type: 'category', boundaryGap: false, data: ['08/20', '08/21', '08/22', '08/23', '08/24', '08/25'] }],
-        yAxis: [{ type: 'value', name: '高程(m)', splitLine: { show: false }, min: 380, max: 420 }],
-        series: [
-          {
-            name: '高程(m)',
-            type: 'line',
-            smooth: true,
-            symbol: 'emptyCircle',
-            symbolSize: 6,
-            lineStyle: { width: 2 },
-            emphasis: { scale: 1.5 },
-            areaStyle: {
-              opacity: 0.8,
-              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
-                { offset: 1, color: 'rgba(102,220,149,0)' },
-                { offset: 0, color: ' rgba(102,220,149,0.25)' }
-              ])
-            },
-            data: [401, 398, 391, 410, 395, 400, 396]
-          }
-        ]
-      },
+      swOption: {},
       swInfo: {}
     }
   },
   mounted() {
     this.$globalEventBus.$on('clickWaterStation', (data) => {
-      if(data.layer.id == '2-2'){
+      this.chartShow = false
+      if (data.layer.id == '2-2') {
         this.getMonitoringStatistics(19)
-      }else{
-         this.getMonitoringStatistics(18)
+      } else {
+        this.getMonitoringStatistics(18)
       }
       this.stationGraphic = data.graphic
     })
@@ -191,9 +178,10 @@ export default {
     },
     getMonitoringStatistics(id) {
       getMonitoringStatistics({ stationId: id }).then((res) => {
+        this.visible = true
+        this.chartShow = true
         if (res.data) {
-          this.visible = true
-          this.swInfo = res.data.info 
+          this.swInfo = res.data.info
           const option = {
             backgroundColor: 'transparent',
             color: ['#66DC95'],
@@ -288,6 +276,9 @@ export default {
             ]
           }
           this.swOption = option
+        }else{
+          this.swOption = {}
+          this.swInfo = {}
         }
       })
     },
@@ -308,38 +299,40 @@ export default {
           'post'
         )
         .then((res) => {
-          if(res.code == 200){
+          if (res.code == 200) {
             this.monitorList = res.data
-            if(res.data.length > 0){
-              this.toLoadVideo(this.monitorList[0],0)
+            if (res.data.length > 0) {
+              this.toLoadVideo(this.monitorList[0], 0)
             }
           }
         })
     },
-    toLoadVideo(row,index){
+    toLoadVideo(row, index) {
       this.activeIndex = index
       this.artShow = false
-       window
-          .requestSDK(
-            '/ttvideo/video/player/getVideoRealtimeUrl',
-            {
-              deviceCode: row.id,
-              channelCode: row.channelCode,
-              netType: '1',
-              protocolType: 5,
-              streamType: 1
-            },
-            {},
-            'post'
-          )
-          .then((res) => {
-             if(res.code == 200){
-              this.artShow = true
-              this.$set(this.videoOption,'url',res.data.streamUrl)
-            }else if(res.code == 4001){
-              this.$message.warning('设备离线')
-            }
-          })
+      window
+        .requestSDK(
+          '/ttvideo/video/player/getVideoRealtimeUrl',
+          {
+            deviceCode: row.id,
+            channelCode: row.channelCode,
+            netType: '1',
+            protocolType: 5,
+            streamType: 1
+          },
+          {},
+          'post'
+        )
+        .then((res) => {
+          if (res.code == 200) {
+            this.artShow = true
+            this.$set(this.videoOption, 'url', res.data.streamUrl)
+          } else if (res.code == 4001) {
+            this.$message.warning('设备离线')
+          }else if(res.code == 400){
+            this.$message.error(res.msg)
+          }
+        })
     },
     addCameraToMap() {
       if (graphicsLayer) {
@@ -360,6 +353,11 @@ export default {
         }
       })
       graphicsLayer.addGraphic(graphic)
+    },
+    closeModal(){
+      this.visible = false
+      this.radius = ''
+      this.typeVal = '1'
     }
   }
 }
@@ -460,26 +458,26 @@ export default {
             &:hover {
               background-color: #498ee3;
             }
-            &.active{
+            &.active {
               background-color: #498ee3;
             }
           }
         }
       }
-      .noData {
-        width: 100%;
-        height: 100%;
-        text-align: center;
-        img {
-          margin-top: px-to-rem(70);
-          margin-bottom: px-to-rem(20);
-        }
-        .txt {
-          color: #fff;
-          font-size: px-to-rem(14);
-        }
-      }
     }
   }
 }
+.noData {
+  width: 100%;
+  height: 100%;
+  text-align: center;
+  img {
+    margin-top: px-to-rem(70);
+    margin-bottom: px-to-rem(20);
+  }
+  .txt {
+    color: #fff;
+    font-size: px-to-rem(14);
+  }
+}
 </style>