From ff82e86becbd200adabd2ce56fba1f6b3c6c37e1 Mon Sep 17 00:00:00 2001
From: Riku <risaku@163.com>
Date: 星期一, 23 六月 2025 23:10:49 +0800
Subject: [PATCH] 2025.6.23

---
 src/views/management/TaskSummary.vue          |  126 +++-
 src/views/visualization/SubtaskVisual.vue     |  281 ++++++----
 src/components/inspection/SceneDevice.vue     |  498 ++++++++++++++++++
 src/views/visualization/SupervisionVisual.vue |    8 
 src/views/management/TaskStats.vue            |   13 
 src/components.d.ts                           |    5 
 src/api/fysp/mediafileApi.js                  |   18 
 src/api/index.js                              |   15 
 src/main.js                                   |    5 
 src/components/FYImageSelectDialog.vue        |  304 +++++++++++
 src/components/inspection/ProblemItem.vue     |   10 
 src/components/inspection/TaskItem.vue        |  219 +++++---
 src/views/main/MonitorView.vue                |   42 +
 src/api/fysp/deviceApi.js                     |   20 
 src/components/BaseCard.vue                   |    2 
 15 files changed, 1,303 insertions(+), 263 deletions(-)

diff --git a/src/api/fysp/deviceApi.js b/src/api/fysp/deviceApi.js
new file mode 100644
index 0000000..953c7b7
--- /dev/null
+++ b/src/api/fysp/deviceApi.js
@@ -0,0 +1,20 @@
+import { $fysp } from '../index';
+
+export default {
+  // 鑾峰彇璁惧
+  fetchDevices(sceneId, deviceTypeId) {
+    const params = `?sceneId=${sceneId}&deviceTypeId=${deviceTypeId}`;
+    return $fysp
+      .get(`device${params}`)
+      .then((res) => res)
+      .then((res) => res.data);
+  },
+  // 鑾峰彇璁惧鐘舵�佷互鍙婅澶囪鎯�
+  fetchDeviceStatus({ deviceId, sceneId, deviceTypeId }) {
+    const params = `?deviceId=${deviceId}&sceneId=${sceneId}&deviceTypeId=${deviceTypeId}`;
+    return $fysp
+      .get(`device/status${params}`)
+      .then((res) => res)
+      .then((res) => res.data);
+  }
+};
diff --git a/src/api/fysp/mediafileApi.js b/src/api/fysp/mediafileApi.js
new file mode 100644
index 0000000..eb2c27b
--- /dev/null
+++ b/src/api/fysp/mediafileApi.js
@@ -0,0 +1,18 @@
+import { $fysp } from '../index';
+
+export default {
+  /**
+   * 鑾峰彇宸℃煡涓殑浠绘剰鍥剧墖
+   */
+  getRoutineByStGuid(stGuid) {
+    const params = `?stGuid=${stGuid}`;
+    return $fysp.get(`mediafile/routine${params}`).then((res) => res.data);
+  },
+  getRoutineByiGuid(iGuid) {
+    const params = `?iGuid=${iGuid}`;
+    return $fysp.get(`mediafile/routine${params}`).then((res) => res.data);
+  },
+  updateMediaFile(mediafile) {
+    return $fysp.post('mediafile', mediafile).then((res) => res.data);
+  }
+};
diff --git a/src/api/index.js b/src/api/index.js
index 3a2b325..a501280 100644
--- a/src/api/index.js
+++ b/src/api/index.js
@@ -41,11 +41,11 @@
   i.interceptors.request.use(
     function (config) {
       // 鍦ㄥ彂閫佽姹備箣鍓嶅仛浜涗粈涔�
-      console.log('==>璇锋眰寮�濮�')
-      console.log(`${config.baseURL}${config.url}`)
-      if (config.data) {
-        console.log('==>璇锋眰鏁版嵁', config.data)
-      }
+      // console.log('==>璇锋眰寮�濮�')
+      // console.log(`${config.baseURL}${config.url}`)
+      // if (config.data) {
+      //   console.log('==>璇锋眰鏁版嵁', config.data)
+      // }
       return config
     },
     function (error) {
@@ -65,6 +65,11 @@
     function (response) {
       // 2xx 鑼冨洿鍐呯殑鐘舵�佺爜閮戒細瑙﹀彂璇ュ嚱鏁般��
       // 瀵瑰搷搴旀暟鎹仛鐐逛粈涔�
+      console.log('==>璇锋眰寮�濮�')
+      console.log(`${response.config.baseURL}${response.config.url}`)
+      if (response.config.data) {
+        console.log('==>璇锋眰鏁版嵁', response.config.data)
+      }
       console.log(response)
       console.log('==>璇锋眰缁撴潫')
       if (response.status == 200) {
diff --git a/src/components.d.ts b/src/components.d.ts
index 5103eec..3f60b91 100644
--- a/src/components.d.ts
+++ b/src/components.d.ts
@@ -19,6 +19,7 @@
     ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
     ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
     ElDivider: typeof import('element-plus/es')['ElDivider']
+    ElEmpty: typeof import('element-plus/es')['ElEmpty']
     ElIcon: typeof import('element-plus/es')['ElIcon']
     ElImage: typeof import('element-plus/es')['ElImage']
     ElLink: typeof import('element-plus/es')['ElLink']
@@ -32,14 +33,18 @@
     ElStatistic: typeof import('element-plus/es')['ElStatistic']
     ElTable: typeof import('element-plus/es')['ElTable']
     ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
+    ElTabPane: typeof import('element-plus/es')['ElTabPane']
+    ElTabs: typeof import('element-plus/es')['ElTabs']
     ElTag: typeof import('element-plus/es')['ElTag']
     ElText: typeof import('element-plus/es')['ElText']
+    FYImageSelectDialog: typeof import('./components/FYImageSelectDialog.vue')['default']
     OptionLocation: typeof import('./components/search/OptionLocation.vue')['default']
     OptionSceneType: typeof import('./components/search/OptionSceneType.vue')['default']
     OptionTime: typeof import('./components/search/OptionTime.vue')['default']
     ProblemItem: typeof import('./components/inspection/ProblemItem.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
+    SceneDevice: typeof import('./components/inspection/SceneDevice.vue')['default']
     SubtaskExamineItem: typeof import('./components/inspection/SubtaskExamineItem.vue')['default']
     SubtaskItem: typeof import('./components/inspection/SubtaskItem.vue')['default']
     TaskItem: typeof import('./components/inspection/TaskItem.vue')['default']
diff --git a/src/components/BaseCard.vue b/src/components/BaseCard.vue
index dbf4a2c..0a2f89d 100644
--- a/src/components/BaseCard.vue
+++ b/src/components/BaseCard.vue
@@ -20,7 +20,7 @@
       </el-row>
     </el-space>
     <!-- <Transition name="el-zoom-in-left"> -->
-    <div class="m-t-8" v-if="show">
+    <div class="m-t-8" v-show="show">
       <slot></slot>
     </div>
     <!-- </Transition> -->
diff --git a/src/components/FYImageSelectDialog.vue b/src/components/FYImageSelectDialog.vue
new file mode 100644
index 0000000..965b0c8
--- /dev/null
+++ b/src/components/FYImageSelectDialog.vue
@@ -0,0 +1,304 @@
+<template>
+  <!-- <el-dialog
+    :model-value="dialogVisible"
+    @opened="handleOpen"
+    @closed="handleClose"
+    top="5vh"
+    width="68%"
+    destroy-on-close
+    :close-on-press-escape="false"
+  > -->
+  <!-- <el-row justify="end">
+      <el-text v-if="onContextMenu != undefined" size="small" type="info">{{
+        `锛�${contextMenuStr}锛塦
+      }}</el-text>
+      <div v-if="!readonly">
+        <el-text size="small" type="info" class="m-r-8"
+          >鏈�澶氶�夋嫨{{ maxSelect }}寮犲浘鐗�</el-text
+        >
+        <el-button
+          size="small"
+          type="primary"
+          @click="handleSubmit"
+          :disabled="selectedImgUrlList.length == 0"
+          >纭畾</el-button
+        >
+        <el-button size="small" type="primary" @click="handleCancel"
+          >鍙栨秷</el-button
+        >
+      </div>
+    </el-row> -->
+
+  <div class="center">
+    <el-tabs v-if="typeList.length > 0" v-model="activeId" type="card">
+      <el-tab-pane
+        v-for="item in typeList"
+        :key="item.typeId"
+        :label="item.typeName + ' (' + typeImgMap.get(item.typeId).length + ')'"
+        :name="item.typeId"
+      >
+      </el-tab-pane>
+    </el-tabs>
+    <el-scrollbar :height="height">
+      <div v-if="typeImgMap.get(activeId) && typeImgMap.get(activeId).length > 0" class="imgs">
+        <el-image
+          v-loading="img.loading"
+          v-for="(img, i) in typeImgMap.get(activeId)"
+          :key="i"
+          :class="[img.isSelect ? 'selected' : 'noActive', 'image']"
+          :style="`width: ${imageWidth}px;height: ${imageWidth}px;`"
+          fit="cover"
+          :src="img.url"
+          :preview-src-list="readonly ? typeImgMap.get(activeId).map((v) => v.url) : []"
+          :initial-index="i"
+          @contextmenu="(e) => showContextMenu(e, i)"
+          @click="onSelect(img, i)"
+          @load="onOneImgLoadSuccess(img)"
+          @error="onOneImgLoadError(img)"
+        />
+      </div>
+      <el-row v-else justify="space-between">
+        <el-empty description="鏆傛棤璁板綍" />
+      </el-row>
+    </el-scrollbar>
+  </div>
+  <!-- </el-dialog> -->
+</template>
+<script setup>
+import { ref, watch, computed, onMounted, onUnmounted } from 'vue'
+
+const props = defineProps({
+  dialogVisible: Boolean,
+  /**
+   * 鍥剧墖鍒嗙被
+   * 缁撴瀯{ typeId, typeName }
+   */
+  typeList: {
+    type: Array,
+    default: () => []
+  },
+  typeImgMap: {
+    type: Array,
+    default: () => new Map()
+  },
+  // 鏄惁浠ュ彧璇荤殑褰㈠紡鏌ョ湅褰撳墠椤甸潰
+  readonly: {
+    type: Boolean,
+    default: false
+  },
+  // 鍥剧墖鍙�夋暟閲忥紝褰撲紶鍏ユ暟瀛楁椂锛屼唬琛ㄥ浘鐗囨暟閲�
+  maxSelect: {
+    type: Number,
+    default: 3
+  },
+  // 鍥剧墖鍙抽敭鐐瑰嚮浜嬩欢
+  onContextMenu: {
+    type: Function
+  },
+  contextMenuStr: {
+    type: String,
+    default: '鍙抽敭鐐瑰嚮鍥剧墖瑙﹀彂棰濆鎿嶄綔'
+  },
+  height: {
+    type: String,
+    default: '70vh'
+  },
+  imageWidth: {
+    type: Number,
+    default: 240
+  }
+})
+
+const emit = defineEmits(['submit', 'cancel', 'update:dialogVisible'])
+
+const activeId = ref('')
+
+const selectedImgUrlList = ref([])
+
+let loadedImgCount = ref(0)
+// 鍔犺浇鐘舵��
+const loading = computed(() => {
+  if (activeId.value == '') {
+    return false
+  }
+  // 淇濊瘉鏈�寮�濮嬫槸鍔犺浇鐘舵�侊紝涓夊垎涔嬩竴鍔犺浇涔嬪悗鍋滄灞曠ず鍔犺浇鐘舵��
+  return !(props.typeImgMap.get(activeId.value).length / 3 <= loadedImgCount.value)
+})
+function onOneImgLoadError(img) {
+  img.loading = false
+  loadedImgCount.value++
+}
+function onOneImgLoadSuccess(img) {
+  img.loading = false
+  loadedImgCount.value++
+}
+watch(
+  () => activeId.value,
+  (nV, oV) => {
+    loadedImgCount.value = 0
+  },
+  { immediate: true }
+)
+
+function onSelect(img, i) {
+  if (props.readonly) {
+    return
+  }
+  const imgList = selectedImgUrlList.value
+  const index = imgList.indexOf(img)
+  if (index == -1) {
+    if (props.maxSelect == 1) {
+      img.isSelect = true
+      imgList.push(img)
+      if (imgList.length > 1) {
+        imgList.splice(0, 1).forEach((e) => {
+          e.isSelect = false
+        })
+      }
+    } else if (props.maxSelect > 1) {
+      if (imgList.length < props.maxSelect) {
+        img.isSelect = true
+        imgList.push(img)
+      }
+    }
+  } else {
+    imgList.splice(index, 1)
+    img.isSelect = false
+  }
+}
+function handleOpen() {
+  emit('update:dialogVisible', true)
+}
+function handleClose() {
+  selectedImgUrlList.value.forEach((item) => (item.isSelect = false))
+  selectedImgUrlList.value = []
+  emit('update:dialogVisible', false)
+}
+function handleSubmit() {
+  emit('submit', selectedImgUrlList.value)
+  emit('update:dialogVisible', false)
+}
+
+function handleCancel() {
+  emit('cancel')
+  emit('update:dialogVisible', false)
+}
+
+// 鍥剧墖鍙抽敭鐐瑰嚮鏃堕棿
+function showContextMenu(event, index) {
+  if (props.onContextMenu) {
+    event.preventDefault()
+    props.onContextMenu(event, activeId.value, index)
+  }
+}
+
+watch(
+  () => props.typeList,
+  (nV, oV) => {
+    if (nV != oV && nV.length > 0) {
+      activeId.value = nV[0].typeId
+    }
+  },
+  { immediate: true }
+)
+</script>
+<style scoped>
+.center {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+.text {
+  padding: 20px;
+}
+
+.main {
+  /* 浣跨埗鍏冪礌灞呬腑 */
+  /* margin: 0 auto;  */
+  /* width: 100%; */
+}
+
+.imgs {
+  width: 100%;
+  /* border-style:solid;
+    border-radius: 1px; */
+  /* height: 100%; */
+  flex-grow: 1 !important;
+  overflow-y: auto !important;
+  /* 鍐呭鐨勫唴杈硅窛 */
+  display: flex !important;
+  flex-wrap: wrap !important;
+  /* overflow: hidden; */
+}
+
+.image {
+  margin: 5px;
+  /* height: 250px;
+  width: 240px; */
+  border-radius: 4px;
+}
+
+.selected {
+  margin: 3px;
+  color: #4abe84;
+  box-shadow: 0 2px 7px 0 rgba(85, 110, 97, 0.35);
+  border: 2px solid rgba(74, 190, 132, 1);
+}
+
+.selected:before {
+  content: '';
+  position: absolute;
+  right: 0;
+  bottom: 0;
+  border: 17px solid #4abe84;
+  border-top-color: transparent;
+  border-left-color: transparent;
+}
+
+.selected:after {
+  content: '';
+  width: 5px;
+  height: 12px;
+  position: absolute;
+  right: 6px;
+  bottom: 6px;
+  border: 2px solid #fff;
+  border-top-color: transparent;
+  border-left-color: transparent;
+  transform: rotate(45deg);
+}
+
+.noActive {
+  /* padding: 5px; */
+}
+
+.blurry {
+  filter: blur(3px);
+}
+.filters {
+  display: flex;
+  padding: 5px;
+}
+
+::v-deep .el-dialog__body {
+  padding: 10px calc(var(--el-dialog-padding-primary) + 10px) !important;
+}
+
+:deep(.el-tabs__item) {
+  /* color: var(--el-text-color-info); */
+}
+
+:deep(.el-tabs__item.is-active) {
+  color: var(--el-color-warning);
+}
+
+:deep(.el-tabs--card > .el-tabs__header .el-tabs__nav) {
+  border: 1px solid rgba(255, 255, 255, 0.541);
+}
+:deep(.el-tabs--card > .el-tabs__header .el-tabs__item) {
+  border-left: 1px solid rgba(255, 255, 255, 0.541);
+}
+:deep(.el-tabs--card > .el-tabs__header .el-tabs__item:first-child) {
+  border-left: none;
+}
+</style>
diff --git a/src/components/inspection/ProblemItem.vue b/src/components/inspection/ProblemItem.vue
index 34c195d..21f6a57 100644
--- a/src/components/inspection/ProblemItem.vue
+++ b/src/components/inspection/ProblemItem.vue
@@ -1,7 +1,7 @@
 <template>
   <div>
-    <el-text tag="b" type="warning" size="small">{{ index }}. </el-text>
-    <el-text type="warning" size="small">{{ title }}</el-text>
+    <el-text tag="b" size="large">{{ index }}. </el-text>
+    <el-text size="large">{{ title }}</el-text>
   </div>
   <!-- <div>
     <el-text>{{ proStatus.name }}</el-text>
@@ -9,7 +9,7 @@
   <template v-for="(pic, t) in pics" :key="t">
     <template v-if="pic.path.length > 0">
       <div>
-        <el-text size="small" type="info">{{ pic.title }}</el-text>
+        <el-text size="default" type="info">{{ pic.title }}</el-text>
       </div>
       <el-space>
         <el-image
@@ -61,8 +61,8 @@
 </script>
 <style scoped>
 .image {
-  width: 60px;
-  height: 60px;
+  width: 134px;
+  height: 134px;
   border-radius: 2px;
 }
 </style>
diff --git a/src/components/inspection/SceneDevice.vue b/src/components/inspection/SceneDevice.vue
new file mode 100644
index 0000000..18a8f77
--- /dev/null
+++ b/src/components/inspection/SceneDevice.vue
@@ -0,0 +1,498 @@
+<template>
+  <!-- <CompGenericWrapper type="drawer">
+    <template #content> -->
+  <!-- 閫夐」 -->
+  <!-- 璁惧绫诲瀷  -->
+  <el-row>
+    <el-col>
+      <el-tabs class="child_select" placeholder="璁惧绫诲瀷" v-model="currSelect.topDeviceTypeId">
+        <el-tab-pane v-for="item in deviceTopTypes" :key="item.id" :name="item.id">
+          <template #label>
+            <el-badge :value="item.count" :type="item.count == 0 ? 'danger' : 'primary'">
+              <span class="custom-tabs-label">
+                <span>{{ item.label }}</span>
+              </span>
+            </el-badge>
+          </template>
+        </el-tab-pane>
+      </el-tabs>
+    </el-col>
+  </el-row>
+  <el-collapse v-model="activeNames" style="border: 4px">
+    <el-collapse-item
+      v-for="item in formInfo"
+      :key="item.id"
+      :name="item.id"
+      class="collapse-item-class"
+    >
+      <template #title>
+        <div style="display: flex; width: 100%; justify-content: space-between">
+          <div style="">
+            <el-descriptions style="" :column="3" size="small" border>
+              <el-descriptions-item
+                width="64px"
+                :label="currSelect.topDeviceTypeId == 0 ? '绔欑偣鍚嶇О' : '璁惧鍚嶇О'"
+                :span="3"
+                >{{ item.name || '鏃�' }}</el-descriptions-item
+              >
+              <el-descriptions-item label="渚涘簲鍟�">{{
+                item.supplier || '鏃�'
+              }}</el-descriptions-item>
+              <el-descriptions-item label="杩愮淮鍟�">{{
+                item.maintainer || '鏃�'
+              }}</el-descriptions-item>
+              <el-descriptions-item label="杩愮淮棰戞">
+                <el-select
+                  v-model="item.maintainFrequency"
+                  :disabled="isDisabled"
+                  style="width: 150px"
+                >
+                  <el-option
+                    v-for="frequency of maintainFrequencysArray"
+                    :key="frequency.key"
+                    :label="frequency.value"
+                    :value="frequency.key"
+                  ></el-option>
+                </el-select>
+              </el-descriptions-item>
+              <el-descriptions-item label="杩愮淮浜哄憳">{{
+                item.maintainStaff || '鏃�'
+              }}</el-descriptions-item>
+              <el-descriptions-item label="杩愮淮鑱旂郴鏂瑰紡">{{
+                item.maintainTel || '鏃�'
+              }}</el-descriptions-item>
+              <el-descriptions-item label="鍝佺墝鍨嬪彿">{{
+                item.brandModel || '鏃�'
+              }}</el-descriptions-item>
+              <el-descriptions-item label="杩愯鐘舵��">
+                <el-select v-model="item.runningStatus" :disabled="isDisabled" style="width: 150px">
+                  <el-option
+                    v-for="status of runStatusArray"
+                    :key="status.key"
+                    :label="status.value"
+                    :value="status.key"
+                  ></el-option>
+                </el-select>
+              </el-descriptions-item>
+              <el-descriptions-item label="绫诲瀷">
+                {{ item._typename || '鏃�' }}
+              </el-descriptions-item>
+            </el-descriptions>
+          </div>
+
+          <div style="display: flex">
+            <!-- <div class="sub-title">{{ item.name }}</div> -->
+            <!-- 鍥剧墖 -->
+            <div class="image-container">
+              <div
+                class="block-div"
+                @click="onClickPic($event)"
+                v-for="(status, index) in item._statusList"
+                :key="index"
+              >
+                <el-image
+                  v-if="index == 0"
+                  fit="cover"
+                  class="pic-style"
+                  :src="status._picUrl"
+                  :preview-src-list="Array.of(status._picUrl)"
+                />
+                <span class="abstract_pic_text" v-if="index == 0">{{
+                  `鏈�鏂扮姸鎬佸浘鐗� ${status.dlCreateTime.slice(0, 10)}`
+                }}</span>
+              </div>
+            </div>
+          </div>
+        </div>
+      </template>
+      <!-- 璇︾粏鍐呭寮�濮� -->
+      <el-form :model="item" class="form_class">
+        <el-form-item label="鐘舵��">
+          <el-tabs tab-position="top">
+            <el-tab-pane
+              v-for="(status, i) in item._statusList"
+              :label="status.dlCreateTime.slice(0, 10)"
+              :key="i"
+            >
+              <el-form :model="status" class="form-class">
+                <el-form-item label="浣嶇疆" style="margin-bottom: 10px">
+                  {{ status.dlLocation }}
+                </el-form-item>
+                <el-form-item label="鍥剧墖">
+                  <!-- 鍥剧墖 -->
+                  <el-space>
+                    <div v-if="status._paths && status._paths.length > 0">
+                      <el-image
+                        v-for="(path, i) in status._paths"
+                        fit="cover"
+                        class="pic-style"
+                        :src="path"
+                        :preview-src-list="Array.of(path)"
+                        :key="i"
+                      />
+                    </div>
+                    <el-empty v-else></el-empty>
+                  </el-space>
+                </el-form-item>
+              </el-form>
+            </el-tab-pane>
+          </el-tabs>
+        </el-form-item>
+      </el-form>
+      <!-- 璇︾粏鍐呭缁撴潫 -->
+    </el-collapse-item>
+  </el-collapse>
+  <!-- 绌虹姸鎬� -->
+  <el-empty v-if="isEmpty" />
+  <!-- </template>
+  </CompGenericWrapper> -->
+</template>
+
+<script>
+import deviceApi from '@/api/fysp/deviceApi'
+import { $fysp } from '@/api/index'
+import { toLabel } from '@/enum/device/device'
+export default {
+  components: {},
+  props: {
+    scene: Object
+  },
+  watch: {
+    // 閫夋嫨鏀瑰彉鐩戝惉
+    currSelect: {
+      handler(newObj, oldObj) {
+        this.getList()
+      },
+      deep: true
+    }
+  },
+  data() {
+    return {
+      activeNames: [],
+      // 鎺у埗鏄惁灞曠ず绌虹姸鎬�
+      isEmpty: false,
+      // 璇︽儏鎸夐挳澶у皬
+      detailSize: '22px',
+      // 琛ㄥ崟璇︽儏鐐瑰嚮鎸夐挳鐨勫浘鏍�
+      isDetail: false,
+      currSelect: {
+        topDeviceTypeId: 0
+      },
+      // 鎺у埗琛ㄥ崟鏄惁鍙互缂栬緫
+      isDisabled: true,
+      formInfo: {},
+      rules: [],
+      // 璁惧绫诲瀷
+      deviceTopTypes: [
+        { id: 0, label: '鐩戞帶璁惧' },
+        { id: 1, label: '娌荤悊璁惧' },
+        { id: 2, label: '鐢熶骇璁惧' }
+      ],
+      // 杩愯鐘舵��
+      runStatusArray: [
+        { key: 0, value: '鏈仈缃�' },
+        { key: 1, value: '涓婄嚎涓�' },
+        { key: 2, value: '涓嬬嚎' },
+        { key: 3, value: '鎷嗛櫎' }
+      ],
+      // 缁存姢棰戠巼鐘舵��
+      maintainFrequencysArray: [
+        { key: 1, value: '姣忔湀涓�娆�' },
+        { key: 2, value: '姣忓搴︿竴娆�' },
+        { key: 3, value: '姣忓崐骞翠竴娆�' },
+        { key: 4, value: '姣忓勾涓�娆�' }
+      ],
+      // 绉熻祦鏂瑰紡
+      ownershipArray: [
+        { key: 0, value: '璐拱' },
+        { key: 1, value: '绉熻祦' }
+      ],
+      scene: {}
+    }
+  },
+
+  mounted() {},
+  methods: {
+    // 鑾峰彇褰撳墠绫诲瀷璁惧鏁伴噺
+    getTabsCount() {
+      this.deviceTopTypes.forEach((item) => {
+        deviceApi.fetchDevices(this.scene.guid, item.id).then((result) => {
+          item.count = result.data.length
+        })
+      })
+    },
+    // 鑾峰彇杩愯鐘舵�佸搴旂殑value
+    getRunStatusValueByRunStatusKey(status) {
+      var runningStatusValueArray = this.runStatusArray.filter((runStatus) => {
+        return runStatus.key == status
+      })
+      if (runningStatusValueArray.length > 0) {
+        return runningStatusValueArray[0].value
+      }
+    },
+    // 灞曠ず琛ㄥ崟鐨勮鎯呯殑鐐瑰嚮浜嬩欢
+    showDetail(item) {
+      item._isDetail = !item._isDetail
+    },
+    init(scene) {
+      // 鐖剁粍浠朵富鍔ㄨ皟鐢ㄥ垵濮嬪寲瀛愮粍浠剁殑鏂规硶
+      this.scene = scene
+
+      this.getList()
+      this.getTabsCount()
+    },
+    // 閲嶇疆灞曠ず鐨勬暟鎹�
+    initList() {
+      this.formInfo = []
+      this.isEmpty = false
+    },
+    // 鏍囧噯鍖栧睘鎬у悕
+    convertKeys(obj) {
+      // 灏嗕竴涓猨s瀵硅薄涓墍鏈塪i锛寃i锛宲i寮�澶寸殑灞炴�у叏閮ㄦ敼鎴愬幓鎺夎繖浜涘墠缂�骞朵笖閲嶆柊鍙樹负椹煎嘲寮忓懡鍚�
+      const newObj = {}
+      for (const key in obj) {
+        let newKey = key
+        if (key.startsWith('di')) {
+          newKey = key.substring(2)
+        } else if (key.startsWith('wi')) {
+          newKey = key.substring(2)
+        } else if (key.startsWith('pi')) {
+          newKey = key.substring(2)
+        }
+        newKey = newKey.charAt(0).toLowerCase() + newKey.slice(1)
+        newObj[newKey] = obj[key]
+      }
+      return newObj
+    },
+    // 鏂板瀛楁
+    initFormData(data) {
+      data._isDetail = false
+    },
+    getList() {
+      deviceApi.fetchDevices(this.scene.guid, this.currSelect.topDeviceTypeId).then((result) => {
+        this.initList()
+        if (result.data == null || result.data.length <= 0) {
+          this.isEmpty = true
+          return
+        }
+        // 鏍囧噯鍖栧睘鎬у悕
+        for (let index = 0; index < result.data.length; index++) {
+          var element = this.convertKeys(result.data[index])
+          this.initFormData(element)
+          // 鑾峰彇璁惧鐘舵�佷俊鎭�
+          let data = {
+            deviceId: element.id,
+            sceneId: element.sceneGuid,
+            deviceTypeId: this.currSelect.topDeviceTypeId
+          }
+          deviceApi.fetchDeviceStatus(data).then((status) => {
+            var statusData = status.data
+            var imgPaths = []
+            if (statusData) {
+              if (statusData.length == 0) {
+                this.formInfo.push(element)
+                return
+              }
+              element = this.convertKeys(result.data[index])
+              element = this.setDeviceType(element)
+              element._picUrls = imgPaths
+              for (let index = 0; index < statusData.length; index++) {
+                const statusItem = statusData[index]
+                // 璁惧瀵硅薄娣诲姞涓�涓睘鎬у垪琛ㄥ睘鎬х敤鏉ヤ繚瀛樿澶囩姸鎬�
+                this.saveStatus(element, statusItem)
+                element.dlLocation = statusItem.dlLocation
+                this.formInfo.push(element)
+              }
+            }
+          })
+        }
+      })
+    },
+    setDeviceType(element) {
+      var type = []
+      type = toLabel(element.sceneTypeId, this.currSelect.topDeviceTypeId, [
+        element.typeId,
+        element.subtypeId
+      ])
+      element._typename = type.join('-')
+      return element
+    },
+    // 淇濆瓨鐘舵�佷俊鎭�
+    saveStatus(device, status) {
+      var _picUrl = $fysp.imgUrl + status.dlPicUrl
+      status._picUrl = _picUrl
+      status._paths = _picUrl.split(';')
+      device._picUrls.push(_picUrl)
+      if ('_statusList' in device) {
+        device._statusList.push(status)
+      } else {
+        device._statusList = Array.of(status)
+      }
+      // 鎺掑簭
+      device._statusList.sort(function (x, y) {
+        return new Date(x.dlCreateTime) - new Date(y.dlCreateTime) //	闄嶅簭锛屽崌搴忓垯鍙嶄箣
+      })
+    },
+    submit() {},
+    cancel() {},
+    modifyObjectKeys(obj) {
+      const newObj = {}
+      for (const key in obj) {
+        // 璺宠繃浠� 'dl' 鎴� '_' 寮�澶寸殑灞炴��
+        if (key.startsWith('dl') || key.startsWith('_')) {
+          newObj[key] = obj[key]
+          continue
+        }
+        // 鏍规嵁 topDeviceTypeId 娣诲姞鍓嶇紑
+        let prefix = ''
+        switch (this.currSelect.topDeviceTypeId) {
+          case 0:
+            prefix = 'di'
+            break
+          case 1:
+            prefix = 'pi'
+            break
+          case 2:
+            prefix = 'wi'
+            break
+          default:
+            // 濡傛灉 topDeviceTypeId 涓嶆槸 0, 1, 鎴� 2锛屼笉娣诲姞鍓嶇紑
+            newObj[key] = obj[key]
+            continue
+        }
+
+        // 娣诲姞鍓嶇紑骞惰浆鎹负椹煎嘲寮忓懡鍚�
+        const newKey = `${prefix}${key.charAt(0).toUpperCase() + key.slice(1)}`
+        newObj[newKey] = obj[key]
+      }
+      return newObj
+    },
+    // 鐢熸垚鎺ュ彛鍙傛暟
+    generateQuery(obj) {
+      // 闇�瑕佹牴鎹満鏅被鍨嬬‘瀹氭帴鍙e弬鏁扮殑灞炴�у悕
+      var query = this.modifyObjectKeys(obj)
+      return query
+    },
+    onClickPic(e, item) {
+      e.stopPropagation()
+    }
+  }
+}
+</script>
+
+<style scoped>
+.image-container {
+  justify-content: flex-end;
+  display: flex;
+  /* width: 300px; */
+  /* flex-direction: row-reverse; */
+  /* height: 225px; */
+  /* overflow: hidden; 纭繚鍥剧墖涓嶄細瓒呭嚭瀹瑰櫒 */
+}
+.pic-style {
+  width: 150px;
+  height: 150px;
+  border-radius: 4px;
+}
+.card-style {
+  height: 400px;
+  margin-bottom: 10px;
+  border-color: rgba(0, 0, 0, 0.308);
+}
+.centerDiv {
+  text-align: center; /* 姘村钩灞呬腑 */
+}
+.dot {
+  position: absolute;
+  top: 0;
+  right: 0;
+  width: 10px;
+  height: 10px;
+  background-color: #f56c6c;
+  border-radius: 50%;
+}
+.abstract_main {
+  width: 98%;
+}
+.abstract_main_item {
+  display: flex;
+  flex-direction: column;
+  margin-right: 50px;
+  margin-top: 10px;
+  /* width: 20%; */
+}
+.abstract_other_item {
+  /* display: flex;
+    flex-direction: column; */
+  /* margin-left: 50px; */
+  /* margin-top: 10px;
+    width: 100vh; */
+}
+.abstract_main_item_inner {
+  display: flex;
+  justify-content: center;
+}
+.abstract_other_item_inner {
+  margin-left: 10px;
+  display: flex;
+}
+.abstract_main_title {
+  /* margin-left: -400px; */
+  color: #303133;
+  font-size: 16px;
+}
+.abstract_main_title {
+  color: #606266;
+  font-size: 13px;
+  margin-top: 10px;
+}
+.abstract_other_title {
+  color: #606266;
+  font-size: 13px;
+  margin-top: 45px;
+}
+.abstract_main_text {
+  color: #303133;
+  font-size: 17px;
+  margin-top: 5px;
+}
+.abstract_pic_text {
+  display: block;
+  color: var(--el-text-color-secondary);
+  font-size: 14px;
+  /* margin-top: 20px; */
+}
+.block-div {
+  display: block;
+}
+.form_class {
+  /* margin-left: 10px; */
+}
+
+.el-collapse {
+  /* 鎶樺彔闈㈡澘鎶樺彔鏃剁殑楂樺害 */
+  --el-collapse-header-height: auto;
+}
+.el-collapse-item__header {
+  width: 100%;
+}
+.form-class {
+  width: 50vw;
+}
+.form-item-class {
+  margin-bottom: 10px;
+}
+.sub-title {
+  font-size: var(--el-font-size-large);
+  margin-bottom: 30px;
+  margin-left: 20px;
+}
+.collapse-item-class {
+  height: 100%;
+  border: 5px;
+}
+
+::-webkit-scrollbar {
+  height: 0;
+}
+</style>
diff --git a/src/components/inspection/TaskItem.vue b/src/components/inspection/TaskItem.vue
index 433d7d1..dcd6f49 100644
--- a/src/components/inspection/TaskItem.vue
+++ b/src/components/inspection/TaskItem.vue
@@ -1,43 +1,69 @@
 <template>
-  <div v-if="value">
-    <el-row justify="start">
-      <el-text>{{ value.name }}</el-text>
+  <div v-if="value" class="wrapper">
+    <el-row justify="center">
+      <el-text size="large">{{ value.districtName }}</el-text>
+      <!-- <el-tag type="info">{{ value.districtName }}</el-tag> -->
     </el-row>
     <div>
-      <el-text>鎬婚噺</el-text>
-      <el-text v-if="value._totaltask > 0" size="default">{{
-        value._completetask + '/' + value._totaltask
-      }}</el-text>
-      <el-text v-else size="default">{{ value.completetask + '/' + value.totaltask }}</el-text>
-      <el-progress
-        style="width: 300px"
-        type="line"
-        status="warning"
-        :text-inside="true"
-        :stroke-width="18"
-        :striped="percentFormat(value.completetask, value.totaltask) < 100"
-        striped-flow
-        :percentage="percentFormat(value.completetask, value.totaltask)"
-      >
-        <template #default="{ percentage }">
-          <span class="percentage-value">{{ percentage }}%</span>
-        </template>
-      </el-progress>
+      <div class="text_title">瀹炴柦杩涘害</div>
+      <template v-if="value._totaltask > 0">
+        <el-space>
+          <!-- <el-text size="default">{{ value._completetask + '/' + value._totaltask }}</el-text> -->
+          <el-text>鎬昏</el-text>
+          <el-progress
+            style="width: 350px"
+            type="line"
+            status="warning"
+            :text-inside="true"
+            :stroke-width="22"
+            :striped="percentFormat(value._completetask, value._totaltask) < 100"
+            striped-flow
+            :percentage="percentFormat(value._completetask, value._totaltask)"
+          >
+            <template #default="{ percentage }">
+              <span class="percentage-value">{{
+                `${value._completetask}/${value._totaltask} (${percentage}%)`
+              }}</span>
+            </template>
+          </el-progress>
+        </el-space>
+      </template>
+      <template v-else>
+        <el-space>
+          <!-- <el-text size="default">{{ value.completetask + '/' + value.totaltask }}</el-text> -->
+          <el-text>鎬昏</el-text>
+          <el-progress
+            style="width: 350px"
+            type="line"
+            status="warning"
+            :text-inside="true"
+            :stroke-width="22"
+            :striped="percentFormat(value.completetask, value.totaltask) < 100"
+            striped-flow
+            :percentage="percentFormat(value.completetask, value.totaltask)"
+          >
+            <template #default="{ percentage }">
+              <span class="percentage-value">{{
+                `${value.completetask}/${value.totaltask} (${percentage}%)`
+              }}</span>
+            </template>
+          </el-progress>
+        </el-space>
+      </template>
     </div>
     <!-- </el-col> -->
     <!-- <el-col span="12" class="flex-bottom"> -->
     <!-- <div>{{ name }}</div> -->
     <!-- <div>{{ planTime }}</div> -->
     <!-- <div>{{ userName }}</div> -->
-    <el-row class="m-t-8">
-      <div
-        align="center"
-        :style="'width: ' + 300 / value.count.length + 'px'"
-        v-for="item in value.count"
-        :key="item.sceneType"
-      >
+    <!-- :style="'width: ' + 300 / value.count.length + 'px'" -->
+    <!-- <el-row class="m-t-8"> -->
+    <div v-for="item in value.count" :key="item.sceneType">
+      <el-space v-if="item.finish > 0">
+        <el-text size="default" truncated>{{ item.sceneType }}</el-text>
         <el-progress
-          :stroke-width="18"
+          :style="'width:' + width"
+          :stroke-width="16"
           status="exception"
           :text-inside="true"
           :striped="percentFormat(item.finish, item.total) < 100"
@@ -45,72 +71,85 @@
           :percentage="percentFormat(item.finish, item.total)"
         >
           <template #default="{ percentage }">
-            <span class="percentage-value-small">{{ percentage }}%</span>
+            <span class="percentage-value-small">{{
+              `${item.finish}/${item.total} (${percentage}%)`
+            }}</span>
           </template>
         </el-progress>
-        <el-text size="small" truncated>{{ item.sceneType }}</el-text>
         <!-- <el-text size="small">{{ item.finish + '/' + item.total }}</el-text> -->
         <!-- <span class="percentage-value-small">{{ percentFormat(item.finish, item.total) }}%</span> -->
         <!-- <div class="percentage-label-small">{{ item.sceneType }}</div> -->
         <!-- <span class="percentage-label-small">{{ item.finish + '/' + item.total }} </span> -->
-      </div>
-    </el-row>
+      </el-space>
+    </div>
+    <div>
+      <div class="text_title">鏃ョ▼杩涘害</div>
+      <el-space>
+        <el-text style="color: transparent;">鏃ョ▼</el-text>
+        <el-progress
+          style="width: 350px"
+          type="line"
+          color="#00b487"
+          :text-inside="true"
+          :stroke-width="22"
+          :percentage="percentFormat(date, dayCount)"
+        >
+          <template #default="{ percentage }">
+            <span class="percentage-value">{{ `${dateStr} (${percentage}%)` }}</span>
+          </template>
+        </el-progress>
+      </el-space>
+    </div>
+    <!-- </el-row> -->
   </div>
 </template>
 
-<script>
+<script setup>
+import { ref, computed } from 'vue'
+import { useAreaStore } from '@/stores/area.js'
+import dayjs from 'dayjs'
+
 /**
  * 宸℃煡浠诲姟鍖哄煙缁熻淇℃伅
  */
-export default {
-  props: {
-    // name: String,
-    // province: String,
-    // district: String,
-    // planTime: String,
-    // startTime: String,
-    // endTime: String,
-    // userName: String,
-    // status: String,
-    // totaltask: Number,
-    // completetask: Number,
-    // count: Array,
+const areaStore = useAreaStore()
 
-    value: Object
-  },
-  data() {
-    return {}
-  },
-  watch: {},
-  computed: {
-    // total() {
-    //   let t = 0
-    //   this.count.forEach((c) => {
-    //     t += c.total
-    //   })
-    //   return t
-    // },
-    // finish() {
-    //   let t = 0
-    //   this.count.forEach((c) => {
-    //     t += c.finish
-    //   })
-    //   return t
-    // }
-  },
-  methods: {
-    percentFormat(finish, total) {
-      if (total == 0) {
-        return 0
-      } else {
-        const per = finish / total > 1 ? 1 : finish / total
-        return Math.round(per * 100)
-      }
-    },
-    format(percentage) {
-      percentage === 100 ? 'Full' : `${percentage}%`
-    }
+const props = defineProps({
+  value: Object
+})
+
+const width = ref('300px')
+
+const dayCount = computed(() => dayjs(areaStore.area.endtime).daysInMonth())
+const date = computed(() => {
+  const today = dayjs()
+  const planEndTime = dayjs(areaStore.area.endtime)
+  if (today.isBefore(planEndTime)) {
+    return today.date()
+  } else {
+    return planEndTime.daysInMonth()
   }
+})
+const dateStr = computed(()=>{
+  const today = dayjs()
+  const planEndTime = dayjs(areaStore.area.endtime)
+  if (today.isBefore(planEndTime)) {
+    return today.format('MM鏈圖D鏃�')
+  } else {
+    return today.format('MM鏈圖D鏃�')
+  }
+})
+
+function percentFormat(finish, total) {
+  if (total == 0) {
+    return 0
+  } else {
+    const per = finish / total > 1 ? 1 : finish / total
+    return Math.round(per * 100)
+  }
+}
+function format(percentage) {
+  percentage === 100 ? 'Full' : `${percentage}%`
 }
 </script>
 
@@ -118,6 +157,19 @@
 .wrapper {
   border: var(--el-border);
   border-radius: var(--el-border-radius-base);
+  box-shadow: var(--el-box-shadow-lighter);
+  background-color: rgba(161, 161, 161, 0.068);
+  padding: 8px 8px;
+}
+
+.text_title {
+  border-left: 6px solid rgb(49, 221, 6);
+  border-bottom: 1px solid rgb(49, 221, 6);
+  padding: 0px 4px 0px 4px;
+  width: 100px;
+  font-size: 15px;
+  margin-bottom: 4px;
+  /* color: #00b487; */
 }
 
 .flex-bottom {
@@ -145,4 +197,7 @@
   /* margin-top: 10px; */
   font-size: var(--el-font-size-small);
 }
+:deep(.el-progress-bar__outer) {
+  background-color: rgba(211, 211, 211, 0.411);
+}
 </style>
diff --git a/src/main.js b/src/main.js
index 78f6786..c50ba05 100644
--- a/src/main.js
+++ b/src/main.js
@@ -3,6 +3,11 @@
 import * as ElementPlusIconsVue from '@element-plus/icons-vue'
 import 'element-plus/theme-chalk/src/dark/css-vars.scss'
 
+import 'element-plus/theme-chalk/src/overlay.scss';
+import 'element-plus/theme-chalk/src/message.scss';
+import 'element-plus/theme-chalk/src/message-box.scss';
+import 'element-plus/theme-chalk/src/notification.scss';
+
 import App from './App.vue'
 import router from './router'
 import timeUtil from './utils/time-util'
diff --git a/src/views/main/MonitorView.vue b/src/views/main/MonitorView.vue
index fc0eea2..0640fd7 100644
--- a/src/views/main/MonitorView.vue
+++ b/src/views/main/MonitorView.vue
@@ -1,27 +1,31 @@
 <template>
   <BaseMap></BaseMap>
   <el-row class="overlay-container" v-if="true">
-    <el-col :span="17">
-      <el-scrollbar class="page-left-top">
-        <!-- <VisualizationView></VisualizationView> -->
+    <el-col :span="7" class="page-right">
+      <el-scrollbar height="var(--fy-body-height)" class="p-events-auto" style="width: 450px">
+        <TaskStats></TaskStats>
+        <TaskSummary></TaskSummary>
       </el-scrollbar>
-      <el-scrollbar class="page-left-bottom p-events-auto">
-        <!-- <InspectionView></InspectionView> -->
-      </el-scrollbar>
+    </el-col>
+    <el-col :span="10">
+      <el-row justify="end">
+        <SubtaskVisual class="subtask-visual"></SubtaskVisual>
+      </el-row>
+      <!-- <el-scrollbar class="page-left-top"> -->
+      <!-- <VisualizationView></VisualizationView> -->
+      <!-- </el-scrollbar> -->
+      <!-- <el-scrollbar class="page-left-bottom p-events-auto">
+        <InspectionView></InspectionView>
+      </el-scrollbar> -->
     </el-col>
     <el-col :span="7" class="page-right">
       <el-scrollbar height="var(--fy-body-height)" class="p-events-auto">
         <ManagementView></ManagementView>
       </el-scrollbar>
     </el-col>
-    <!-- <el-col :span="7" class="page-right">
-      <el-scrollbar height="var(--fy-body-height)">
-        <StatisticView></StatisticView>
-      </el-scrollbar>
-    </el-col> -->
   </el-row>
   <SupervisionVisual class="supervision-view"></SupervisionVisual>
-  <TaskStats class="task-stats"></TaskStats>
+
   <WorkStream class="work-stream"></WorkStream>
   <!-- <ProblemTrack class="problem-track"></ProblemTrack> -->
 </template>
@@ -47,6 +51,8 @@
 import SupervisionVisual from '@/views/visualization/SupervisionVisual.vue'
 import WorkStream from '@/views/inspection/WorkStream.vue'
 import TaskStats from '@/views/management/TaskStats.vue'
+import TaskSummary from '@/views/management/TaskSummary.vue'
+import SubtaskVisual from '@/views/visualization/SubtaskVisual.vue'
 
 // provide('mapHeight', 'calc(var(--fy-body-height) / 4 * 3)')
 provide('mapHeight', 'calc(var(--fy-body-height))')
@@ -64,10 +70,10 @@
   pName: '涓婃捣甯�',
   cCode: '3100',
   cName: '涓婃捣甯�',
-  dCode: '310106',
-  dName: '闈欏畨鍖�'
+  dCode: '310104',
+  dName: '寰愭眹鍖�'
 })
-areaStore.setSceneType({ label: '宸ュ湴', value: '1' })
+areaStore.setSceneType({ label: '鍏ㄩ儴鍦烘櫙', value: null })
 
 // 鑾峰彇鏈湀鐨勬墍鏈夊贰鏌ョ粺璁′俊鎭�
 subtaskStore.fetchTopTaskProgress(areaStore.area)
@@ -131,4 +137,10 @@
 }
 .problem-track {
 }
+
+.subtask-visual {
+  margin-top: 50px;
+  /* min-width: 450px;
+  max-width: 600px; */
+}
 </style>
diff --git a/src/views/management/TaskStats.vue b/src/views/management/TaskStats.vue
index 7092c58..271414c 100644
--- a/src/views/management/TaskStats.vue
+++ b/src/views/management/TaskStats.vue
@@ -10,7 +10,7 @@
       </el-row> -->
       <!-- <el-row> -->
       <TaskItem v-for="item in tasks" :key="item.guid" :value="item"></TaskItem>
-      <TaskSummary></TaskSummary>
+      <!-- <TaskSummary></TaskSummary> -->
       <!-- </el-row> -->
 
       <!-- <el-row>
@@ -74,11 +74,12 @@
     }
 
     const task = {
-      name: tInfo.name,
-      province: tInfo.provinceName,
-      district: tInfo.districtName,
-      totaltask: tInfo.totaltask,
-      completetask: tInfo.completetask,
+      // name: tInfo.name,
+      // province: tInfo.provinceName,
+      // district: tInfo.districtName,
+      // totaltask: tInfo.totaltask,
+      // completetask: tInfo.completetask,
+      ...tInfo,
       _totaltask,
       _completetask,
       count: []
diff --git a/src/views/management/TaskSummary.vue b/src/views/management/TaskSummary.vue
index 5317441..1926d9f 100644
--- a/src/views/management/TaskSummary.vue
+++ b/src/views/management/TaskSummary.vue
@@ -1,18 +1,20 @@
 <template>
-  <el-row> 宸℃煡姹囨�� </el-row>
-  <el-segmented v-model="value" :options="options" block />
-  <div v-show="value == '浠婃棩姹囨��'">
-    <div ref="echart1" class="bar-chart"></div>
-  </div>
-  <div v-show="value == '鏈懆姹囨��'">
-    <div ref="echart2" class="bar-chart"></div>
-  </div>
-  <div v-show="value == '涓婂懆姹囨��'">
-    <div ref="echart3" class="bar-chart"></div>
-  </div>
-  <div v-show="value == '鏈堝害姹囨��'">
-    <div ref="echart4" class="bar-chart"></div>
-  </div>
+  <BaseCard title="宸℃煡姹囨��">
+    <!-- <el-row> 宸℃煡姹囨�� </el-row> -->
+    <el-segmented v-model="value" :options="options" block />
+    <div v-show="value == '浠婃棩姹囨��'">
+      <div ref="echart1" class="bar-chart"></div>
+    </div>
+    <div v-show="value == '鏈懆姹囨��'">
+      <div ref="echart2" class="bar-chart"></div>
+    </div>
+    <div v-show="value == '涓婂懆姹囨��'">
+      <div ref="echart3" class="bar-chart"></div>
+    </div>
+    <div v-show="value == '鏈湀姹囨��'">
+      <div ref="echart4" class="bar-chart"></div>
+    </div>
+  </BaseCard>
 </template>
 
 <script setup>
@@ -30,8 +32,8 @@
 
 const emits = defineEmits(['update:height'])
 
-const value = ref('鏈堝害姹囨��')
-const options = ['浠婃棩姹囨��', '鏈懆姹囨��', '涓婂懆姹囨��', '鏈堝害姹囨��']
+const value = ref('鏈湀姹囨��')
+const options = ['浠婃棩姹囨��', '鏈懆姹囨��', '涓婂懆姹囨��', '鏈湀姹囨��']
 
 const subtaskToday = ref([])
 const subtaskWeek = ref([])
@@ -139,7 +141,7 @@
     yAxis: [
       {
         type: 'category',
-        data: ['鏁存敼鏁�', '闂鏁�', '瀹屾垚閲�']
+        data: ['澶嶆牳鏁�', '鏁存敼鏁�', '闂鏁�', '瀹屾垚閲�']
       }
     ],
     series: series
@@ -153,6 +155,7 @@
   const totalCount = {
     numByTotal: {},
     numByDistrict: {},
+    numByStreet: {},
     numByScene: {},
     numByUser: {}
   }
@@ -160,6 +163,7 @@
   const proCount = {
     numByTotal: {},
     numByDistrict: {},
+    numByStreet: {},
     numByScene: {},
     numByUser: {}
   }
@@ -167,19 +171,24 @@
   const changeCount = {
     numByTotal: {},
     numByDistrict: {},
+    numByStreet: {},
     numByScene: {},
     numByUser: {}
   }
-  // // 鎸夌敤鎴峰垎绫�
-  // const userCount = {
-  //   numByTotal: {},
-  //   numByDistrict: {},
-  //   numByScene: {},
-  //   numByUser: {}
-  // }
+  // 澶嶆牳鏁�
+  const reCheckCount = {
+    numByTotal: {},
+    numByDistrict: {},
+    numByStreet: {},
+    numByScene: {},
+    numByUser: {}
+  }
+  const historySceneId = []
+
   dataList.forEach((d) => {
     const tName = '鎬昏'
     const dName = d.subtask.districtname
+    const townName = d.scene.townname
     const sType = d.sceneType
     const uName = d.subtask.deployerrealname
 
@@ -190,6 +199,10 @@
     // 浠诲姟閲忓垎鍖哄幙
     totalCount.numByDistrict[dName] = totalCount.numByDistrict[dName]
       ? totalCount.numByDistrict[dName] + 1
+      : 1
+    // 浠诲姟閲忓垎琛楅晣
+    totalCount.numByStreet[townName] = totalCount.numByStreet[townName]
+      ? totalCount.numByStreet[townName] + 1
       : 1
     // 浠诲姟閲忓垎鍦烘櫙绫诲瀷
     totalCount.numByScene[sType] = totalCount.numByScene[sType]
@@ -205,6 +218,10 @@
     // 闂鏁板垎鍖哄幙
     proCount.numByDistrict[dName] = proCount.numByDistrict[dName]
       ? proCount.numByDistrict[dName] + d.proCheckedNum
+      : d.proCheckedNum
+    // 闂鏁板垎琛楅晣
+    proCount.numByStreet[townName] = proCount.numByStreet[townName]
+      ? proCount.numByStreet[townName] + d.proCheckedNum
       : d.proCheckedNum
     // 闂鏁板垎鍦烘櫙绫诲瀷
     proCount.numByScene[sType] = proCount.numByScene[sType]
@@ -223,6 +240,10 @@
     changeCount.numByDistrict[dName] = changeCount.numByDistrict[dName]
       ? changeCount.numByDistrict[dName] + d.changeCheckedNum
       : d.changeCheckedNum
+    // 鏁存敼鏁板垎琛楅晣
+    changeCount.numByStreet[townName] = changeCount.numByStreet[townName]
+      ? changeCount.numByStreet[townName] + d.changeCheckedNum
+      : d.changeCheckedNum
     // 鏁存敼鏁板垎鍦烘櫙绫诲瀷
     changeCount.numByScene[sType] = changeCount.numByScene[sType]
       ? changeCount.numByScene[sType] + d.changeCheckedNum
@@ -231,12 +252,40 @@
     changeCount.numByUser[uName] = changeCount.numByUser[uName]
       ? changeCount.numByUser[uName] + d.changeCheckedNum
       : d.changeCheckedNum
+
+    if (historySceneId.length == 0) {
+      reCheckCount.numByTotal[tName] = 0
+      reCheckCount.numByDistrict[dName] = 0
+      reCheckCount.numByScene[sType] = 0
+      reCheckCount.numByUser[uName] = 0
+    } else if (historySceneId.indexOf(d.sceneId) != -1) {
+      // 澶嶆牳鏁版�昏
+      reCheckCount.numByTotal[tName] = reCheckCount.numByTotal[tName]
+        ? reCheckCount.numByTotal[tName] + 1
+        : 1
+      // 澶嶆牳鏁板垎鍖哄幙
+      reCheckCount.numByDistrict[dName] = reCheckCount.numByDistrict[dName]
+        ? reCheckCount.numByDistrict[dName] + 1
+        : 1
+      // 澶嶆牳鏁板垎琛楅晣
+      reCheckCount.numByStreet[townName] = reCheckCount.numByStreet[townName]
+        ? reCheckCount.numByStreet[townName] + 1
+        : 1
+      // 澶嶆牳鏁板垎鍦烘櫙绫诲瀷
+      reCheckCount.numByScene[sType] = reCheckCount.numByScene[sType]
+        ? reCheckCount.numByScene[sType] + 1
+        : 1
+      // 澶嶆牳鏁板垎鐢ㄦ埛
+      reCheckCount.numByUser[uName] = reCheckCount.numByUser[uName]
+        ? reCheckCount.numByUser[uName] + 1
+        : 1
+    } else {
+      historySceneId.push(d.sceneId)
+    }
   })
 
   let series = {}
-  totalCount
-  proCount
-  changeCount
+  parseSeries(series, reCheckCount)
   parseSeries(series, changeCount)
   parseSeries(series, proCount)
   parseSeries(series, totalCount)
@@ -291,12 +340,33 @@
         label: {
           show: true,
           formatter: '{c}'
-          // position: [0, 10],
+          // position: 'top',
         },
         data: [value]
       }
     }
   }
+  // for (const key in c.numByStreet) {
+  //   const value = c.numByStreet[key]
+  //   if (series[key]) {
+  //     series[key].data.push(value)
+  //   } else {
+  //     series[key] = {
+  //       name: `琛楅晣锛�${key}`,
+  //       type: 'bar',
+  //       stack: 'street',
+  //       emphasis: {
+  //         focus: 'series'
+  //       },
+  //       label: {
+  //         show: true,
+  //         formatter: '{c}'
+  //         // position: 'top',
+  //       },
+  //       data: [value]
+  //     }
+  //   }
+  // }
   for (const key in c.numByScene) {
     const value = c.numByScene[key]
     if (series[key]) {
diff --git a/src/views/visualization/SubtaskVisual.vue b/src/views/visualization/SubtaskVisual.vue
index 5ed728e..3d0196e 100644
--- a/src/views/visualization/SubtaskVisual.vue
+++ b/src/views/visualization/SubtaskVisual.vue
@@ -1,149 +1,196 @@
 <template>
-  <el-scrollbar v-if="mapStore.focusMarker" :height="mapHeight">
-    <el-card class="p-events-auto wrapper">
+  <el-scrollbar v-if="mapStore.focusMarker" class="wrapper">
+    <el-card class="p-events-auto">
       <el-row justify="space-between">
-        <div class="font-small">{{ scene.name }}</div>
+        <!-- <div class="font-small">{{ scene.name }}</div> -->
+        <el-text size="large">{{ scene.name }}</el-text>
         <el-button icon="Close" circle @click="mapStore.focusMarker = undefined"></el-button>
       </el-row>
+      <el-row justify="space-between">
+        <el-text size="small">{{ '鍦板潃锛�' + scene.location }}</el-text>
+      </el-row>
+      <el-space class="m-t-8">
+        <el-tag type="info" effect="plain">
+          {{ scene.districtname + scene.townname }}
+        </el-tag>
+        <el-tag type="info" effect="plain">
+          {{ scene.type }}
+        </el-tag>
+      </el-space>
       <el-divider></el-divider>
-      <div class="font-small">鐘舵�侊細{{ subtask.status }}</div>
-      <div class="font-small">璁″垝锛歿{ $fm.formatYMD(subtask.planstarttime) }}</div>
-      <div v-if="subtask.status != '鏈墽琛�'" class="font-small">
-        <span>鎵ц锛歿{ $fm.formatYMDH(subtask.executionstarttime) }}</span>
-        <span> - </span>
-        <span>{{ $fm.formatYMDH(subtask.executionendtime) }}</span>
+      <el-row justify="space-between">
+        <el-col :span="8" style="text-align: center">
+          <el-text>鐘舵�侊細{{ subtask.status }}</el-text>
+        </el-col>
+        <el-col :span="8" style="text-align: center">
+          <el-text>璁″垝锛歿{ $fm.formatYMD(subtask.planstarttime) }}</el-text>
+        </el-col>
+        <el-col v-if="subtask.status != '鏈墽琛�'" :span="8" style="text-align: center">
+          <el-text>
+            <span>鎵ц锛歿{ $fm.formatH(subtask.executionstarttime) }}</span>
+            <!-- <span> - </span>
+          <span>{{ $fm.formatYMDH(subtask.executionendtime) }}</span> -->
+          </el-text>
+        </el-col>
+      </el-row>
+
+      <el-segmented v-model="value" :options="options" block />
+      <div v-show="value == '鐜板満闂'">
+        <problem-item
+          v-for="(item, i) in problemList"
+          :key="item.guid"
+          :index="i + 1"
+          :problem="item"
+        ></problem-item>
+        <el-empty v-if="problemList.length == 0" description="鏃犵幇鍦洪棶棰�">
+          <template #image> </template>
+        </el-empty>
       </div>
-      <div class="font-small">闂锛�</div>
-      <problem-item
-        v-for="(item, i) in problemList"
-        :key="item.guid"
-        :index="i + 1"
-        :problem="item"
-      ></problem-item>
-      <!-- <div v-for="item in problemList" :key="item.guid">
-        {{ item.problemname }}
-      </div> -->
-      <!-- <el-timeline style="max-width: 600px">
-        <el-timeline-item
-          v-for="(activity, index) in activities"
-          :key="index"
-          :timestamp="activity.timestamp"
-          :hide-timestamp="activity.running"
-          :type="activity.running ? 'danger' : 'success'"
-          :size="activity.running ? 'large' : 'normal'"
-          :hollow="false"
-        >
-          {{ activity.content }}
-        </el-timeline-item>
-      </el-timeline> -->
+      <div v-show="value == '鍦烘櫙鍥剧墖'">
+        <FYImageSelectDialog
+          readonly
+          height="500px"
+          :imageWidth="134"
+          v-loading="scenePicLoading"
+          :typeList="scenePicTypeList"
+          :typeImgMap="scenePicTypeMap"
+        ></FYImageSelectDialog>
+      </div>
+      <div v-show="value == '璁惧璁炬柦'">
+        
+      </div>
     </el-card>
   </el-scrollbar>
 </template>
 
-<script>
-import { inject } from 'vue'
+<script setup>
+import { inject, ref, computed, watch } from 'vue'
 import { useMapStore } from '@/stores/map.js'
 import { mapStores } from 'pinia'
 
+import { $fysp } from '@/api/index.js'
 import problemApi from '@/api/fysp/problemApi.js'
+import mediafileApi from '@/api/fysp/mediafileApi.js'
+import deviceApi from '@/api/fysp/deviceApi'
 
 /**
  * 鍏蜂綋宸℃煡浠诲姟鍙鍖�
  * 鍖呮嫭鍦板浘瀹氫綅淇℃伅灞曠ず銆佸贰鏌ヤ换鍔″叏娴佺▼骞抽摵灞曠ず
  */
-export default {
-  setup() {
-    const mapHeight = inject('mapHeight')
+const mapHeight = inject('mapHeight')
+const height = 'height:' + mapHeight
 
-    const height = 'height:' + mapHeight
-    return { height, mapHeight }
-  },
-  props: {
-    // subtask: {
-    //   type: Object,
-    //   default: () => {
-    //     return {
-    //       guid: 'SMuheEkjswioSn7A',
-    //       name: '涓鐢熸�佹暟瀛楁腐椤圭洰宸℃煡涓鐢熸�佹暟瀛楁腐椤圭洰宸℃煡',
-    //       district: '閲戝北鍖�',
-    //       planTime: '2024-06-04',
-    //       startTime: '2024-06-04 13:31:26',
-    //       endTime: '2024-06-04 13:33:37',
-    //       userName: '鏈辨寮�',
-    //       status: '宸茬粨鏉�',
-    //       total: 4,
-    //       checked: 2
-    //     }
-    //   }
-    // }
-  },
-  data() {
-    return {
-      // activities: [
-      //   {
-      //     content: '浠诲姟鍒涘缓',
-      //     timestamp: '2024-06-04 08:00',
-      //     running: false
-      //   },
-      //   {
-      //     content: '寮�濮嬪贰鏌�',
-      //     timestamp: '2024-06-04 09:00',
-      //     running: false
-      //   },
-      //   {
-      //     content: '缁撴潫宸℃煡',
-      //     timestamp: '2024-06-04 09:15',
-      //     running: false
-      //   },
-      //   {
-      //     content: '瀹屾垚闂瀹℃牳',
-      //     timestamp: '2024-06-04 10:15',
-      //     running: false
-      //   },
-      //   {
-      //     content: '闂鏁存敼涓�...',
-      //     timestamp: '2024-06-04 10:15',
-      //     running: true
-      //   }
-      // ]
-      problemList: []
-    }
-  },
-  computed: {
-    ...mapStores(useMapStore),
-    subtask() {
-      return this.mapStore.focusMarker ? this.mapStore.focusMarker.subtask : undefined
-    },
-    scene() {
-      return this.mapStore.focusMarker ? this.mapStore.focusMarker.scene : undefined
-    },
-    inspection() {
-      return this.mapStore.focusMarker ? this.mapStore.focusMarker.inspection : undefined
-    }
-  },
-  watch: {
-    subtask(nV, oV) {
-      if (nV != undefined && nV != oV) {
-        this.fetchProblem(nV.stguid)
+const mapStore = useMapStore()
+const subtask = computed(() => (mapStore.focusMarker ? mapStore.focusMarker.subtask : undefined))
+const scene = computed(() => (mapStore.focusMarker ? mapStore.focusMarker.scene : undefined))
+const inspection = computed(() =>
+  mapStore.focusMarker ? mapStore.focusMarker.inspection : undefined
+)
+
+const value = ref('鐜板満闂')
+const options = ['鐜板満闂', '鍦烘櫙鍥剧墖', '璁惧璁炬柦']
+
+const problemList = ref([])
+const problemListLoading = ref(false)
+
+const scenePicTypeList = ref([])
+const scenePicTypeMap = ref(new Map())
+const scenePicLoading = ref(false)
+
+watch(subtask, (nV, oV) => {
+  if (nV != undefined && nV != oV) {
+    fetchProblem(nV.stguid)
+    getRoutineByStGuid(nV.stguid)
+  }
+})
+function fetchProblem(stguid) {
+  problemListLoading.value = true
+  problemApi
+    .fetchProblems(stguid)
+    .then((res) => {
+      problemList.value = res
+    })
+    .finally(() => (problemListLoading.value = false))
+}
+
+// 鍥剧墖鍒嗙被
+function getRoutineByStGuid(stguid) {
+  scenePicLoading.value = true
+  mediafileApi
+    .getRoutineByStGuid(stguid)
+    .then((res) => {
+      let typeList = []
+      let typeMap = new Map()
+      const data = res.data
+      for (const e of data) {
+        let img = {
+          url: $fysp.imgUrl + e.extension1 + e.guid + '.jpg',
+          data: e
+        }
+        const businesstype = e.businesstype
+        const businesstypeid = e.businesstypeid
+        if (typeList.find((item) => item.typeId == businesstypeid) != undefined) {
+          typeMap.get(businesstypeid).push(img)
+        } else {
+          typeList.push({
+            typeId: businesstypeid,
+            typeName: businesstype
+          })
+          typeMap.set(businesstypeid, [img])
+        }
       }
+      scenePicTypeList.value = typeList
+      scenePicTypeMap.value = typeMap
+    })
+    .finally(() => (scenePicLoading.value = false))
+}
+
+function fetchDevices() {
+  deviceApi.fetchDevices(scene.value.guid, this.currSelect.topDeviceTypeId).then((result) => {
+    this.initList()
+    if (result.data == null || result.data.length <= 0) {
+      this.isEmpty = true
+      return
     }
-  },
-  methods: {
-    fetchProblem(stguid) {
-      problemApi.fetchProblems(stguid).then((res) => {
-        this.problemList = res
+    // 鏍囧噯鍖栧睘鎬у悕
+    for (let index = 0; index < result.data.length; index++) {
+      var element = this.convertKeys(result.data[index])
+      this.initFormData(element)
+      // 鑾峰彇璁惧鐘舵�佷俊鎭�
+      let data = {
+        deviceId: element.id,
+        sceneId: element.sceneGuid,
+        deviceTypeId: this.currSelect.topDeviceTypeId
+      }
+      deviceApi.fetchDeviceStatus(data).then((status) => {
+        var statusData = status.data
+        var imgPaths = []
+        if (statusData) {
+          if (statusData.length == 0) {
+            this.formInfo.push(element)
+            return
+          }
+          element = this.convertKeys(result.data[index])
+          element = this.setDeviceType(element)
+          element._picUrls = imgPaths
+          for (let index = 0; index < statusData.length; index++) {
+            const statusItem = statusData[index]
+            // 璁惧瀵硅薄娣诲姞涓�涓睘鎬у垪琛ㄥ睘鎬х敤鏉ヤ繚瀛樿澶囩姸鎬�
+            this.saveStatus(element, statusItem)
+            element.dlLocation = statusItem.dlLocation
+            this.formInfo.push(element)
+          }
+        }
       })
     }
-  }
+  })
 }
 </script>
 
 <style scoped>
 .wrapper {
-  /* position: absolute; */
-  top: 0;
-  right: 0;
-  /* background-color: wheat; */
+  width: 450px;
+  max-height: 800px;
 }
 
 .el-card {
diff --git a/src/views/visualization/SupervisionVisual.vue b/src/views/visualization/SupervisionVisual.vue
index 3f78a95..3c1c421 100644
--- a/src/views/visualization/SupervisionVisual.vue
+++ b/src/views/visualization/SupervisionVisual.vue
@@ -49,12 +49,12 @@
         pName: '涓婃捣甯�',
         cCode: '3100',
         cName: '涓婃捣甯�',
-        dCode: '310106',
-        dName: '闈欏畨鍖�'
+        dCode: '310104',
+        dName: '寰愭眹鍖�'
       },
       sceneType: {
-        label: '宸ュ湴',
-        value: '1'
+        label: '鍏ㄩ儴鍦烘櫙',
+        value: null
       },
       time: ''
     }

--
Gitblit v1.9.3