From 0a97c702074830791cb29a158098f05c8033c4b1 Mon Sep 17 00:00:00 2001
From: riku <risaku@163.com>
Date: 星期五, 07 六月 2024 17:39:43 +0800
Subject: [PATCH] 原型制作中

---
 src/views/visualization/SubtaskVisual.vue     |   95 +++
 package-lock.json                             |   11 
 src/assets/styles/base.scss                   |    9 
 src/views/visualization/SupervisionVisual.vue |   70 ++
 src/components.d.ts                           |   16 
 src/components/inspection/TaskSummaryItem.vue |   39 +
 src/components/search/OptionTime.vue          |   59 ++
 src/utils/time-util.js                        |   39 +
 src/views/inspection/SelfInspection.vue       |   20 
 src/api/index.js                              |   99 +++
 src/components/map/BaseMap.vue                |   50 +
 src/api/fysp/taskApi.js                       |   53 +
 src/utils/map/index.js                        |  110 ++++
 src/views/main/MonitorView.vue                |   12 
 src/views/inspection/JointEnforcement.vue     |   20 
 src/views/visualization/VisualizationView.vue |   32 +
 src/assets/icon/scene_1.png                   |    0 
 src/views/inspection/TaskTrack.vue            |   35 +
 src/views/management/ManagementView.vue       |    2 
 src/views/management/TaskSummary.vue          |   26 
 src/views/management/TaskStats.vue            |   15 
 src/components/inspection/TaskNode.vue        |    6 
 src/components/search/OptionSceneType.vue     |   78 ++
 src/enum/location.js                          |  252 +++++++++
 src/utils/map/marks.js                        |  131 ++++
 src/enum/scene.js                             |  126 ++++
 src/views/inspection/InspectionView.vue       |    3 
 src/main.js                                   |    3 
 package.json                                  |    1 
 src/components/search/OptionLocation.vue      |  165 ++++++
 30 files changed, 1,563 insertions(+), 14 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index b3a90df..2cf619c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,7 @@
       "name": "fy-report",
       "version": "0.0.0",
       "dependencies": {
+        "@amap/amap-jsapi-loader": "^1.0.1",
         "@element-plus/icons-vue": "^2.3.1",
         "@vueuse/core": "^10.10.0",
         "axios": "^1.7.2",
@@ -31,6 +32,11 @@
         "vite": "^5.2.8",
         "vitest": "^1.4.0"
       }
+    },
+    "node_modules/@amap/amap-jsapi-loader": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/@amap/amap-jsapi-loader/-/amap-jsapi-loader-1.0.1.tgz",
+      "integrity": "sha512-nPyLKt7Ow/ThHLkSvn2etQlUzqxmTVgK7bIgwdBRTg2HK5668oN7xVxkaiRe3YZEzGzfV2XgH5Jmu2T73ljejw=="
     },
     "node_modules/@antfu/utils": {
       "version": "0.7.8",
@@ -4674,6 +4680,11 @@
     }
   },
   "dependencies": {
+    "@amap/amap-jsapi-loader": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/@amap/amap-jsapi-loader/-/amap-jsapi-loader-1.0.1.tgz",
+      "integrity": "sha512-nPyLKt7Ow/ThHLkSvn2etQlUzqxmTVgK7bIgwdBRTg2HK5668oN7xVxkaiRe3YZEzGzfV2XgH5Jmu2T73ljejw=="
+    },
     "@antfu/utils": {
       "version": "0.7.8",
       "resolved": "https://registry.npmmirror.com/@antfu/utils/-/utils-0.7.8.tgz",
diff --git a/package.json b/package.json
index 7690c78..f9d0366 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,7 @@
     "format": "prettier --write src/"
   },
   "dependencies": {
+    "@amap/amap-jsapi-loader": "^1.0.1",
     "@element-plus/icons-vue": "^2.3.1",
     "@vueuse/core": "^10.10.0",
     "axios": "^1.7.2",
diff --git a/src/api/fysp/taskApi.js b/src/api/fysp/taskApi.js
new file mode 100644
index 0000000..54ab091
--- /dev/null
+++ b/src/api/fysp/taskApi.js
@@ -0,0 +1,53 @@
+import { $fysp } from '../index'
+
+export default {
+  /**
+   * 鑾峰彇椤跺眰浠诲姟
+   */
+  getTopTask() {
+    return $fysp.get('task/alltask/0').then((res) => res.data)
+  },
+
+  /**
+   * 鏌ヨ鎬讳换鍔�
+   * @param {Object} area
+   * @returns
+   */
+  fetchTopTasks(area) {
+    return $fysp.post('task/find', area).then((res) => res.data)
+  },
+
+  /**
+   * 鑾峰彇瀛愪换鍔$粺璁′俊鎭�
+   */
+  fetchSubtaskSummary({ topTaskId = undefined, sceneTypeId = undefined }) {
+    return $fysp
+      .get('subtask/summary', {
+        params: {
+          topTaskId: topTaskId,
+          sceneTypeId: sceneTypeId
+        }
+      })
+      .then((res) => res.data)
+  },
+
+  /**
+   * 鑾峰彇瀛愪换鍔$粺璁′俊鎭�
+   */
+  fetchSubtaskSummaryArea(area) {
+    return $fysp.post('subtask/summary/area', area).then((res) => res.data)
+  },
+
+  /**
+   * 鑾峰彇瀛愪换鍔¢棶棰樿鎯�
+   */
+  getProBySubtask(id) {
+    return $fysp
+      .get('problemlist/subtask', {
+        params: {
+          stGuid: id
+        }
+      })
+      .then((res) => res.data)
+  }
+}
diff --git a/src/api/index.js b/src/api/index.js
new file mode 100644
index 0000000..01609aa
--- /dev/null
+++ b/src/api/index.js
@@ -0,0 +1,99 @@
+import axios from 'axios'
+import { ElMessage } from 'element-plus'
+
+const debug = true
+
+let ip1 = 'http://47.100.191.150:9005/'
+let ip1_file = 'http://47.100.191.150:9005/'
+// let ip1 = 'https://fyami.com.cn:447/';
+// let ip1_file = 'https://fyami.com.cn:447/';
+let ip2 = 'https://fyami.com.cn/'
+let ip2_file = 'https://fyami.com.cn/'
+
+if (debug) {
+  ip1 = 'http://192.168.0.138:8082/'
+  // ip1_file = 'http://47.100.191.150:9005/';
+  // ip2 = 'http://192.168.0.138:8080/';
+  // ip2_file = 'https://fyami.com.cn/';
+}
+
+//椋炵窘鐩戠
+const $fysp = axios.create({
+  baseURL: ip1,
+  timeout: 20000
+})
+$fysp.imgUrl = `${ip1_file}images/`
+$fysp.downloadUrl = `${ip1_file}files/`
+
+//椋炵窘鐜
+const $fytz = axios.create({
+  baseURL: ip2,
+  timeout: 20000
+})
+$fytz.imgUrl = `${ip2_file}images/`
+
+//娣诲姞鎷︽埅鍣�
+;[$fysp, $fytz].forEach((i) => {
+  // 娣诲姞璇锋眰鎷︽埅鍣�
+  i.interceptors.request.use(
+    function (config) {
+      // 鍦ㄥ彂閫佽姹備箣鍓嶅仛浜涗粈涔�
+      console.log('==>璇锋眰寮�濮�')
+      console.log(`${config.baseURL}${config.url}`)
+      if (config.data) {
+        console.log('==>璇锋眰鏁版嵁', config.data)
+      }
+      return config
+    },
+    function (error) {
+      // 瀵硅姹傞敊璇仛浜涗粈涔�
+      console.log('==>璇锋眰寮�濮�')
+      console.log(error)
+      ElMessage({
+        message: error,
+        type: 'error'
+      })
+      return Promise.reject(error)
+    }
+  )
+
+  // 娣诲姞鍝嶅簲鎷︽埅鍣�
+  i.interceptors.response.use(
+    function (response) {
+      // 2xx 鑼冨洿鍐呯殑鐘舵�佺爜閮戒細瑙﹀彂璇ュ嚱鏁般��
+      // 瀵瑰搷搴旀暟鎹仛鐐逛粈涔�
+      console.log(response)
+      console.log('==>璇锋眰缁撴潫')
+      if (response.status == 200) {
+        if (response.data.success != undefined && response.data.success != null) {
+          if (response.data.success == true) {
+            return response
+          } else {
+            ElMessage({
+              message: response.data.message,
+              type: 'error'
+            })
+            return Promise.reject(response.data.message)
+          }
+        } else {
+          return response
+        }
+      } else {
+        return Promise.reject(response)
+      }
+    },
+    function (error) {
+      // 瓒呭嚭 2xx 鑼冨洿鐨勭姸鎬佺爜閮戒細瑙﹀彂璇ュ嚱鏁般��
+      // 瀵瑰搷搴旈敊璇仛鐐逛粈涔�
+      console.log(error)
+      console.log('==>璇锋眰缁撴潫')
+      ElMessage({
+        message: error,
+        type: 'error'
+      })
+      return Promise.reject(error)
+    }
+  )
+})
+
+export { $fysp, $fytz }
diff --git a/src/assets/icon/scene_1.png b/src/assets/icon/scene_1.png
new file mode 100644
index 0000000..972dd1c
--- /dev/null
+++ b/src/assets/icon/scene_1.png
Binary files differ
diff --git a/src/assets/styles/base.scss b/src/assets/styles/base.scss
index 8f27fcf..6227694 100644
--- a/src/assets/styles/base.scss
+++ b/src/assets/styles/base.scss
@@ -115,3 +115,12 @@
   flex-direction: column;
   align-items: center;
 }
+
+/************************************** 榧犳爣浜嬩欢 **************************************/
+.p-events-auto {
+  pointer-events: auto;
+}
+
+.p-events-none {
+  pointer-events: none;
+}
diff --git a/src/components.d.ts b/src/components.d.ts
index fb94fc6..0baba56 100644
--- a/src/components.d.ts
+++ b/src/components.d.ts
@@ -7,21 +7,37 @@
 /* prettier-ignore */
 declare module 'vue' {
   export interface GlobalComponents {
+    BaseMap: typeof import('./components/map/BaseMap.vue')['default']
+    copy: typeof import('./components/search/OptionLocation copy.vue')['default']
     CoreHeader: typeof import('./components/core/CoreHeader.vue')['default']
     ElButton: typeof import('element-plus/es')['ElButton']
     ElCard: typeof import('element-plus/es')['ElCard']
+    ElCascader: typeof import('element-plus/es')['ElCascader']
     ElCol: typeof import('element-plus/es')['ElCol']
     ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
+    ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
+    ElFormItem: typeof import('element-plus/es')['ElFormItem']
+    ElOption: typeof import('element-plus/es')['ElOption']
     ElProgress: typeof import('element-plus/es')['ElProgress']
     ElRow: typeof import('element-plus/es')['ElRow']
+    ElScorllbar: typeof import('element-plus/es')['ElScorllbar']
     ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
+    ElSelect: typeof import('element-plus/es')['ElSelect']
+    ElStatistic: typeof import('element-plus/es')['ElStatistic']
     ElTag: typeof import('element-plus/es')['ElTag']
     ElText: typeof import('element-plus/es')['ElText']
+    ElTimeline: typeof import('element-plus/es')['ElTimeline']
+    ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
+    FYOptionTime: typeof import('./components/search/FYOptionTime.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']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
     SubtaskExamineItem: typeof import('./components/inspection/SubtaskExamineItem.vue')['default']
     SubtaskItem: typeof import('./components/inspection/SubtaskItem.vue')['default']
     TaskItem: typeof import('./components/inspection/TaskItem.vue')['default']
     TaskNode: typeof import('./components/inspection/TaskNode.vue')['default']
+    TaskSummaryItem: typeof import('./components/inspection/TaskSummaryItem.vue')['default']
   }
 }
diff --git a/src/components/inspection/TaskNode.vue b/src/components/inspection/TaskNode.vue
index 4d5b161..71fa853 100644
--- a/src/components/inspection/TaskNode.vue
+++ b/src/components/inspection/TaskNode.vue
@@ -1,6 +1,9 @@
 <template>
   <div>
-    <el-row justify="center">{{ title }}</el-row>
+    <el-row justify="start">
+      <div>{{ title }}</div>
+      <el-text type="danger" size="small">{{ warning }}</el-text>
+    </el-row>
     <div class="border-r-small f-b">
       <div v-for="(item, i) in items" :key="title + i">
         <slot :item="item" :index="i"></slot>
@@ -16,6 +19,7 @@
 export default {
   props: {
     title: String,
+    warning: String,
     count: Number,
     items: Array
   },
diff --git a/src/components/inspection/TaskSummaryItem.vue b/src/components/inspection/TaskSummaryItem.vue
new file mode 100644
index 0000000..2ff508e
--- /dev/null
+++ b/src/components/inspection/TaskSummaryItem.vue
@@ -0,0 +1,39 @@
+<template>
+  <div class="border-r-small p-h-8">
+    <el-row justify="space-between">
+      <div>{{ title }}</div>
+      <el-text size="small">{{ sceneType }}</el-text>
+    </el-row>
+    <el-row>
+      <el-col :span="8">
+        <el-statistic title="宸℃煡" :value="10">
+          <template #suffix>涓�</template>
+        </el-statistic>
+      </el-col>
+      <el-col :span="8">
+        <el-statistic title="闂" :value="16">
+          <template #suffix>涓�</template>
+        </el-statistic>
+      </el-col>
+      <el-col :span="8">
+        <el-statistic title="鏁存敼" :value="8">
+          <template #suffix>涓�</template>
+        </el-statistic>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+export default {
+  props: {
+    title: String,
+    sceneType: {
+      type: String,
+      default: '鍏ㄥ満鏅�'
+    }
+  }
+}
+</script>
+
+<style scoped></style>
diff --git a/src/components/map/BaseMap.vue b/src/components/map/BaseMap.vue
new file mode 100644
index 0000000..51deb80
--- /dev/null
+++ b/src/components/map/BaseMap.vue
@@ -0,0 +1,50 @@
+<template>
+  <div id="container" :style="height"></div>
+</template>
+
+<script setup>
+import { onMounted } from 'vue'
+import { createMap } from '@/utils/map/index'
+// import { createMap } from '@/utils/map/baseMap'
+import { inject } from 'vue'
+
+const mapHeight = inject('mapHeight')
+
+const height = 'height:' + mapHeight
+
+onMounted(() => {
+  // 楂樺痉鍦板浘鍒濆鍖�
+  createMap('container')
+})
+</script>
+<style>
+#container {
+  position: relative;
+  width: 100%;
+  /* height: calc(var(--fy-body-height) / 2); */
+  /* min-height: var(--screen-min-height);
+  min-width: var(--screen-min-width); */
+  z-index: 0px;
+}
+
+/* 鍘婚櫎楂樺痉鍦板浘鐨勫浐瀹氱増鍙峰浘鏍� */
+.amap-logo {
+  display: none;
+  opacity: 0 !important;
+}
+.amap-copyright {
+  display: none;
+  opacity: 0 !important;
+}
+
+.amap-marker-label {
+  font-size: 13px;
+  text-align: center;
+  color: white;
+  background-color: transparent;
+  text-shadow: black 2px 2px 2px;
+  border-radius: 2px;
+  border: 0px;
+  padding: 4px;
+}
+</style>
diff --git a/src/components/search/OptionLocation.vue b/src/components/search/OptionLocation.vue
new file mode 100644
index 0000000..c2368e5
--- /dev/null
+++ b/src/components/search/OptionLocation.vue
@@ -0,0 +1,165 @@
+<template>
+  <!-- <el-form-item :label="placeholder" :prop="prop"> -->
+  <el-cascader
+    :model-value="formatedValue"
+    @change="handleChange"
+    :options="locations"
+    :placeholder="placeholder"
+    :props="optionProps"
+    size="small"
+    :style="'width: ' + width + 'px'"
+  />
+  <!-- </el-form-item> -->
+</template>
+
+<script>
+import { enumLocation } from '@/enum/location'
+
+export default {
+  props: {
+    // 鏄惁鍦ㄩ閫夐」澶勬坊鍔犫�滃叏閮ㄢ�濋�夐」
+    allOption: {
+      type: Boolean,
+      default: true
+    },
+    // 鏌ヨ鐨勮鏀跨骇鍒紝鍙栧��1锛�2锛�3锛�4, 5, 6
+    level: {
+      type: Number,
+      default: 4
+    },
+    // 缁撴灉杩斿洖
+    modelValue: Object,
+    // 鏄惁榛樿杩斿洖鍒濆閫夐」
+    initValue: {
+      type: Boolean,
+      default: true
+    },
+    // 鑳藉惁閫夋嫨浠绘剰涓�绾ч�夐」
+    checkStrictly: {
+      type: Boolean,
+      default: true
+    },
+    prop: {
+      type: String,
+      default: '_locations'
+    },
+    width: {
+      type: Number,
+      default: 220
+    }
+  },
+  emits: ['update:modelValue'],
+  data() {
+    return {
+      locations: enumLocation(this.allOption, this.level),
+      optionProps: {
+        checkStrictly: this.checkStrictly
+      }
+    }
+  },
+  computed: {
+    placeholder() {
+      const list = '鐪�/甯�/鍖�/闀�/闆�/鐗�'.split('/')
+      const p = []
+      for (let i = 0; i < this.level; i++) {
+        p.push(list[i])
+      }
+      return p.join('/')
+    },
+    formatedValue() {
+      return this.optionFormatReverse(this.modelValue)
+    }
+  },
+  methods: {
+    handleChange(value) {
+      this.$emit('update:modelValue', this.optionFormat(value))
+    },
+    /**
+     * 鍦板尯閫夐」缁撴灉鏍煎紡鍖�
+     */
+    optionFormat(val) {
+      const res = {
+        pCode: null,
+        pName: null,
+        cCode: null,
+        cName: null,
+        dCode: null,
+        dName: null,
+        tCode: null,
+        tName: null,
+        aCode: null,
+        aName: null,
+        mCode: null,
+        mName: null
+      }
+      if (val.length > 0) {
+        res.pCode = val[0][0]
+        res.pName = val[0][1]
+      }
+      if (val.length > 1) {
+        res.cCode = val[1][0]
+        res.cName = val[1][1]
+      }
+      if (val.length > 2) {
+        res.dCode = val[2][0]
+        res.dName = val[2][1]
+      }
+      if (val.length > 3) {
+        res.tCode = val[3][0]
+        res.tName = val[3][1]
+      }
+      if (val.length > 4) {
+        res.aCode = val[4][0]
+        res.aName = val[4][1]
+      }
+      if (val.length > 5) {
+        res.mCode = val[5][0]
+        res.mName = val[5][1]
+      }
+      return res
+    },
+    optionFormatReverse(val) {
+      const res = []
+      if (val) {
+        if (val.pName) {
+          res.push([val.pCode, val.pName])
+        }
+        if (val.cName) {
+          res.push([val.cCode, val.cName])
+        }
+        if (val.dName) {
+          res.push([val.dCode, val.dName])
+        }
+        if (val.tName) {
+          res.push([val.tCode, val.tName])
+        }
+        if (val.aName) {
+          res.push([val.aCode, val.aName])
+        }
+        if (val.mName) {
+          res.push([val.mCode, val.mName])
+        }
+      }
+      return res
+    }
+  },
+  mounted() {
+    if (this.initValue) {
+      if (this.checkStrictly) {
+        this.handleChange([this.locations[0].value])
+      } else {
+        const f = (location) => {
+          if (location.children && location.children.length > 0) {
+            const r = f(location.children[0])
+            r.unshift(location.value)
+            return r
+          } else {
+            return [location.value]
+          }
+        }
+        this.handleChange(f(this.locations[0]))
+      }
+    }
+  }
+}
+</script>
diff --git a/src/components/search/OptionSceneType.vue b/src/components/search/OptionSceneType.vue
new file mode 100644
index 0000000..4fd1f73
--- /dev/null
+++ b/src/components/search/OptionSceneType.vue
@@ -0,0 +1,78 @@
+<template>
+  <!-- <el-form-item label="鍦烘櫙绫诲瀷" :prop="prop"> -->
+  <el-select
+    :model-value="modelValue"
+    @change="handleChange"
+    placeholder="鍦烘櫙绫诲瀷"
+    size="small"
+    :style="'width: ' + width + 'px'"
+  >
+    <el-option v-for="s in sceneTypes" :key="s.value" :label="s.label" :value="s" />
+  </el-select>
+  <!-- </el-form-item> -->
+</template>
+
+<script>
+import { enumScene } from '@/enum/scene'
+
+export default {
+  props: {
+    // 鏄惁鍦ㄩ閫夐」澶勬坊鍔犫�滃叏閮ㄢ�濋�夐」
+    allOption: {
+      type: Boolean,
+      default: true
+    },
+    // 1:椋炵窘鐜绯荤粺锛�2锛氶缇界洃绠$郴缁燂紱
+    type: {
+      type: Number,
+      default: 1
+    },
+    // 杩斿洖缁撴灉
+    modelValue: Object,
+    // 鏄惁榛樿杩斿洖鍒濆閫夐」
+    initValue: {
+      type: Boolean,
+      default: true
+    },
+    // form琛ㄥ崟缁戝畾灞炴�у悕
+    prop: {
+      type: String,
+      default: '_scenetype'
+    },
+    // 鍒囨崲 type 鍚庯紝褰撳墠閫夐」鏄惁娓呯┖
+    sourceInit: {
+      type: Boolean,
+      default: true
+    },
+    width: {
+      type: Number,
+      default: 220
+    }
+  },
+  emits: ['update:modelValue'],
+  data() {
+    return {
+      // sceneTypes: enumScene(this.type, this.allOption),
+    }
+  },
+  computed: {
+    sceneTypes() {
+      if (this.sourceInit) {
+        // 褰撳洜涓簍ype鎴栬�卆llOption鍙傛暟鍙樺寲寮曡捣閫夐」鍙樻洿鏃讹紝娓呯┖褰撳墠閫夐」
+        this.handleChange()
+      }
+      return enumScene(this.type, this.allOption)
+    }
+  },
+  methods: {
+    handleChange(value) {
+      this.$emit('update:modelValue', value)
+    }
+  },
+  mounted() {
+    if (this.initValue) {
+      this.handleChange(this.sceneTypes[0])
+    }
+  }
+}
+</script>
diff --git a/src/components/search/OptionTime.vue b/src/components/search/OptionTime.vue
new file mode 100644
index 0000000..f9f7bc5
--- /dev/null
+++ b/src/components/search/OptionTime.vue
@@ -0,0 +1,59 @@
+<template>
+  <!-- <el-form-item label="鏃堕棿" :prop="prop"> -->
+  <el-date-picker
+    v-model="date"
+    @change="handleChange"
+    :type="type"
+    placeholder="閫夋嫨鏃堕棿"
+    size="small"
+    style="width: 100px"
+  />
+  <!-- </el-form-item> -->
+</template>
+
+<script>
+const MONTH = 'month'
+
+export default {
+  props: {
+    type: {
+      type: String,
+      default: MONTH
+    },
+    // 杩斿洖缁撴灉
+    modelValue: Date,
+    // 鏄惁榛樿杩斿洖鍒濆閫夐」
+    initValue: {
+      type: Boolean,
+      default: true
+    },
+    prop: String
+  },
+  emits: ['update:modelValue'],
+  data() {
+    return {
+      date: this.modelValue
+    }
+  },
+  computed: {},
+  methods: {
+    handleChange(value) {
+      this.$emit('update:modelValue', value)
+    },
+    timeFormat() {
+      switch (this.type) {
+        case MONTH:
+          return 'YYYY-MM'
+        default:
+          return 'YYYY-MM'
+      }
+    }
+  },
+  mounted() {
+    if (this.initValue) {
+      this.date = new Date()
+      this.handleChange(this.date)
+    }
+  }
+}
+</script>
diff --git a/src/enum/location.js b/src/enum/location.js
new file mode 100644
index 0000000..3401361
--- /dev/null
+++ b/src/enum/location.js
@@ -0,0 +1,252 @@
+/**
+ * 鑾峰彇琛屾斂鍖哄垝
+ * @param {Boolean} allOption 鏄惁鍦ㄥご閮ㄦ坊鍔犫�滃叏閮ㄢ�濋�夐」
+ * @param {Number} level 鑾峰彇鐨勫垎绫绘繁搴︼紝鑼冨洿 1 - 4
+ * @returns
+ */
+function enumLocation(allOption = true, level = 4) {
+  const l = _enumLocation()
+  if (!allOption) {
+    l.shift()
+  }
+  _deleteByLevel(l, level, 1)
+
+  return l
+}
+
+function _enumLocation() {
+  return [
+    {
+      label: '鍏ㄥ競',
+      value: [null, '鍏ㄥ競']
+    },
+    {
+      label: '涓婃捣甯�',
+      value: ['31', '涓婃捣甯�'],
+      children: [
+        {
+          label: '涓婃捣甯�',
+          value: ['3100', '涓婃捣甯�'],
+          children: [
+            {
+              label: '閲戝北鍖�',
+              value: ['310116', '閲戝北鍖�'],
+              children: [
+                { label: '寮犲牥闀�', value: ['310116103', '寮犲牥闀�'] },
+                { label: '浜灄闀�', value: ['310116104', '浜灄闀�'] },
+                { label: '鍚曞贩闀�', value: ['310116105', '鍚曞贩闀�'] },
+                { label: '寤婁笅闀�', value: ['310116107', '寤婁笅闀�'] },
+                { label: '楂樻柊鍖�', value: ['310116503', '楂樻柊鍖�'] },
+                { label: '閲戝北鍗晣', value: ['310116109', '閲戝北鍗晣'] },
+                { label: '婕曟尘闀�', value: ['310116112', '婕曟尘闀�'] },
+                {
+                  label: '灞遍槼闀�',
+                  value: ['310116113', '灞遍槼闀�'],
+                  children: [
+                    {
+                      label: '涓囪揪骞垮満',
+                      value: ['31011611301', '涓囪揪骞垮満']
+                    }
+                  ]
+                },
+                { label: '鐭冲寲琛楅亾', value: ['310116001', '鐭冲寲琛楅亾'] },
+                { label: '鏈辨尘闀�', value: ['310116101', '鏈辨尘闀�'] },
+                { label: '鏋尘闀�', value: ['310116102', '鏋尘闀�'] },
+                { label: '纰宠胺缁挎咕', value: ['9000', '纰宠胺缁挎咕'] }
+              ]
+            },
+            {
+              label: '寰愭眹鍖�',
+              value: ['310104', '寰愭眹鍖�'],
+              children: [
+                {
+                  label: '婕曟渤娉炬柊鍏存妧鏈紑鍙戝尯',
+                  value: ['310104501', '婕曟渤娉炬柊鍏存妧鏈紑鍙戝尯']
+                },
+                { label: '婀栧崡璺閬�', value: ['310104004', '婀栧崡璺閬�'] },
+                { label: '澶╁钩璺閬�', value: ['310104003', '澶╁钩璺閬�'] },
+                { label: '铏规璺閬�', value: ['310104012', '铏规璺閬�'] },
+                { label: '鏋灄璺閬�', value: ['310104008', '鏋灄璺閬�'] },
+                { label: '鏂滃湡璺閬�', value: ['310104007', '鏂滃湡璺閬�'] },
+                { label: '闀挎ˉ琛楅亾', value: ['310104010', '闀挎ˉ琛楅亾'] },
+                {
+                  label: '鐢版灄琛楅亾',
+                  value: ['310104011', '鐢版灄琛楅亾'],
+                  children: [
+                    {
+                      label: '鐢板皻鍧�',
+                      value: ['31010401101', '鐢板皻鍧�']
+                    }
+                  ]
+                },
+                { label: '搴峰仴鏂版潙琛楅亾', value: ['310104013', '搴峰仴鏂版潙琛楅亾'] },
+                {
+                  label: '寰愬姹囪閬�',
+                  value: ['310104014', '寰愬姹囪閬�'],
+                  children: [
+                    {
+                      label: '澶╅挜妗�',
+                      value: ['31010401401', '澶╅挜妗�']
+                    }
+                  ]
+                },
+                { label: '鍑屼簯璺閬�', value: ['310104015', '鍑屼簯璺閬�'] },
+                { label: '榫欏崕琛楅亾', value: ['310104016', '榫欏崕琛楅亾'] },
+                { label: '婕曟渤娉捐閬�', value: ['310104017', '婕曟渤娉捐閬�'] },
+                { label: '鍗庢尘闀�', value: ['310104103', '鍗庢尘闀�'] }
+              ]
+            },
+            {
+              label: '闈欏畨鍖�',
+              value: ['310106', '闈欏畨鍖�'],
+              children: [
+                {
+                  label: '澶у畞璺閬�',
+                  value: ['310106019', '澶у畞璺閬�'],
+                  children: [
+                    {
+                      label: '涔呭厜涓績',
+                      value: ['31010601901', '涔呭厜涓績']
+                    }
+                  ]
+                },
+                { label: '褰郸鏂版潙琛楅亾', value: ['310106020', '褰郸鏂版潙琛楅亾'] },
+                { label: '涓存本璺閬�', value: ['310106021', '涓存本璺閬�'] },
+                { label: '鑺锋睙瑗胯矾琛楅亾', value: ['310106022', '鑺锋睙瑗胯矾琛楅亾'] },
+                {
+                  label: '褰郸闀�',
+                  value: ['310106101', '褰郸闀�'],
+                  children: [
+                    {
+                      label: '澶ц瀺鍩�',
+                      value: ['31010610101', '澶ц瀺鍩�']
+                    }
+                  ]
+                },
+                { label: '姹熷畞璺閬�', value: ['310106006', '姹熷畞璺閬�'] },
+                { label: '鐭抽棬浜岃矾琛楅亾', value: ['310106011', '鐭抽棬浜岃矾琛楅亾'] },
+                {
+                  label: '鍗椾含瑗胯矾琛楅亾',
+                  value: ['310106012', '鍗椾含瑗胯矾琛楅亾'],
+                  children: [
+                    {
+                      label: 'X88',
+                      value: ['31010601201', 'X88']
+                    }
+                  ]
+                },
+                { label: '闈欏畨瀵鸿閬�', value: ['310106013', '闈欏畨瀵鸿閬�'] },
+                {
+                  label: '鏇瑰娓¤閬�',
+                  value: ['310106014', '鏇瑰娓¤閬�'],
+                  children: [
+                    {
+                      label: '889',
+                      value: ['31010601401', '889']
+                    }
+                  ]
+                },
+                { label: '澶╃洰瑗胯矾琛楅亾', value: ['310106015', '澶╃洰瑗胯矾琛楅亾'] },
+                {
+                  label: '鍖楃珯琛楅亾',
+                  value: ['310106016', '鍖楃珯琛楅亾'],
+                  children: [
+                    {
+                      label: '澶ф偊鍩�',
+                      value: ['31010601601', '澶ф偊鍩�']
+                    }
+                  ]
+                },
+                { label: '瀹濆北璺閬�', value: ['310106017', '瀹濆北璺閬�'] },
+                { label: '鍏卞拰鏂拌矾琛楅亾', value: ['310106018', '鍏卞拰鏂拌矾琛楅亾'] }
+              ]
+            },
+            {
+              label: '鏅檧鍖�',
+              value: ['310107', '鏅檧鍖�'],
+              children: [
+                { label: '鏇规潹鏂版潙琛楅亾', value: ['310107005', '鏇规潹鏂版潙琛楅亾'] },
+                { label: '涓囬噷琛楅亾', value: ['310107021', '涓囬噷琛楅亾'] },
+                { label: '鐪熷闀囪閬�', value: ['310107022', '鐪熷闀囪閬�'] },
+                { label: '闀垮緛闀�', value: ['310107102', '闀垮緛闀�'] },
+                { label: '妗冩郸闀�', value: ['310107103', '妗冩郸闀�'] },
+                { label: '鐭虫硥璺閬�', value: ['310107017', '鐭虫硥璺閬�'] },
+                { label: '鐢樻硥璺閬�', value: ['310107016', '鐢樻硥璺閬�'] },
+                { label: '闀垮璺閬�', value: ['310107015', '闀垮璺閬�'] },
+                { label: '闀块鏂版潙琛楅亾', value: ['310107014', '闀块鏂版潙琛楅亾'] },
+                { label: '瀹滃窛璺閬�', value: ['310107020', '瀹滃窛璺閬�'] }
+              ]
+            },
+            {
+              label: '闂佃鍖�',
+              value: ['310112', '闂佃鍖�'],
+              children: [
+                { label: '姹熷窛璺閬�', value: ['310112001', '姹熷窛璺閬�'] },
+                { label: '鍙ょ編琛楅亾', value: ['310112006', '鍙ょ編琛楅亾'] },
+                { label: '鏂拌櫣琛楅亾', value: ['310112008', '鏂拌櫣琛楅亾'] },
+                { label: '娴﹂敠琛楅亾', value: ['310112009', '娴﹂敠琛楅亾'] },
+                { label: '鑾樺簞闀�', value: ['310112101', '鑾樺簞闀�'] },
+                { label: '涓冨疂闀�', value: ['310112102', '涓冨疂闀�'] },
+                { label: '棰涙ˉ闀�', value: ['310112103', '棰涙ˉ闀�'] },
+                { label: '鍗庢紩闀�', value: ['310112106', '鍗庢紩闀�'] },
+                { label: '铏规ˉ闀�', value: ['310112107', '铏规ˉ闀�'] },
+                { label: '姊呴檱闀�', value: ['310112108', '姊呴檱闀�'] },
+                { label: '鍚存尘闀�', value: ['310112110', '鍚存尘闀�'] },
+                { label: '椹ˉ闀�', value: ['310112112', '椹ˉ闀�'] },
+                { label: '娴︽睙闀�', value: ['310112114', '娴︽睙闀�'] },
+                { label: '鑾樺簞宸ヤ笟鍖�', value: ['310112501', '鑾樺簞宸ヤ笟鍖�'] }
+              ]
+            },
+            {
+              label: '闀垮畞鍖�',
+              value: ['310105', '闀垮畞鍖�'],
+              children: [
+                { label: '鍗庨槼璺閬�', value: ['310105001', '鍗庨槼璺閬�'] },
+                { label: '姹熻嫃璺閬�', value: ['310105002', '姹熻嫃璺閬�'] },
+                { label: '鏂板崕璺閬�', value: ['310105004', '鏂板崕璺閬�'] },
+                { label: '鍛ㄥ妗ヨ閬�', value: ['310105005', '鍛ㄥ妗ヨ閬�'] },
+                { label: '澶╁北璺閬�', value: ['310105006', '澶╁北璺閬�'] },
+                { label: '浠欓湠鏂版潙琛楅亾', value: ['310105008', '浠欓湠鏂版潙琛楅亾'] },
+                { label: '铏规ˉ琛楅亾', value: ['310105009', '铏规ˉ琛楅亾'] },
+                { label: '绋嬪妗ヨ閬�', value: ['310105010', '绋嬪妗ヨ閬�'] },
+                { label: '鍖楁柊娉捐閬�', value: ['310105011', '鍖楁柊娉捐閬�'] },
+                { label: '鏂版尘闀�', value: ['310105102', '鏂版尘闀�'] }
+              ]
+            },
+            {
+              label: '瀹濆北鍖�',
+              value: ['310113', '瀹濆北鍖�'],
+              children: []
+            },
+            {
+              label: '鍢夊畾鍖�',
+              value: ['310114', '鍢夊畾鍖�'],
+              children: []
+            }
+          ]
+        }
+      ]
+    }
+  ]
+}
+
+// 鎸夌収闇�姹傜殑瀹氫綅绮惧害杩斿洖瀵瑰簲鏁版嵁
+function _deleteByLevel(locations, level, step) {
+  if (step == level) {
+    locations.forEach((l) => {
+      if (l.children) {
+        l.children = undefined
+      }
+    })
+    return
+  } else {
+    step++
+    locations.forEach((l) => {
+      if (l.children) {
+        _deleteByLevel(l.children, level, step)
+      }
+    })
+  }
+}
+
+export { enumLocation }
diff --git a/src/enum/scene.js b/src/enum/scene.js
new file mode 100644
index 0000000..6667e9d
--- /dev/null
+++ b/src/enum/scene.js
@@ -0,0 +1,126 @@
+/**
+ * 鍦烘櫙绫诲瀷鏋氫妇
+ * @param {Number} type 1:椋炵窘鐜绯荤粺锛�2锛氶缇界洃绠$郴缁燂紱
+ * @param {Boolean} allOption 鏄惁鍦ㄥご閮ㄦ坊鍔犫�滃叏閮ㄢ�濋�夐」
+ */
+function enumScene(type, allOption = true) {
+  let l
+  switch (type) {
+    case 1:
+      l = _enumScene_1()
+      break
+    case 2:
+      l = _enumScene_2()
+      break
+    default:
+      l = _enumScene_1()
+      break
+  }
+  if (!allOption) {
+    l.shift()
+  }
+
+  return l
+}
+
+function getSceneName(value, type = 1) {
+  return enumScene(type).find((v) => {
+    if (v.value == value) {
+      return v
+    }
+  })
+}
+
+// 椋炵窘鐜绯荤粺
+function _enumScene_1() {
+  return [
+    {
+      label: '鍏ㄩ儴鍦烘櫙',
+      value: null
+    },
+    {
+      label: '椁愰ギ',
+      value: '1'
+    },
+    {
+      label: '宸ュ湴',
+      value: '2'
+    },
+    {
+      label: '鐮佸ご',
+      value: '3'
+    },
+    {
+      label: '鍫嗗満',
+      value: '4'
+    },
+    {
+      label: '鎼呮媽绔�',
+      value: '5'
+    },
+    {
+      label: '宸ヤ笟浼佷笟',
+      value: '6'
+    },
+    {
+      label: '姹戒慨',
+      value: '7'
+    },
+    {
+      label: '瀹為獙瀹�',
+      value: '8'
+    },
+    {
+      label: '鍖荤枟鏈烘瀯',
+      value: '9'
+    }
+  ]
+}
+
+// 椋炵窘鐩戠绯荤粺
+function _enumScene_2() {
+  return [
+    {
+      label: '鍏ㄩ儴鍦烘櫙',
+      value: null
+    },
+    {
+      label: '宸ュ湴',
+      value: '1'
+    },
+    {
+      label: '鐮佸ご',
+      value: '2'
+    },
+    {
+      label: '鎼呮媽绔�',
+      value: '3'
+    },
+    {
+      label: '宸ヤ笟浼佷笟',
+      value: '4'
+    },
+    {
+      label: '椁愰ギ',
+      value: '5'
+    },
+    {
+      label: '姹戒慨',
+      value: '6'
+    },
+    {
+      label: '閬撹矾鎵皹鐩戞祴鐐�',
+      value: '9'
+    },
+    {
+      label: '閬撹矾',
+      value: '10'
+    },
+    {
+      label: '鍫嗗満',
+      value: '14'
+    }
+  ]
+}
+
+export { enumScene, getSceneName }
diff --git a/src/main.js b/src/main.js
index e491791..af0994d 100644
--- a/src/main.js
+++ b/src/main.js
@@ -4,9 +4,12 @@
 
 import App from './App.vue'
 import router from './router'
+import timeUtil from './utils/time-util'
 
 const app = createApp(App)
 
+app.config.globalProperties.$fm = timeUtil
+
 // elementUI Icon 娉ㄥ唽
 for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
   app.component(key, component)
diff --git a/src/utils/map/index.js b/src/utils/map/index.js
new file mode 100644
index 0000000..4341f17
--- /dev/null
+++ b/src/utils/map/index.js
@@ -0,0 +1,110 @@
+import AMapLoader from '@amap/amap-jsapi-loader'
+
+var mapInitDone = false
+var onMapMountedEvents = []
+
+var AMap
+// 鍦板浘瀵硅薄
+var map
+// 鍗槦鍥惧眰
+var satellite
+// 榧犳爣缁樺浘
+var mouseTool
+// 3D鍥惧眰
+var object3Dlayer
+// 鍦板浘鎷栧姩鐘舵��
+var isDragging = false
+
+// 鍦板浘鍔犺浇瀹屾垚瑙﹀彂
+function onMapMounted(...events) {
+  if (mapInitDone) {
+    events.forEach((e) => {
+      e()
+    })
+  } else {
+    onMapMountedEvents = onMapMountedEvents.concat(events)
+  }
+}
+
+function createMap(id) {
+  AMapLoader.load({
+    key: 'c55f27799afbfa69dc5a3fad90cafe51', // 鐢宠濂界殑Web绔紑鍙戣�匥ey锛岄娆¤皟鐢� load 鏃跺繀濉�
+    version: '2.0', // 鎸囧畾瑕佸姞杞界殑 JS API 鐨勭増鏈紝缂虹渷鏃堕粯璁や负 1.4.15
+    plugins: [
+      'Map3D',
+      'ElasticMarker',
+      'AMap.ControlBar',
+      'AMap.ToolBar',
+      'AMap.Scale',
+      'AMap.DragRoute',
+      'AMap.MouseTool',
+      'AMap.PolygonEditor'
+    ] // 闇�瑕佷娇鐢ㄧ殑鐨勬彃浠跺垪琛紝濡傛瘮渚嬪昂'AMap.Scale'绛�
+  })
+    .then((_AMap) => {
+      AMap = _AMap
+      _initMap(id)
+      mapInitDone = true
+      onMapMountedEvents.forEach((e) => {
+        e()
+      })
+      onMapMountedEvents = []
+    })
+    .catch((e) => {
+      console.log(e)
+    })
+}
+
+function _initMap(elementId) {
+  map = new AMap.Map(elementId, {
+    rotateEnable: true,
+    pitchEnable: true,
+    alwaysRender: false,
+    showLabel: true,
+    showBuildingBlock: true,
+    mapStyle: 'amap://styles/e1e78509de64ddcd2efb4cb34c6fae2a',
+    features: ['bg', 'road'],
+    pitch: 45, // 鍦板浘淇话瑙掑害锛屾湁鏁堣寖鍥� 0 搴�- 83 搴�
+    viewMode: '3D', // 鍦板浘妯″紡
+    resizeEnable: true,
+    center: [121.603928, 31.252955],
+    zooms: [3, 18],
+    zoom: 14
+  })
+
+  // 娣诲姞鍗槦鍦板浘
+  satellite = new AMap.TileLayer.Satellite()
+  satellite.hide()
+  map.add([satellite])
+
+  _initMouseTool()
+  // _init3DLayer();
+  _initDragEvent()
+}
+
+// 榧犳爣缁樺浘鍒濆鍖�
+function _initMouseTool() {
+  mouseTool = new AMap.MouseTool(map)
+}
+
+// 3D鍥惧眰鍒濆鍖�
+// function _init3DLayer() {
+//   object3Dlayer = new AMap.Object3DLayer();
+//   map.add(object3Dlayer);
+// }
+
+// 璁剧疆鍦板浘鎷栨嫿鐩戝惉浜嬩欢
+function _initDragEvent() {
+  let dragEndEvent
+  map.on('dragstart', () => {
+    clearTimeout(dragEndEvent)
+    isDragging = true
+  })
+  map.on('dragend', function () {
+    dragEndEvent = setTimeout(() => {
+      isDragging = false
+    }, 8000)
+  })
+}
+
+export { createMap, onMapMounted, map, AMap, mouseTool, object3Dlayer, isDragging }
diff --git a/src/utils/map/marks.js b/src/utils/map/marks.js
new file mode 100644
index 0000000..8882fea
--- /dev/null
+++ b/src/utils/map/marks.js
@@ -0,0 +1,131 @@
+/**
+ * 楂樺痉鍦板浘鐐规爣璁扮粯鍒剁浉鍏�
+ */
+
+import { map, AMap } from './index'
+
+var _massMarks = undefined
+
+export default {
+  /**
+   * 缁樺埗娴烽噺鐐规爣璁�
+   * @param fDatas 瀹屾暣鐩戞祴鏁版嵁
+   * @param _factor 褰撳墠灞曠ず鐨勭洃娴嬪洜瀛愬璞�
+   */
+  drawMassMarks(fDatas, _factor, onClick) {
+    if (_massMarks) {
+      map.remove(_massMarks)
+      _massMarks = undefined
+    }
+    const lnglats = fDatas.lnglats_GD
+    var data = []
+    for (let i = 0; i < lnglats.length; i++) {
+      data.push({
+        lnglat: lnglats[i], //鐐规爣璁颁綅缃�
+        name: `${fDatas.times[i]}<br/>${_factor.factorName}: ${_factor.datas[i].factorData} mg/m鲁`,
+        id: i
+      })
+    }
+
+    // 鍒涘缓鏍峰紡瀵硅薄
+    var styleObject = {
+      url: 'https://a.amap.com/jsapi_demos/static/images/mass1.png',
+      // url: './asset/mipmap/ic_up_white.png', // 鍥炬爣鍦板潃
+
+      size: new AMap.Size(11, 11), // 鍥炬爣澶у皬
+
+      anchor: new AMap.Pixel(5, 5) // 鍥炬爣鏄剧ず浣嶇疆鍋忕Щ閲忥紝鍩哄噯鐐逛负鍥炬爣宸︿笂瑙�
+    }
+
+    var massMarks = new AMap.MassMarks(data, {
+      zIndex: 5, // 娴烽噺鐐瑰浘灞傚彔鍔犵殑椤哄簭
+      zooms: [15, 18], // 鍦ㄦ寚瀹氬湴鍥剧缉鏀剧骇鍒寖鍥村唴灞曠ず娴烽噺鐐瑰浘灞�
+      style: styleObject // 璁剧疆鏍峰紡瀵硅薄
+    })
+    massMarks.on('click', (event) => {
+      const i = event.data.id
+      // 3. 鑷畾涔夌偣鍑讳簨浠�
+      onClick(i)
+    })
+
+    var marker = new AMap.Marker({
+      content: ' ',
+      map: map,
+
+      offset: new AMap.Pixel(13, 12)
+    })
+    var timeout
+    massMarks.on('mouseover', (e) => {
+      if (timeout) {
+        clearTimeout(timeout)
+      }
+      marker.setPosition(e.data.lnglat)
+      marker.setLabel({ content: e.data.name })
+      map.add(marker)
+      timeout = setTimeout(() => {
+        map.remove(marker)
+      }, 2000)
+    })
+    _massMarks = massMarks
+    map.add(massMarks)
+  },
+
+  createLabelMarks(img, dataList) {
+    const layer = new AMap.LabelsLayer({
+      zooms: [3, 20],
+      zIndex: 1000,
+      // 寮�鍚爣娉ㄩ伩璁╋紝榛樿涓哄紑鍚紝v1.4.15 鏂板灞炴��
+      collision: true,
+      // 寮�鍚爣娉ㄦ贰鍏ュ姩鐢伙紝榛樿涓哄紑鍚紝v1.4.15 鏂板灞炴��
+      animation: true
+    })
+
+    map.add(layer)
+
+    // var markers = [];
+    for (var i = 0; i < dataList.length; i++) {
+      const data = dataList[i]
+      var curData = {
+        name: data.sceneName,
+        position: [data.longitude, data.latitude],
+        zooms: [10, 20],
+        opacity: 1,
+        zIndex: 10,
+        icon: {
+          type: 'image',
+          image: img,
+          // clipOrigin: [14, 92],
+          // clipSize: [50, 68],
+          size: [30, 30],
+          anchor: 'bottom-center',
+          angel: 0,
+          retina: true
+        },
+        text: {
+          content: data.sceneName,
+          direction: 'top',
+          offset: [0, -5],
+          style: {
+            fontSize: 16,
+            fontWeight: 'normal',
+            fillColor: '#fff',
+            strokeColor: '#333',
+            strokeWidth: 0,
+            backgroundColor: '#122b54a9'
+          }
+        }
+      }
+      curData.extData = {
+        index: i
+      }
+
+      var labelMarker = new AMap.LabelMarker(curData)
+
+      // markers.push(labelMarker);
+
+      layer.add(labelMarker)
+    }
+
+    return layer
+  }
+}
diff --git a/src/utils/time-util.js b/src/utils/time-util.js
new file mode 100644
index 0000000..951116b
--- /dev/null
+++ b/src/utils/time-util.js
@@ -0,0 +1,39 @@
+import dayjs from "dayjs";
+
+export default {
+  format(date, template) {
+    return dayjs(date).format(template)
+  },
+
+  formatH(date){
+    if (date) {
+      return this.format(date, 'HH:mm:ss')
+    } else {
+      return '--:--:--'
+    }
+  },
+
+  formatYM(date){
+    if (date) {
+      return this.format(date, 'YYYY-MM')
+    } else {
+      return '----/--'
+    }
+  },
+
+  formatYMD(date){
+    if (date) {
+      return this.format(date, 'YYYY-MM-DD')
+    } else {
+      return '----/--/--'
+    }
+  },
+
+  formatYMDH(date){
+    if (date) {
+      return this.format(date, 'YYYY-MM-DD HH:mm:ss')
+    } else {
+      return '----/--/-- --:--:--'
+    }
+  },
+}
\ No newline at end of file
diff --git a/src/views/inspection/InspectionView.vue b/src/views/inspection/InspectionView.vue
index d6f44b8..a42ca35 100644
--- a/src/views/inspection/InspectionView.vue
+++ b/src/views/inspection/InspectionView.vue
@@ -6,6 +6,9 @@
 </template>
 
 <script setup>
+/**
+ * 鐜板満宸℃煡瀹炴椂璺熻釜
+ */
 import TaskTrack from '@/views/inspection/TaskTrack.vue'
 </script>
 
diff --git a/src/views/inspection/JointEnforcement.vue b/src/views/inspection/JointEnforcement.vue
new file mode 100644
index 0000000..f722836
--- /dev/null
+++ b/src/views/inspection/JointEnforcement.vue
@@ -0,0 +1,20 @@
+<template>
+  <div class="border-r-small" style="height: 200px">
+    <div class="f-l">鑱斿悎鎵ф硶</div>
+  </div>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      tasks: []
+    }
+  },
+  watch: {},
+  methods: {},
+  mounted() {}
+}
+</script>
+
+<style scoped></style>
diff --git a/src/views/inspection/SelfInspection.vue b/src/views/inspection/SelfInspection.vue
new file mode 100644
index 0000000..86e2f1a
--- /dev/null
+++ b/src/views/inspection/SelfInspection.vue
@@ -0,0 +1,20 @@
+<template>
+  <div class="border-r-small" style="height: 200px">
+    <div class="f-l">搴旀�ヨ嚜宸℃煡</div>
+  </div>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      tasks: []
+    }
+  },
+  watch: {},
+  methods: {},
+  mounted() {}
+}
+</script>
+
+<style scoped></style>
diff --git a/src/views/inspection/TaskTrack.vue b/src/views/inspection/TaskTrack.vue
index 97097a3..e0e6cf2 100644
--- a/src/views/inspection/TaskTrack.vue
+++ b/src/views/inspection/TaskTrack.vue
@@ -4,27 +4,52 @@
       <!-- <div class="f-l">鐜板満宸℃煡璺熻釜锛堢幇鍦轰汉鍛樺贰鏌ユ儏鍐靛疄鏃舵帉鎻★級</div> -->
       <!-- <SubtaskItem v-for="item in subtaskList" :key="item.guid" v-bind="item"> </SubtaskItem> -->
       <el-col :span="4">
-        <TaskNode title="鏂颁换鍔�" :items="subtaskList" v-slot="{ item, index }">
+        <TaskNode
+          title="鏂颁换鍔�"
+          :items="subtaskList"
+          warning="璀﹀憡锛氭湁鍘嗗彶鏈畬鎴愪换鍔�"
+          v-slot="{ item, index }"
+        >
           <SubtaskItem v-bind="item" :index="index"> </SubtaskItem>
         </TaskNode>
       </el-col>
       <el-col :span="4">
-        <TaskNode title="姝e湪鎵ц" :items="subtaskList" v-slot="{ item, index }">
+        <TaskNode
+          title="姝e湪鎵ц"
+          :items="subtaskList"
+          warning="璀﹀憡锛氭湁鎵ц鏃堕棿杩囬暱鐨勪换鍔�"
+          v-slot="{ item, index }"
+        >
           <SubtaskItem v-bind="item" :index="index"> </SubtaskItem>
         </TaskNode>
       </el-col>
       <el-col :span="4">
-        <TaskNode title="寰呭鏍�" :items="subtaskList" v-slot="{ item, index }">
+        <TaskNode
+          title="寰呭鏍�"
+          :items="subtaskList"
+          warning="璀﹀憡锛氭湁闀挎椂闂存湭瀹℃牳闂"
+          v-slot="{ item, index }"
+        >
           <SubtaskExamineItem v-bind="item" :index="index" type="闂瀹℃牳"> </SubtaskExamineItem>
         </TaskNode>
       </el-col>
       <el-col :span="4">
-        <TaskNode title="寰呮暣鏀�" :items="subtaskList" v-slot="{ item, index }">
+        <TaskNode
+          title="寰呮暣鏀�"
+          :items="subtaskList"
+          warning="璀﹀憡锛氭湁闀挎椂闂存湭鏁存敼闂"
+          v-slot="{ item, index }"
+        >
           <SubtaskExamineItem v-bind="item" :index="index" type="寰呮暣鏀�"> </SubtaskExamineItem>
         </TaskNode>
       </el-col>
       <el-col :span="4">
-        <TaskNode title="寰呯‘璁�" :items="subtaskList" v-slot="{ item, index }">
+        <TaskNode
+          title="寰呯‘璁�"
+          :items="subtaskList"
+          warning="璀﹀憡锛氭湁闀挎椂闂存湭瀹℃牳鏁存敼"
+          v-slot="{ item, index }"
+        >
           <SubtaskExamineItem v-bind="item" :index="index" type="鏁存敼瀹℃牳"> </SubtaskExamineItem>
         </TaskNode>
       </el-col>
diff --git a/src/views/main/MonitorView.vue b/src/views/main/MonitorView.vue
index 5f35cd8..4a88dca 100644
--- a/src/views/main/MonitorView.vue
+++ b/src/views/main/MonitorView.vue
@@ -20,16 +20,20 @@
 import InspectionView from '@/views/inspection/InspectionView.vue'
 import ManagementView from '@/views/management/ManagementView.vue'
 import VisualizationView from '@/views/visualization/VisualizationView.vue'
+
+import { provide } from 'vue'
+
+provide('mapHeight', 'calc(var(--fy-body-height) / 3 * 2)')
 </script>
 
 <style scoped>
 .page-left-top {
-  height: calc(var(--fy-body-height) / 2);
-  background-color: aquamarine;
+  height: calc(var(--fy-body-height) / 3 * 2);
+  /* background-color: aquamarine; */
 }
 .page-left-bottom {
-  height: calc(var(--fy-body-height) / 2);
-  background-color: bisque;
+  height: calc(var(--fy-body-height) / 3 * 1);
+  /* background-color: bisque; */
 }
 
 .page-right {
diff --git a/src/views/management/ManagementView.vue b/src/views/management/ManagementView.vue
index 681eeb0..9473a84 100644
--- a/src/views/management/ManagementView.vue
+++ b/src/views/management/ManagementView.vue
@@ -1,10 +1,12 @@
 <template>
   <el-row> 缁熻绠$悊 </el-row>
   <TaskStats></TaskStats>
+  <TaskSummary></TaskSummary>
 </template>
 
 <script setup>
 import TaskStats from '@/views/management/TaskStats.vue'
+import TaskSummary from '@/views/management/TaskSummary.vue'
 </script>
 
 <style scoped></style>
diff --git a/src/views/management/TaskStats.vue b/src/views/management/TaskStats.vue
index 509ba31..019e240 100644
--- a/src/views/management/TaskStats.vue
+++ b/src/views/management/TaskStats.vue
@@ -4,11 +4,26 @@
     <el-row>
       <TaskItem v-for="item in tasks" :key="item.guid" v-bind="item"></TaskItem>
     </el-row>
+    <el-row>
+      <el-col :span="12">
+        <SelfInspection></SelfInspection>
+      </el-col>
+      <el-col :span="12">
+        <JointEnforcement></JointEnforcement>
+      </el-col>
+    </el-row>
   </div>
 </template>
 
 <script>
+import SelfInspection from '@/views/inspection/SelfInspection.vue'
+import JointEnforcement from '@/views/inspection/JointEnforcement.vue'
+
+/**
+ * 浠诲姟瀹屾垚鎯呭喌
+ */
 export default {
+  components: { SelfInspection, JointEnforcement },
   data() {
     return {
       tasks: []
diff --git a/src/views/management/TaskSummary.vue b/src/views/management/TaskSummary.vue
new file mode 100644
index 0000000..4bc0909
--- /dev/null
+++ b/src/views/management/TaskSummary.vue
@@ -0,0 +1,26 @@
+<template>
+  <el-row> 宸℃煡姹囨�� </el-row>
+  <el-row>
+    <el-col :span="8">
+      <TaskSummaryItem title="浠婃棩姹囨��"></TaskSummaryItem>
+    </el-col>
+    <el-col :span="8">
+      <TaskSummaryItem title="鍛ㄥ害姹囨��"></TaskSummaryItem>
+    </el-col>
+    <el-col :span="8">
+      <TaskSummaryItem title="鏈堝害姹囨��"></TaskSummaryItem>
+    </el-col>
+  </el-row>
+  <el-row>
+    <el-col :span="12">
+      <TaskSummaryItem title="瀛e害姹囨��"></TaskSummaryItem>
+    </el-col>
+    <el-col :span="12">
+      <TaskSummaryItem title="骞村害姹囨��"></TaskSummaryItem>
+    </el-col>
+  </el-row>
+</template>
+
+<script setup></script>
+
+<style scoped></style>
diff --git a/src/views/visualization/SubtaskVisual.vue b/src/views/visualization/SubtaskVisual.vue
new file mode 100644
index 0000000..591758d
--- /dev/null
+++ b/src/views/visualization/SubtaskVisual.vue
@@ -0,0 +1,95 @@
+<template>
+  <el-scrollbar :height="mapHeight">
+    <el-card class="p-events-auto wrapper">
+      <div>{{ subtask.name }}</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>
+    </el-card>
+  </el-scrollbar>
+</template>
+
+<script>
+import { inject } from 'vue'
+/**
+ * 鍏蜂綋宸℃煡浠诲姟鍙鍖�
+ * 鍖呮嫭鍦板浘瀹氫綅淇℃伅灞曠ず銆佸贰鏌ヤ换鍔″叏娴佺▼骞抽摵灞曠ず
+ */
+export default {
+  setup() {
+    const mapHeight = inject('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
+        }
+      ]
+    }
+  }
+}
+</script>
+
+<style scoped>
+.wrapper {
+  /* position: absolute; */
+  top: 0;
+  right: 0;
+  /* background-color: wheat; */
+}
+</style>
diff --git a/src/views/visualization/SupervisionVisual.vue b/src/views/visualization/SupervisionVisual.vue
new file mode 100644
index 0000000..d362111
--- /dev/null
+++ b/src/views/visualization/SupervisionVisual.vue
@@ -0,0 +1,70 @@
+<template>
+  <el-row>
+    <div class="p-events-auto">
+      <OptionLocation :level="3" :width="170" v-model="locations"></OptionLocation>
+      <OptionSceneType :type="2" :width="120" v-model="sceneType"></OptionSceneType>
+      <OptionTime v-model="time"></OptionTime>
+    </div>
+  </el-row>
+</template>
+
+<script>
+import { inject } from 'vue'
+import taskApi from '@/api/fysp/taskApi.js'
+import marks from '@/utils/map/marks.js'
+import scene_1 from '@/assets/icon/scene_1.png'
+/**
+ * 鐩戠鍙鍖�
+ * 灞曠幇鍖哄煙鏁翠綋鐩戠鐘舵��
+ */
+export default {
+  setup() {
+    const mapHeight = inject('mapHeight')
+
+    const height = 'height:' + mapHeight
+    return { height, mapHeight }
+  },
+  props: {},
+  data() {
+    return {
+      locations: {},
+      sceneType: {},
+      time: ''
+    }
+  },
+  computed: {
+    area() {
+      return {
+        provincecode: this.locations.pCode,
+        provincename: this.locations.pName,
+        citycode: this.locations.cCode,
+        cityname: this.locations.cName,
+        districtcode: this.locations.dCode,
+        districtname: this.locations.dName,
+        starttime: this.$fm.formatYMDH(this.time),
+        scensetypeid: this.sceneType.value
+      }
+    }
+  },
+  methods: {
+    // 鏌ヨ
+    fetchSubtaskSummaryArea() {
+      return taskApi.fetchSubtaskSummaryArea(this.area).then((res) => {
+        let list = []
+        res.data.forEach((e) => {
+          list = list.concat(e.subTaskSummary)
+        })
+        this.newLabelMasks(list)
+      })
+    },
+    newLabelMasks(data) {
+      marks.createLabelMarks(scene_1, data)
+    }
+  },
+  mounted() {
+    this.fetchSubtaskSummaryArea()
+  }
+}
+</script>
+
+<style scoped></style>
diff --git a/src/views/visualization/VisualizationView.vue b/src/views/visualization/VisualizationView.vue
index 920b0aa..89942b4 100644
--- a/src/views/visualization/VisualizationView.vue
+++ b/src/views/visualization/VisualizationView.vue
@@ -1,11 +1,35 @@
 <template>
-  <el-row class="wrapper"> 鍙鍖� </el-row>
+  <!-- <el-row class="wrapper"> 鍙鍖� </el-row> -->
+  <BaseMap></BaseMap>
+  <el-row class="overlay-container" :style="height">
+    <el-col :span="18">
+      <SupervisionVisual></SupervisionVisual>
+    </el-col>
+    <el-col :span="6">
+      <SubtaskVisual></SubtaskVisual>
+    </el-col>
+  </el-row>
 </template>
 
-<script setup></script>
+<script setup>
+import SubtaskVisual from './SubtaskVisual.vue'
+import SupervisionVisual from './SupervisionVisual.vue'
+import { inject } from 'vue'
+
+const mapHeight = inject('mapHeight')
+
+const height = 'height:' + mapHeight
+</script>
 
 <style scoped>
-.wrapper {
-  /* background-color: aquamarine; */
+.overlay-container {
+  /* background: aliceblue; */
+  position: absolute;
+  width: 100%;
+  /* height: 100vh; */
+  top: 0;
+  left: 0;
+  /* padding: 4px; */
+  pointer-events: none;
 }
 </style>

--
Gitblit v1.9.3