forked from drone/command-center-dashboard

罗广辉
2025-04-21 2800fa4f32f3900509cb4d6eefaf2bfaf54efdd7
src/views/Home/EventOverviewDetail/EventOverviewDetailRight.vue
@@ -1,10 +1,19 @@
<template>
   <UserOperate/>
   <div class="event-overviewdetail-right">
      <div>工单列表</div>
      <div>
   <div class="event-overviewdetail-right" :class="{ isMore }">
      <CommonTitle title="事件概况" :style="{ width: isMore ? pxToRem(820) : pxToRem(404) }" />
      <div class="content">
         <img
            class="leftArrow"
            :class="isMore ? 'rightArrow' : ''"
            :src="isMore ? rightArrowImg : leftArrowImg"
            @click="leftArrowFun"
            alt=""
         />
         <el-date-picker
            style="width: 100%"
         popper-class="custom-date-picker"
            class="ztzf-date-picker"
            v-model="timeArr"
            type="daterange"
            :value-format="timeFormat"
@@ -13,69 +22,142 @@
            end-placeholder="结束日期"
            @change="timeChange"
         />
      </div>
      <el-select style="width: 100%" v-model="params.device_sn" placeholder="机巢" filterable clearable @change="getList">
         <el-option v-for="item in deviceList" :label="item.nickname" :value="item.device_sn" :key="item.device_sn" />
      </el-select>
      <el-select v-model="params.word_order_type" placeholder="工单类型" filterable clearable @change="getList">
         <el-option v-for="item in workOrderType" :label="item.dictValue" :value="item.dictKey" :key="item.dictKey" />
      </el-select>
      <div class="statusList">
         <div v-for="item in statusList" @click="statusClick(item)" :class="{ active: item.active }">
            <div>{{ item.name }}</div>
            <div>{{ item.num || 0 }}</div>
         </div>
      </div>
         <el-select
         :teleported="false"
            class="ztzf-select"
            :style="{ width: pxToRem(174) }"
            v-model="params.device_sn"
            placeholder="机巢"
            filterable
            clearable
            @change="getList"
         >
            <el-option v-for="item in deviceList" :label="item.nickname" :value="item.device_sn" :key="item.device_sn" />
         </el-select>
      <el-checkbox-group v-model="params.eventKeys" @change="getList">
         <el-checkbox
            v-for="item in eventList"
            :label="item.name + ' ' + item.value"
            :value="item.status"
            :key="item.dictKey"
         />
      </el-checkbox-group>
         <el-select
         :teleported="false"
            class="ztzf-select"
            :style="{ width: pxToRem(174) }"
            v-model="params.word_order_type"
            placeholder="工单类型"
            filterable
            clearable
            @change="getList"
         >
            <el-option v-for="item in workOrderType" :label="item.dictValue" :value="item.dictKey" :key="item.dictKey" />
         </el-select>
      <div class="eventList">
         <div class="eventListItem" v-for="item in list">
            <img class="eventListItemImg"
         :src="item.photo_url ?
        item.photo_url.substring(0, item.photo_url.lastIndexOf('.')) + '_small' +
        item.photo_url.substring(item.photo_url.lastIndexOf('.'))
        : ''"
      alt=""
    />
            <div class="eventListItemText">
               <div class="eventListItemName">{{ item.event_name }}</div>
               <div @click="positioning(item)">定位</div>
               <div class="eventListItemTime">{{ item.create_time }}</div>
         <div class="statusList">
            <div
               class="statusItem"
               v-for="item in isMore ? statusList : statusList.filter(i => i.few)"
               @click="statusClick(item)"
               :class="{ active: item.active }"
            >
               <img class="statusItemImg" :src="item.img" alt="" />
               <div class="statusItemInfo">
                  <div class="statusItemName">{{ item.name }}</div>
                  <div class="statusItemNum" :style="{ color: item.color }">{{ item.num || 0 }}</div>
               </div>
            </div>
         </div>
      </div>
      <el-pagination
         background
         v-model:current-page="params.current"
         v-model:page-size="params.size"
         layout="total,  prev, pager, next"
         :total="total"
         @change="pageChange"
      />
         <el-checkbox-group v-show="isMore" v-model="params.event_keys" @change="getList">
            <el-checkbox v-for="item in eventList" :value="item.status" :key="item.dictKey">
               <template #default>
                  <span class="eventListItemCheckboxName">{{ item.name }}</span>
                  <span class="eventListItemCheckboxNum">({{ item.value }})</span>
               </template>
            </el-checkbox>
         </el-checkbox-group>
         <div class="eventList">
            <div class="eventListItem" v-for="item in list">
               <img class="eventListItemImg" :src="getSmallImg(item.photo_url)" alt="" @click="getFindImgHistory(item)" />
               <div class="eventListItemPosition" :title="item.address" @click="positioning(item)">
                  <img :src="positioningImg" alt="" title="点击定位" />
                  <!-- <div class="address">{{ item.address }}</div> -->
               </div>
               <div class="eventListItemText">
                  <div class="eventListItemName">{{ item.event_name }}</div>
                  <div class="eventListItemTime">{{ dayjs(item.create_time).format('MM/DD HH:mm') }}</div>
               </div>
            </div>
         </div>
         <div class="pagination">
            <el-pagination
               class="ztzf-pagination"
               background
               v-model:current-page="params.current"
               v-model:page-size="params.size"
               layout="total,  prev, pager, next"
               :total="total"
               @change="pageChange"
            />
         </div>
      </div>
   </div>
   <div class="image-list" v-if="isShowBigImg" :style="{ right: isMore ? pxToRem(838) : pxToRem(460) }">
      <div class="title">
         <img @click="isShowBigImg = false" src="@/assets/images/home/useEventOperate/close.png" alt="" />
      </div>
      <div class="content">
         <img :src="clickImgSrc" alt="" />
      </div>
      <div class="card">
         <div class="card-content">
            <div v-for="(item, index) in imageList">
               <div class="time-top" :class="index === selectedImgIndex ? 'active' : ''" @click="clickWeekTime(item, index)">
                  {{ item.create_time_str }}
               </div>
               <div class="time-point">
                  <img
                     class="active"
                     v-if="index === selectedImgIndex"
                     src="@/assets/images/home/useEventOperate/point-active.png"
                     alt=""
                  />
                  <img v-else src="@/assets/images/home/useEventOperate/point.png" alt="" />
               </div>
               <div class="time-bottom">{{ item.create_time }}</div>
            </div>
            <div class="time-line"></div>
         </div>
      </div>
   </div>
</template>
<script setup>
import UserOperate from '@/components/UserOperate.vue';
import status0Img from '@/assets/images/home/eventOverviewDetail/status0.png'
import status1Img from '@/assets/images/home/eventOverviewDetail/status1.png'
import status2Img from '@/assets/images/home/eventOverviewDetail/status2.png'
import status3Img from '@/assets/images/home/eventOverviewDetail/status3.png'
import status4Img from '@/assets/images/home/eventOverviewDetail/status4.png'
import status5Img from '@/assets/images/home/eventOverviewDetail/status5.png'
import positioningImg from '@/assets/images/home/eventOverviewDetail/positioning.png'
import leftArrowImg from '@/assets/images/home/eventOverviewDetail/leftArrow.png'
import rightArrowImg from '@/assets/images/home/eventOverviewDetail/rightArrow.png'
import dayjs from 'dayjs'
import { selectDevicePage } from '@/api/home/machineNest'
import { getMultipleDictionary } from '@/api/system/dictbiz'
import { getEvenNum, getEventPage, getEventStatusNum, getEventTrend } from '@/api/home/event'
import { getEvenNum, getEventPage, getEventStatusNum, findImgHistory } from '@/api/home/event'
import cesiumOperation from '@/utils/cesium-tsa'
import CommonTitle from '@/components/CommonTitle.vue'
import { pxToRem } from '@/utils/rem'
import { useStore } from 'vuex'
const store = useStore()
// const eventTimeRange = computed(() => store.state.home.eventTimeRange)
const timeFormat = 'YYYY-MM-DD HH:mm:ss'
const today = dayjs().format(timeFormat)
const oneWeekAgo = dayjs().subtract(7, 'day').format(timeFormat)
const timeArr = ref([oneWeekAgo, today])
// const oneWeekAgo = dayjs().subtract(7, 'day').format(timeFormat)
// const timeArr = ref([oneWeekAgo, today])
const startOfWeek = dayjs().startOf('week').add(1, 'day').format(timeFormat)
const endOfWeek = dayjs().endOf('week').add(1, 'day').format(timeFormat)
const timeArr = ref([startOfWeek, endOfWeek])
const deviceList = ref([])
const total = ref(0)
@@ -85,10 +167,24 @@
   device_sn: undefined,
   word_order_type: undefined,
   status: undefined,
   eventKeys: [],
   event_keys: [],
   current: 1,
   size: 10,
   size: 8,
})
const getSmallImg = url => {
   return url ? url.substring(0, url.lastIndexOf('.')) + '_small' + url.substring(url.lastIndexOf('.')) : ''
}
const isMore = ref(false)
const leftArrowFun = () => {
   isMore.value = !isMore.value
   params.value.size = isMore.value ? 16 : 8
   params.value.current = 1
   // 下半部分隐藏 右侧隐藏
   store.commit('setHideBottomIcon', !isMore.value)
   getList()
}
const statusClick = row => {
   params.value.status = row.id || undefined
@@ -111,19 +207,35 @@
      workOrderType.value = res.data.data?.['WORK_ORDER_TYPE'] || []
   })
}
const { flyTo } = cesiumOperation();
const positioning = (row) =>{
const { flyTo } = cesiumOperation()
const longitudeOffset = ref(0)
const positioning = row => {
   const longitude = Number(row.longitude)
   const latitude = Number(row.latitude)
   flyTo({ longitude, latitude }, 1, 1000);
   flyTo({ longitude: longitude, latitude: latitude + 0.0008 }, 1, 1000)
}
// 事件状态+数量
const statusList = ref([])
const statusList = ref([
   { name: '全部状态', img: status0Img, id: undefined, few: true, color: '#FFD509' },
   { name: '待审核', img: status1Img, id: 2, color: '#8CFEA7' },
   { name: '待处理', img: status2Img, id: 0, few: true, color: '#FF7411' },
   { name: '处理中', img: status3Img, id: 3, few: true, color: '#FFC398' },
   { name: '已完成', img: status4Img, id: 4, few: true, color: '#AFD9FB' },
   { name: '待分拨', img: status5Img, id: 1, color: '#11C4FF' },
])
const getEventStatusNumFun = () => {
   const [start_date, end_date] = timeArr.value
   getEventStatusNum({ start_date, end_date }).then(res => {
      statusList.value = (res.data?.data || []).map(i => ({ ...i, active: false }))
      const list = res.data?.data || []
      statusList.value = statusList.value.map(item => {
         const find = list.find(item1 => item.id === item1.id) || {}
         return {
            ...item,
            ...find,
         }
      })
      statusList.value[0].active = true
   })
}
@@ -141,11 +253,13 @@
   const [start_date, end_date] = timeArr.value
   params.value.start_date = start_date
   params.value.end_date = end_date
   // 提交数据,更新聚合
   store.commit('setEventTimeRang', timeArr.value)
   // 重置
   getEventStatusNumFun()
   getEventList()
   params.value.status = undefined
   params.value.eventKeys = undefined
   params.value.event_keys = undefined
   getList()
}
@@ -156,12 +270,59 @@
const list = ref([])
const getList = () => {
   getEventPage(params.value).then(res => {
   const pageParams = {
      current: params.value.current,
      size: params.value.size,
   }
   getEventPage(params.value, pageParams).then(res => {
      const resData = res.data.data
      list.value = resData?.records || []
      total.value = resData.total
   })
}
const isShowBigImg = ref(false)
const imageList = ref([])
const clickImgSrc = ref('')
const selectedImgIndex = ref(0)
// 点击图片放大 显示详细信息
const getFindImgHistory = item => {
   // 下半部分隐藏 右侧隐藏
   isShowBigImg.value = true
   store.commit('setHideBottomIcon', false)
   findImgHistory(item.id).then(res => {
      if (res.data.code !== 0) return
      imageList.value = res.data.data
      clickImgSrc.value = res.data.data[0]?.url
   })
   positioning(item)
}
// 点击周期
const clickWeekTime = (item, index) => {
   clickImgSrc.value = item.url
   selectedImgIndex.value = index
}
const getDateRange = unit => {
   if (unit === 'today') {
      return [dayjs().format('YYYY-MM-DD HH:mm:ss'), dayjs().format('YYYY-MM-DD HH:mm:ss')]
   }
   return [dayjs().startOf(unit).format('YYYY-MM-DD HH:mm:ss'), dayjs().endOf(unit).format('YYYY-MM-DD HH:mm:ss')]
}
// 监听事件时间范围变化
watch(
   () => store.state.home.eventTimeType,
   newVal => {
      timeArr.value = getDateRange(newVal)
      const [start_date, end_date] = getDateRange(newVal)
      params.value.start_date = start_date
      params.value.end_date = end_date
      getEventStatusNumFun()
      getEventList()
      getList()
   },
   { immediate: false, deep: true }
)
onMounted(() => {
   getDeviceList()
@@ -174,40 +335,342 @@
<style scoped lang="scss">
.event-overviewdetail-right {
   position: absolute;
   right: 29px;
   right: 36px;
   top: 122px;
   // color: black;
   display: flex;
   flex-direction: column;
   align-items: center;
   height: 900px;
   width: 400px;
   background: white;
   align-items: end;
   height: 922px;
   width: 405px;
   font-size: 18px;
   z-index: 10;
   .statusList {
      display: flex;
      flex-wrap: wrap;
   &.isMore {
      width: 808px;
      .active {
         background: #19ad8d;
      .content {
         width: 792px;
         padding-left: 44px;
      }
   }
   .eventList {
   .content {
      width: 390px;
      height: 877px;
      background: linear-gradient(270deg, rgba(31, 62, 122, 0) 0%, rgba(31, 62, 122, 0.35) 21%, #1f3e7a 100%);
      display: flex;
      justify-content: space-between;
      align-content: start;
      flex-wrap: wrap;
      padding: 7px 13px 0 11px;
      gap: 16px;
      .eventListItem {
         .eventListItemImg {
            width: 120px;
            height: 100px;
      .leftArrow {
         position: absolute;
         left: -11px;
         top: 50%;
         transform: translateY(-50%);
         cursor: pointer;
      }
      .rightArrow {
         position: absolute;
         top: 50%;
         left: 30px;
         transform: translateY(-50%);
         cursor: pointer;
      }
      .statusList {
         width: 100%;
         display: flex;
         justify-content: space-between;
         .statusItem {
            // width: 95px;
            height: 44px;
            display: flex;
            flex: 1;
            justify-content: center;
            position: relative;
            cursor: pointer;
            // margin-right: 4px;
            &.active {
               background: linear-gradient(
                  180deg,
                  rgba(19, 80, 141, 0) 0%,
                  rgba(22, 56, 91, 0.48) 48%,
                  #053462 98%,
                  #259dff 98%,
                  #259dff 98%
               );
            }
            .statusItemImg {
               width: 34px;
               height: 34px;
               margin-right: 1px;
            }
            .statusItemInfo {
               .statusItemName {
                  // width: 59px;
                  height: 17px;
                  font-family: Segoe UI, Segoe UI;
                  font-weight: 400;
                  font-size: 14px;
                  color: #ffffff;
                  line-height: 17px;
                  text-align: left;
                  font-style: normal;
                  text-transform: none;
                  margin-bottom: 4px;
               }
               .statusItemNum {
                  width: 32px;
                  height: 16px;
                  font-family: Segoe UI, Segoe UI;
                  font-weight: bold;
                  font-size: 16px;
                  color: #ffd509;
                  line-height: 16px;
                  text-align: left;
                  font-style: normal;
                  text-transform: none;
               }
            }
         }
      }
      .el-checkbox-group {
         :deep() {
            .el-checkbox {
               height: 20px;
               margin-right: 10px;
            }
            .el-checkbox__inner {
               background: transparent;
               width: 18px;
               height: 18px;
               border: 1px solid #3194ef;
            }
            .el-checkbox__label {
               font-family: Segoe UI, Segoe UI;
               font-weight: 400;
               font-size: 14px;
               color: #ffffff;
               line-height: 17px;
               .eventListItemCheckboxNum {
                  color: #ffa500;
               }
            }
         }
      }
      .eventList {
         width: 100%;
         display: flex;
         flex-wrap: wrap;
         // justify-content: space-between;
         gap: 20px 10px;
         .eventListItem {
            width: 175px;
            height: 140px;
            position: relative;
            cursor: pointer;
            .eventListItemImg {
               width: 174px;
               height: 116px;
               border-radius: 4px 4px 4px 4px;
               margin-bottom: 7px;
            }
            .eventListItemPosition {
               position: absolute;
               right: 0;
               top: 116px;
               width: 174px;
               height: 20px;
               transform: translateY(-100%);
               background: rgba(22, 22, 22, 0.68);
               border-radius: 0 0 4px 4px;
               font-family: Segoe UI, Segoe UI;
               font-weight: 400;
               font-size: 12px;
               line-height: 17px;
               color: #51a8ff;
               cursor: pointer;
               padding-right: 5px;
               display: flex;
               justify-content: end;
               img {
                  margin-right: 5px;
               }
               .address {
                  display: inline-block;
                  width: 60px;
                  overflow: hidden;
                  white-space: nowrap;
                  text-overflow: ellipsis;
               }
            }
            .eventListItemText {
               display: flex;
               justify-content: space-between;
               font-family: Segoe UI, Segoe UI;
               font-weight: 400;
               font-size: 14px;
               color: #ffffff;
               line-height: 17px;
               .eventListItemTime {
                  font-family: Segoe UI, Segoe UI;
                  font-weight: 400;
                  font-size: 14px;
                  color: #96a3cc;
                  line-height: 17px;
               }
            }
         }
      }
   }
}
.pagination {
   width: 100%;
   display: flex;
   justify-content: center;
}
.image-list {
   position: absolute;
   top: 148px;
   right: 460px;
   width: 632px;
   height: 476px;
   border: solid;
   background: #0f1929;
   z-index: 1;
   border: solid 2px transparent;
   border-radius: 20px;
   background-image: linear-gradient(#fff, #fff), linear-gradient(180deg, rgba(81, 168, 255, 1), rgba(189, 228, 255, 1));
   background-origin: border-box;
   background-clip: content-box, border-box;
   overflow: hidden;
   .title {
      display: flex;
      align-items: center;
      justify-content: center;
      position: absolute;
      text-align: right;
      top: 0;
      right: 0;
      width: 45px;
      height: 44px;
      background: rgba(0, 0, 0, 0.5);
      border-radius: 0px 20px 0px 8px;
      img {
         width: 10.55px;
         height: 10.55px;
         cursor: pointer;
      }
   }
   .content {
      img {
         width: 100%;
         height: 100%;
         border-radius: 10px 10px 10px 10px;
      }
   }
   .card {
      position: absolute;
      bottom: 0px;
      width: 100%;
      height: 67px;
      background: rgba(0, 0, 0, 0.42);
      border-radius: 0px 0px 20px 20px;
      padding: 0px 6px 0px 29px;
      color: #d5d5d5;
      font-size: 14px;
      .card-content {
         display: flex;
         justify-content: space-between;
         position: absolute;
         left: 29px;
         width: calc(100% - 29px - 6px);
         height: 100%;
         & > div {
            position: relative;
         }
         .eventListItemText {
         .time-top {
            margin-top: 10px;
            line-height: 1;
            text-align: center;
            cursor: pointer;
         }
         .active {
            font-weight: 400;
            color: #ffffff;
         }
         .time-point {
            display: flex;
            justify-content: space-between;
            font-size: 16px;
            align-items: center;
            justify-content: center;
            position: absolute;
            top: 28px;
            left: 50%;
            width: 18px;
            height: 18px;
            z-index: 1;
            transform: translate(-50%, 0);
            img {
               width: 11.2px;
               height: 11.2px;
            }
            img.active {
               width: 16.89px;
               height: 16.89px;
            }
         }
         .time-bottom {
            margin-top: 20px;
         }
         .time-line {
            position: absolute;
            top: 35px;
            width: 100%;
            width: 595px;
            border-radius: 0px 0px 0px 0px;
            border: 1px solid rgba(237, 237, 237, 0.6);
            z-index: 0;
         }
      }
   }