From d22ce1ad1c4656f5c2212bbabb35ba498300aced Mon Sep 17 00:00:00 2001
From: riku <risaku@163.com>
Date: 星期四, 20 七月 2023 17:12:20 +0800
Subject: [PATCH] 线索下发及提交结论和问题模块基本完成

---
 src/views/overlay-clue/components/QuestionDetail.vue       |  264 ++++++++++
 src/views/overlay-clue/components/ClueReportQuestion.vue   |  139 +++++
 src/model/clueQuestion.js                                  |   16 
 src/api/clue/clueConclusionApi.js                          |   13 
 src/components.d.ts                                        |    9 
 src/views/HomePage.vue                                     |    4 
 src/composables/formConfirm.js                             |    7 
 src/views/overlay-clue/components/ClueList.vue             |   15 
 src/components/map/baseMap.js                              |   36 
 src/api/clue/clueQuestionApi.js                            |   31 +
 src/components/map/BaseMap.vue                             |    7 
 index.html                                                 |    2 
 src/components/map/baseMapUtil.js                          |  120 ++++
 src/assets/text.css                                        |   25 +
 src/api/clue/clueApi.js                                    |   13 
 src/api/clue/index.js                                      |   28 
 src/assets/main.css                                        |    5 
 src/assets/border.css                                      |    4 
 src/constant/street.js                                     |   16 
 README.md                                                  |    2 
 src/components/map/MapSearch.vue                           |  221 ++++++++
 src/views/overlay-clue/components/ClueReportConclusion.vue |  193 +++++++
 src/assets/base.css                                        |    1 
 src/views/overlay-clue/components/ClueReport.vue           |  280 ++--------
 src/views/overlay-clue/ClueLayout.vue                      |   38 +
 25 files changed, 1,199 insertions(+), 290 deletions(-)

diff --git a/README.md b/README.md
index 1120bea..0ad3379 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# ff-ai-ep-underway-vue
+# ff-ai-ep-underway-vuegit
 
 This template should help get you started developing with Vue 3 in Vite.
 
diff --git a/index.html b/index.html
index 99f583a..a1db4b4 100644
--- a/index.html
+++ b/index.html
@@ -4,7 +4,7 @@
     <meta charset="UTF-8">
     <link rel="icon" href="/favicon.ico">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>Vite App</title>
+    <title>缃戞牸涓�浣撳寲</title>
   </head>
   <body>
     <div id="app"></div>
diff --git a/src/api/clue/clueApi.js b/src/api/clue/clueApi.js
index 2afc729..0ce8f48 100644
--- a/src/api/clue/clueApi.js
+++ b/src/api/clue/clueApi.js
@@ -2,18 +2,21 @@
 
 export default {
   /**
-   * 鏌ヨ宸蹭笅鍙戠殑绾跨储娓呭崟
+   * 鏌ヨ绾跨储娓呭崟
+   * @param {object} param0 
+   * @returns 
    */
-  getClues(time) {
-    return $clue.get(`feedback/queryYxfList?updateTime=${time}`);
-  },
-
   getClue({ sTime, eTime, pageNum = 1, pageSize = 30 }) {
     return $clue.get(
       `clue/fetch?sTime=${sTime}&eTime=${eTime}&pageNum=${pageNum}&pageSize=${pageSize}`
     );
   },
 
+  /**
+   * 浠庣涓夋柟杩滅▼鎷夊彇绾跨储娓呭崟
+   * @param {string} updateTime 鏇存柊鏃堕棿锛岃幏鍙栬鏃堕棿涔嬪悗鐨勭嚎绱�
+   * @returns 
+   */
   fetchRemoteClue(updateTime) {
     return $clue.get(`clue/fetch/remote?updateTime=${updateTime}`);
   }
diff --git a/src/api/clue/clueConclusionApi.js b/src/api/clue/clueConclusionApi.js
index eef1344..ede0d44 100644
--- a/src/api/clue/clueConclusionApi.js
+++ b/src/api/clue/clueConclusionApi.js
@@ -2,16 +2,27 @@
 
 export default {
   /**
-   *
+   * 鑾峰彇绾跨储缁撹
+   * @param {string} clueId 绾跨储id
    */
   getConclusion(clueId) {
     return $clue.get(`clue/conclusion/fetch?clueId=${clueId}`);
   },
 
+  /**
+   * 鎻愪氦绾跨储缁撹
+   * @param {object} conclusion 绾跨储
+   * @returns 
+   */
   uploadConclusion(conclusion) {
     return $clue.post(`clue/conclusion/upload`, conclusion);
   },
 
+  /**
+   * 鎺ㄩ�佺嚎绱㈢粨璁鸿嚦绗笁鏂�
+   * @param {Array} conclusionIdList 绾跨储id闆嗗悎
+   * @returns 
+   */
   pushConclusion(conclusionIdList) {
     return $clue.post(`clue/conclusion/push`, conclusionIdList);
   }
diff --git a/src/api/clue/clueQuestionApi.js b/src/api/clue/clueQuestionApi.js
index 8b2ba10..4f820d5 100644
--- a/src/api/clue/clueQuestionApi.js
+++ b/src/api/clue/clueQuestionApi.js
@@ -1,18 +1,43 @@
 import { $clue } from './index';
+import { getClueQuestionList } from '@/model/clueQuestion';
 
 export default {
   /**
-   *
+   * 鑾峰彇宸叉彁浜ょ殑绾跨储闂
+   * @param {string} clueId 绾跨储id
    */
   getQuestion(clueId) {
-    return $clue.get(`clue/question/fetch?clueId=${clueId}`);
+    return $clue
+      .get(`clue/question/fetch?clueId=${clueId}`)
+      .then((res) => {
+        return getClueQuestionList(res);
+      });
   },
 
+  /**
+   * 涓婁紶绾跨储闂
+   * @param {object} question 闂鎻忚堪
+   * @param {*} files 闂鍥剧墖
+   * @returns
+   */
   uploadQuestion(question, files) {
-    const formData = {};
+    const formData = new FormData();
+    formData.append('question', JSON.stringify(question));
+    files.forEach((e) => {
+      formData.append('images', e);
+    });
     return $clue.post(`clue/question/upload`, formData);
   },
 
+  uploadQuestionUrl() {
+    return `${$clue.defaults.baseURL}clue/question/upload`;
+  },
+
+  /**
+   * 鎺ㄩ�佺嚎绱㈤棶棰樿嚦绗笁鏂�
+   * @param {Array} questionIdList 闂id闆嗗悎
+   * @returns
+   */
   pushQuestion(questionIdList) {
     return $clue.post(`clue/question/push`, questionIdList);
   }
diff --git a/src/api/clue/index.js b/src/api/clue/index.js
index 496fc47..678789c 100644
--- a/src/api/clue/index.js
+++ b/src/api/clue/index.js
@@ -2,17 +2,19 @@
 import md5 from 'md5';
 import { ElMessage } from 'element-plus';
 
-// const ip = 'http://101.230.224.80:8082/';
-const ip = 'http://192.168.0.138:8080/';
+const url = 'http://47.100.191.150:9030/';
+// const url = 'http://192.168.1.9:8080/';
+const imgUrl = url + 'images/';
 
 //椋炵窘鐩戠
 const $clue = axios.create({
-  baseURL: ip,
-  timeout: 10000,
+  baseURL: url,
+  timeout: 10000
   // headers: addHeaders()
 });
+// console.log($clue);
 
-function addHeaders(headers) {
+function getHeaders() {
   const token = 'e6dc8bb9e1ff0ce973fb92b4af2e4c3f';
 
   const date = new Date();
@@ -67,15 +69,17 @@
       console.log('==>璇锋眰缁撴潫');
       if (response.status == 200) {
         if (
-          response.data.code != undefined &&
-          response.data.code != null
+          response.data.success != undefined &&
+          response.data.success != null
         ) {
-          if (response.data.code == 20000) {
+          if (response.data.success == true) {
             return response.data.data;
           } else {
-            return Promise.reject(
-              response.data.code + ', ' + response.data.message
-            );
+            ElMessage({
+              message: response.data.message,
+              type: 'error'
+            });
+            return Promise.reject(response.data.message);
           }
         } else {
           return response;
@@ -98,4 +102,4 @@
   );
 });
 
-export { $clue };
+export { $clue, imgUrl };
diff --git a/src/assets/base.css b/src/assets/base.css
index e34188a..b15a559 100644
--- a/src/assets/base.css
+++ b/src/assets/base.css
@@ -63,6 +63,7 @@
   --screen-min-height: 900px;
   min-height: var(--screen-min-height);
   min-width: var(--screen-min-width);
+  /* overflow: scroll; */
   color: var(--color-text);
   background: var(--color-background);
   transition: color 0.5s, background-color 0.5s;
diff --git a/src/assets/border.css b/src/assets/border.css
new file mode 100644
index 0000000..adea27b
--- /dev/null
+++ b/src/assets/border.css
@@ -0,0 +1,4 @@
+.fy-dashed-border {
+  border: 1px dashed var(--el-border-color);
+  border-radius: var(--el-border-radius-base);
+}
\ No newline at end of file
diff --git a/src/assets/main.css b/src/assets/main.css
index f1e87e4..781f450 100644
--- a/src/assets/main.css
+++ b/src/assets/main.css
@@ -1,8 +1,9 @@
 @import './base.css';
+@import './border.css';
+@import './text.css';
 
 #app {
   margin: 0 auto;
-
   font-weight: normal;
 }
 
@@ -30,4 +31,4 @@
     grid-template-columns: 1fr 1fr;
     padding: 0 2rem;
   } */
-}
+}
\ No newline at end of file
diff --git a/src/assets/text.css b/src/assets/text.css
new file mode 100644
index 0000000..1dd790d
--- /dev/null
+++ b/src/assets/text.css
@@ -0,0 +1,25 @@
+.fy-h1 {
+  padding: 8px 8px 8px 8px;
+  font-size: var(--el-font-size-large);
+}
+
+.fy-h2 {
+  padding: 16px 0 8px 0;
+  font-size: var(--el-font-size-medium);
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.fy-p1 {
+
+}
+
+.fy-p2 {
+  
+}
+
+.fy-tip-red {
+  font-size: var(--el-font-size-small);
+  color: var(--el-color-danger);
+}
\ No newline at end of file
diff --git a/src/components.d.ts b/src/components.d.ts
index 5be1cc6..0499a39 100644
--- a/src/components.d.ts
+++ b/src/components.d.ts
@@ -9,15 +9,15 @@
   export interface GlobalComponents {
     BaseMap: typeof import('./components/map/BaseMap.vue')['default']
     ElButton: typeof import('element-plus/es')['ElButton']
+    ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
     ElCol: typeof import('element-plus/es')['ElCol']
     ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
     ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
     ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
     ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
-    ElDescrrptions: typeof import('element-plus/es')['ElDescrrptions']
-    ElDescrrptionsItem: typeof import('element-plus/es')['ElDescrrptionsItem']
-    ElDiag: typeof import('element-plus/es')['ElDiag']
     ElDialog: typeof import('element-plus/es')['ElDialog']
+    ElDivider: typeof import('element-plus/es')['ElDivider']
+    ElEmpty: typeof import('element-plus/es')['ElEmpty']
     ElForm: typeof import('element-plus/es')['ElForm']
     ElFormItem: typeof import('element-plus/es')['ElFormItem']
     ElIcon: typeof import('element-plus/es')['ElIcon']
@@ -28,7 +28,8 @@
     ElRow: typeof import('element-plus/es')['ElRow']
     ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
     ElSelect: typeof import('element-plus/es')['ElSelect']
-    ElTag: typeof import('element-plus/es')['ElTag']
+    ElUpload: typeof import('element-plus/es')['ElUpload']
+    MapSearch: typeof import('./components/map/MapSearch.vue')['default']
     OptionTime: typeof import('./components/search-option/OptionTime.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
diff --git a/src/components/map/BaseMap.vue b/src/components/map/BaseMap.vue
index 9bc1cd2..75b2527 100644
--- a/src/components/map/BaseMap.vue
+++ b/src/components/map/BaseMap.vue
@@ -4,7 +4,6 @@
 
 <script setup>
 import { onMounted } from 'vue';
-import { map, AMap } from './baseMap';
 
 // window._AMapSecurityConfig = {
 //   securityJsCode: '銆屾偍鐢宠鐨勫畨鍏ㄥ瘑閽ャ��'
@@ -17,13 +16,13 @@
   // map.add(marker); //娣诲姞鍒板湴鍥�
 });
 </script>
-<style>
+<style scoped>
 #container {
   position: relative;
   width: 100%;
   height: 100vh;
-  min-height: 900px;
-  min-width: 1440px;
+  min-height: var(--screen-min-height);
+  min-width: var(--screen-min-width);
   z-index: 0px;
 }
 </style>
diff --git a/src/components/map/MapSearch.vue b/src/components/map/MapSearch.vue
new file mode 100644
index 0000000..909e355
--- /dev/null
+++ b/src/components/map/MapSearch.vue
@@ -0,0 +1,221 @@
+<template>
+  <el-dialog v-model="dialogShow" width="70%" destroy-on-close>
+    <template #header>
+      <div> 鍧愭爣鎷惧彇</div>
+    </template>
+    <div class="fy-tip-red">宸﹂敭鐐瑰嚮鍦板浘閫夊彇鍧愭爣鐐癸紝鎴栬�呮牴鎹叧閿瓧鎼滅储鍦扮偣</div>
+    <el-row>
+      <el-col :span="10">
+        <el-form
+          :inline="true"
+          label-width="50px"
+          label-position="left"
+          :model="formObj"
+          ref="formRef"
+          destroy-on-close
+        >
+          <el-form-item label="鍦板潃" prop="address">
+            <el-input
+              v-model="formObj.address"
+              placeholder="璇疯緭鍏ュ湴鍧�鎼滅储"
+            ></el-input>
+          </el-form-item>
+          <!-- <el-form-item label="缁忓害" prop="lon">
+        <el-input
+          v-model="formObj.lon"
+          style="width: 100px"
+        ></el-input>
+      </el-form-item>
+      <el-form-item label="绾害" prop="lat">
+        <el-input
+          v-model="formObj.lat"
+          style="width: 100px"
+        ></el-input>
+      </el-form-item> -->
+          <el-form-item>
+            <el-button type="primary" @click="searchKeyword"
+              >鎼滅储</el-button
+            >
+          </el-form-item>
+        </el-form>
+      </el-col>
+      <el-col :span="12">
+        <div v-if="searchResult.address">
+          <span>閫夋嫨鍦板潃锛�</span>
+          <span>{{ searchResult.address }}</span>
+          <div>
+            <span>{{
+              searchResult.lon + ', ' + searchResult.lat
+            }}</span>
+            <el-divider direction="vertical" />
+            <span>{{
+              searchResult.gpsLon + ', ' + searchResult.gpsLat
+            }}</span>
+          </div>
+        </div>
+      </el-col>
+      <el-col :span="2">
+        <el-button
+          :disabled="searchResult.gpsLon == undefined"
+          type="success"
+          @click="submit"
+          >閫夋嫨</el-button
+        >
+      </el-col>
+    </el-row>
+    <div id="mapContainer"></div>
+  </el-dialog>
+</template>
+
+<script>
+import { shallowRef } from 'vue';
+import AMapLoader from '@amap/amap-jsapi-loader';
+// import { AMap, onMapMounted } from './baseMap';
+import baseMapUtil from './baseMapUtil.js';
+
+// var map;
+var AMap;
+var geocoder;
+var inited = false;
+// onMapMounted(() => {
+//   AMap.plugin('AMap.Geocoder', function () {
+//     geocoder = new AMap.Geocoder({
+//       city: '涓婃捣' // city 鎸囧畾杩涜缂栫爜鏌ヨ鐨勫煄甯傦紝鏀寔浼犲叆鍩庡競鍚嶃�乤dcode 鍜� citycode
+//     });
+
+//     // 浣跨敤geocoder鍋氬湴鐞�/閫嗗湴鐞嗙紪鐮�
+//   });
+// });
+export default {
+  setup() {
+    const map = shallowRef(null);
+    return {
+      map
+    };
+  },
+  props: {
+    show: Boolean
+  },
+  data() {
+    return {
+      dialogShow: false,
+      formObj: {},
+      searchResult: {}
+    };
+  },
+  emits: ['update:show', 'onSubmit'],
+  watch: {
+    show(val) {
+      this.dialogShow = val;
+    },
+    dialogShow(val) {
+      if (val) {
+        this.mapInit();
+      } else {
+        this.formObj = {};
+        this.searchResult = {};
+        this.map.destroy();
+      }
+      this.$emit('update:show', val);
+    }
+  },
+  methods: {
+    mapInit() {
+      // if (!inited) {
+      AMapLoader.load({
+        key: 'c55f27799afbfa69dc5a3fad90cafe51', // 鐢宠濂界殑Web绔紑鍙戣�匥ey锛岄娆¤皟鐢� load 鏃跺繀濉�
+        version: '2.0', // 鎸囧畾瑕佸姞杞界殑 JS API 鐨勭増鏈紝缂虹渷鏃堕粯璁や负 1.4.15
+        plugins: ['AMap.Geocoder'] // 闇�瑕佷娇鐢ㄧ殑鐨勬彃浠跺垪琛紝濡傛瘮渚嬪昂'AMap.Scale'绛�
+      }).then((_AMap) => {
+        AMap = _AMap;
+        this.map = new AMap.Map('mapContainer', {
+          rotateEnable: true,
+          pitchEnable: true,
+          alwaysRender: false,
+          showLabel: true,
+          showBuildingBlock: true,
+          // mapStyle: 'amap://styles/e1e78509de64ddcd2efb4cb34c6fae2a',
+          // features: ['bg', 'road'],
+          pitch: 0, // 鍦板浘淇话瑙掑害锛屾湁鏁堣寖鍥� 0 搴�- 83 搴�
+          viewMode: '2D', // 鍦板浘妯″紡
+          resizeEnable: true,
+          center: [121.6039283, 31.25295567],
+          zooms: [3, 18],
+          zoom: 14
+        });
+        geocoder = new AMap.Geocoder({
+          city: '涓婃捣' // city 鎸囧畾杩涜缂栫爜鏌ヨ鐨勫煄甯傦紝鏀寔浼犲叆鍩庡競鍚嶃�乤dcode 鍜� citycode
+        });
+        this.map.on('click', (ev) => {
+          // this.formObj.lon = ev.lnglat.getLng();
+          // this.formObj.lat = ev.lnglat.getLat();
+          this.map.clearMap();
+          const marker = new AMap.Marker({
+            position: ev.lnglat
+          });
+          this.map.add(marker);
+
+          geocoder.getAddress(ev.lnglat, (status, result) => {
+            if (status === 'complete' && result.info === 'OK') {
+              this.searchResult.address =
+                result.regeocode.formattedAddress;
+              this.searchResult.lon = ev.lnglat.getLng();
+              this.searchResult.lat = ev.lnglat.getLat();
+              const [gpsLon, gpsLat] = baseMapUtil.gcj02towgs84(
+                this.searchResult.lon,
+                this.searchResult.lat
+              );
+              this.searchResult.gpsLon = gpsLon;
+              this.searchResult.gpsLat = gpsLat;
+            }
+          });
+        });
+      });
+      // inited = true;
+      // }
+    },
+    searchKeyword() {
+      const keyWord = this.formObj.address;
+      this.map.clearMap();
+      geocoder.getLocation(keyWord, (status, result) => {
+        if (status === 'complete' && result.info === 'OK') {
+          const geocode = result.geocodes[0];
+          this.searchResult.address = geocode.formattedAddress;
+
+          this.searchResult.lon = geocode.location.getLng();
+          this.searchResult.lat = geocode.location.getLat();
+          const [gpsLon, gpsLat] = baseMapUtil.gcj02towgs84(
+            this.searchResult.lon,
+            this.searchResult.lat
+          );
+          this.searchResult.gpsLon = gpsLon;
+          this.searchResult.gpsLat = gpsLat;
+
+          const marker = new AMap.Marker({
+            position: geocode.location
+          });
+          this.map.add(marker);
+          this.map.setFitView(marker);
+        }
+      });
+    },
+    submit() {
+      this.$emit('onSubmit', this.searchResult);
+      this.dialogShow = false;
+    }
+  }
+  // updated() {
+  //   this.mapInit();
+  // }
+};
+</script>
+<style>
+#mapContainer {
+  position: relative;
+  width: 100%;
+  height: 60vh;
+  z-index: 0px;
+  border-radius: var(--el-border-radius-round);
+  box-shadow: var(--el-box-shadow);
+}
+</style>
diff --git a/src/components/map/baseMap.js b/src/components/map/baseMap.js
index 67bcdd7..8e433f4 100644
--- a/src/components/map/baseMap.js
+++ b/src/components/map/baseMap.js
@@ -73,25 +73,25 @@
   satellite.hide();
   map.add([satellite]);
 
-  const rPx = 100;
-  const tPx = 110;
+  // const rPx = 100;
+  // const tPx = 110;
   // 娣诲姞鍦板浘鎺у埗宸ュ叿
-  map.addControl(
-    new AMap.ControlBar({
-      position: {
-        right: rPx + 'px',
-        top: tPx + 'px'
-      }
-    })
-  );
-  map.addControl(
-    new AMap.ToolBar({
-      position: {
-        right: rPx + 30 + 'px',
-        top: tPx + 90 + 'px'
-      }
-    })
-  );
+  // map.addControl(
+  //   new AMap.ControlBar({
+  //     position: {
+  //       right: rPx + 'px',
+  //       top: tPx + 'px'
+  //     }
+  //   })
+  // );
+  // map.addControl(
+  //   new AMap.ToolBar({
+  //     position: {
+  //       right: rPx + 30 + 'px',
+  //       top: tPx + 90 + 'px'
+  //     }
+  //   })
+  // );
 
   //
   mouseTool = new AMap.MouseTool(map);
diff --git a/src/components/map/baseMapUtil.js b/src/components/map/baseMapUtil.js
index a152876..735b58e 100644
--- a/src/components/map/baseMapUtil.js
+++ b/src/components/map/baseMapUtil.js
@@ -1,6 +1,74 @@
 import { map, AMap } from './baseMap';
 import { toRaw } from 'vue';
 
+//瀹氫箟涓�浜涘父閲�
+const PI = 3.1415926535897932384626;
+const a = 6378245.0; //闀垮崐杞�
+const ee = 0.00669342162296594323; //鎵佺巼/*** GCJ02 杞崲涓� WGS84* @param lng* @param lat* @returns {*[]}*/
+
+function transformlat(lng, lat) {
+  // lat = +lat lng = +lng
+  let ret =
+    -100.0 +
+    2.0 * lng +
+    3.0 * lat +
+    0.2 * lat * lat +
+    0.1 * lng * lat +
+    0.2 * Math.sqrt(Math.abs(lng));
+  ret +=
+    ((20.0 * Math.sin(6.0 * lng * PI) +
+      20.0 * Math.sin(2.0 * lng * PI)) *
+      2.0) /
+    3.0;
+  ret +=
+    ((20.0 * Math.sin(lat * PI) + 40.0 * Math.sin((lat / 3.0) * PI)) *
+      2.0) /
+    3.0;
+  ret +=
+    ((160.0 * Math.sin((lat / 12.0) * PI) +
+      320 * Math.sin((lat * PI) / 30.0)) *
+      2.0) /
+    3.0;
+  return ret;
+}
+
+function transformlng(lng, lat) {
+  // lat = +latlng = +lng
+  let ret =
+    300.0 +
+    lng +
+    2.0 * lat +
+    0.1 * lng * lng +
+    0.1 * lng * lat +
+    0.1 * Math.sqrt(Math.abs(lng));
+  ret +=
+    ((20.0 * Math.sin(6.0 * lng * PI) +
+      20.0 * Math.sin(2.0 * lng * PI)) *
+      2.0) /
+    3.0;
+  ret +=
+    ((20.0 * Math.sin(lng * PI) + 40.0 * Math.sin((lng / 3.0) * PI)) *
+      2.0) /
+    3.0;
+  ret +=
+    ((150.0 * Math.sin((lng / 12.0) * PI) +
+      300.0 * Math.sin((lng / 30.0) * PI)) *
+      2.0) /
+    3.0;
+  return ret;
+}
+
+/**
+ * 鍒ゆ柇鏄惁鍦ㄥ浗鍐咃紝涓嶅湪鍥藉唴鍒欎笉鍋氬亸绉�
+ * @param lng
+ * @param lat
+ * @returns {boolean}
+ */
+function out_of_china(lng, lat) {
+  // 绾害3.86~53.55,缁忓害73.66~135.05
+  return !(lng > 73.66 && lng < 135.05 && lat > 3.86 && lat < 53.55);
+}
+
 export default {
   /**
    * 灏嗘暟缁勮〃绀虹殑鍧愭爣鐐癸紝杞崲涓篈Map.LngLat瀵硅薄
@@ -45,4 +113,56 @@
     if (map == undefined) return;
     map.clearMap();
   },
+
+  /**
+   * 楂樺痉鍦板浘鍧愭爣杞珿PS鍧愭爣绠楁硶
+   */
+  gcj02towgs84(lng, lat) {
+    // lat = +latlng = +lng
+    if (out_of_china(lng, lat)) {
+      return [lng, lat];
+    } else {
+      let dlat = transformlat(lng - 105.0, lat - 35.0);
+      let dlng = transformlng(lng - 105.0, lat - 35.0);
+      let radlat = (lat / 180.0) * PI;
+      let magic = Math.sin(radlat);
+      magic = 1 - ee * magic * magic;
+      let sqrtmagic = Math.sqrt(magic);
+      dlat =
+        (dlat * 180.0) /
+        (((a * (1 - ee)) / (magic * sqrtmagic)) * PI);
+      dlng =
+        (dlng * 180.0) / ((a / sqrtmagic) * Math.cos(radlat) * PI);
+      let mglat = Math.round((lat * 2 - lat - dlat) * 1000000) / 1000000;
+      let mglng = Math.round((lng * 2 - lng - dlng) * 1000000) / 1000000;
+      return [mglng, mglat];
+    }
+  },
+  /**
+   * WGS84 杞崲涓� GCJ02
+   * @param lng
+   * @param lat
+   * @returns {*[]}
+   */
+  wgs84togcj02(lng, lat) {
+    // lat = +latlng = +lng
+    if (out_of_china(lng, lat)) {
+      return [lng, lat];
+    } else {
+      let dlat = transformlat(lng - 105.0, lat - 35.0);
+      let dlng = transformlng(lng - 105.0, lat - 35.0);
+      let radlat = (lat / 180.0) * PI;
+      let magic = Math.sin(radlat);
+      magic = 1 - ee * magic * magic;
+      let sqrtmagic = Math.sqrt(magic);
+      dlat =
+        (dlat * 180.0) /
+        (((a * (1 - ee)) / (magic * sqrtmagic)) * PI);
+      dlng =
+        (dlng * 180.0) / ((a / sqrtmagic) * Math.cos(radlat) * PI);
+      let mglat = Math.round((lat + dlat) * 1000000) / 1000000;
+      let mglng = Math.round((lng + dlng) * 1000000) / 1000000;
+      return [mglng, mglat];
+    }
+  }
 };
diff --git a/src/composables/formConfirm.js b/src/composables/formConfirm.js
index ef6a057..43b3d35 100644
--- a/src/composables/formConfirm.js
+++ b/src/composables/formConfirm.js
@@ -1,6 +1,6 @@
 // 琛ㄥ崟鐨勭‘璁ゅ拰鍙栨秷
 import { onActivated, onDeactivated, ref, watch } from 'vue';
-import { useCloned } from '@vueuse/core';
+// import { useCloned } from '@vueuse/core';
 import { useMessageBoxTip, useMessageBox } from './messageBox';
 
 export function useFormConfirm({
@@ -70,13 +70,14 @@
     //   manual: true
     // }).cloned.value;
     formRef.value.clearValidate();
+    formRef.value.resetFields();
   };
 
   // 娓呯┖琛ㄥ崟
   const clear = function () {
     edit.value = false;
     isReset = true;
-    formRef.value.resetFields();
+    formObj.value = {}
   };
 
   // 鎻愪氦鎴愬姛鍚�
@@ -141,5 +142,5 @@
     }
   };
 
-  return { formObj, formRef, edit, active, onSubmit, onCancel, onReset };
+  return { formObj, formRef, edit, active, onSubmit, onCancel, onReset, clear };
 }
diff --git a/src/constant/street.js b/src/constant/street.js
new file mode 100644
index 0000000..4742faf
--- /dev/null
+++ b/src/constant/street.js
@@ -0,0 +1,16 @@
+export const streets = [
+  { label: '澶у畞璺閬�', value: ['310106019', '澶у畞璺閬�'] },
+  { label: '褰郸鏂版潙琛楅亾', value: ['310106020', '褰郸鏂版潙琛楅亾'] },
+  { label: '涓存本璺閬�', value: ['310106021', '涓存本璺閬�'] },
+  { label: '鑺锋睙瑗胯矾琛楅亾', value: ['310106022', '鑺锋睙瑗胯矾琛楅亾'] },
+  { label: '褰郸闀�', value: ['310106101', '褰郸闀�'] },
+  { label: '姹熷畞璺閬�', value: ['310106006', '姹熷畞璺閬�'] },
+  { label: '鐭抽棬浜岃矾琛楅亾', value: ['310106011', '鐭抽棬浜岃矾琛楅亾'] },
+  { label: '鍗椾含瑗胯矾琛楅亾', value: ['310106012', '鍗椾含瑗胯矾琛楅亾'] },
+  { label: '闈欏畨瀵鸿閬�', value: ['310106013', '闈欏畨瀵鸿閬�'] },
+  { label: '鏇瑰娓¤閬�', value: ['310106014', '鏇瑰娓¤閬�'] },
+  { label: '澶╃洰瑗胯矾琛楅亾', value: ['310106015', '澶╃洰瑗胯矾琛楅亾'] },
+  { label: '鍖楃珯琛楅亾', value: ['310106016', '鍖楃珯琛楅亾'] },
+  { label: '瀹濆北璺閬�', value: ['310106017', '瀹濆北璺閬�'] },
+  { label: '鍏卞拰鏂拌矾琛楅亾', value: ['310106018', '鍏卞拰鏂拌矾琛楅亾'] }
+];
diff --git a/src/model/clueQuestion.js b/src/model/clueQuestion.js
new file mode 100644
index 0000000..dc7d77f
--- /dev/null
+++ b/src/model/clueQuestion.js
@@ -0,0 +1,16 @@
+import { imgUrl } from '@/api/clue/index';
+
+function getClueQuestion(data) {
+  data.cqFilePath = data.cqFilePath.split(';').map((val) => {
+    return imgUrl + val;
+  });
+  return data;
+}
+
+function getClueQuestionList(dataList) {
+  return dataList.map((v) => {
+    return getClueQuestion(v);
+  });
+}
+
+export { getClueQuestion, getClueQuestionList };
diff --git a/src/views/HomePage.vue b/src/views/HomePage.vue
index 35799d3..8445b25 100644
--- a/src/views/HomePage.vue
+++ b/src/views/HomePage.vue
@@ -17,7 +17,9 @@
 .overlay-container {
   background: transparent;
   position: absolute;
-  width: 100%;
+  min-height: var(--screen-min-height);
+  min-width: var(--screen-min-width);
+  width: 100vw;
   height: 100vh;
   top: 0;
   left: 0;
diff --git a/src/views/overlay-clue/ClueLayout.vue b/src/views/overlay-clue/ClueLayout.vue
index 774d7c1..b47e870 100644
--- a/src/views/overlay-clue/ClueLayout.vue
+++ b/src/views/overlay-clue/ClueLayout.vue
@@ -1,7 +1,7 @@
 <template>
-  <el-row class="container">
+  <el-row class="container" justify="space-between">
     <el-col :span="6" class="grid-content bg-content">
-      <div class="title">涓嬪彂绾跨储娓呭崟</div>
+      <div class="fy-h1">绾跨储娓呭崟</div>
       <div class="search-wrap">
         <span>鏃堕棿</span>
         <el-date-picker
@@ -10,6 +10,9 @@
           placeholder="閫夋嫨鏃ユ湡鍜屾椂闂�"
         />
         <el-button type="primary" @click="getClues">鏌ヨ</el-button>
+        <el-button type="primary" @click="fetchRemoteClue"
+          >鎷夊彇绾跨储</el-button
+        >
       </div>
       <ClueList
         :dataList="clueList"
@@ -17,7 +20,10 @@
       ></ClueList>
     </el-col>
     <el-col :span="6" class="grid-content bg-content-1">
-      <ClueReport></ClueReport>
+      <div class="fy-h1">绾跨储鍙嶉</div>
+      <el-scrollbar height="80vh" class="bg-fill">
+        <ClueReport :clueData="selectedClue"></ClueReport>
+      </el-scrollbar>
     </el-col>
   </el-row>
 </template>
@@ -35,6 +41,7 @@
 const updateTime = ref(new Date());
 // 绾跨储娓呭崟
 const clueList = ref([]);
+const selectedClue = ref();
 
 /**
  * 鏌ヨ宸蹭笅鍙戠殑绾跨储娓呭崟
@@ -42,7 +49,7 @@
 const getClues = function () {
   const now = moment(updateTime.value);
   const sTime = now.format('YYYY-MM-DD HH:mm:ss');
-  const eTime = now.add(1, 'month');
+  const eTime = now.add(1, 'month').format('YYYY-MM-DD HH:mm:ss');
   onMapMounted(() => {
     clueApi.getClue({ sTime, eTime }).then((res) => {
       clueList.value = res;
@@ -50,11 +57,20 @@
   });
 };
 
+function fetchRemoteClue() {
+  const time = moment(updateTime.value).format('YYYY-MM-DD HH:mm:ss');
+  onMapMounted(() => {
+    clueApi.fetchRemoteClue(time).then((res) => {
+      clueList.value = res;
+    });
+  });
+}
+
 /**
  * 閫夋嫨绾跨储浜嬩欢
  */
 const selectClue = function (clue) {
-
+  selectedClue.value = clue;
 };
 </script>
 
@@ -69,12 +85,13 @@
 
 .grid-content {
   /* min-width: 180px; */
-  border-radius: 4px;
+  border-radius: var(--el-border-radius-round);
   display: flex;
   flex-direction: column;
   gap: 16px;
-  padding: 8px 8px;
+  /* padding: 8px 8px; */
   pointer-events: auto;
+  box-shadow: var(--el-box-shadow-dark);
 }
 
 .bg-content {
@@ -89,7 +106,14 @@
 }
 
 .search-wrap {
+  padding: 0 8px;
   display: flex;
+  align-items: center;
   gap: 4px;
 }
+
+.bg-fill {
+  /* background: var(--el-fill-color-extra-light); */
+  padding: 0 8px;
+}
 </style>
diff --git a/src/views/overlay-clue/components/ClueList.vue b/src/views/overlay-clue/components/ClueList.vue
index a55a982..2b49cd8 100644
--- a/src/views/overlay-clue/components/ClueList.vue
+++ b/src/views/overlay-clue/components/ClueList.vue
@@ -8,13 +8,11 @@
             (item.selected ? 'list-item__selected' : '')
           "
           @click="selectItem(item)"
-          @mouseover="item.show = true"
-          @mouseleave="item.show = false"
           v-if="!item.delete"
         >
           <div style="display: flex; gap: 8px">
-            <div>{{ item.id }}</div>
-            <div>{{ item.clueName }}</div>
+            <div>{{ item.cid }}</div>
+            <div>{{ item.cclueName }}</div>
           </div>
         </li>
       </template>
@@ -35,9 +33,15 @@
   methods: {
     // 鍒楄〃閫夋嫨
     selectItem(item) {
+      this.clearSelect();
+      item.selected = true;
       this.$emit('itemSelected', item);
     },
-    clearSelect() {}
+    clearSelect() {
+      this.dataList.forEach((e) => {
+        e.selected = false;
+      });
+    }
   }
 };
 </script>
@@ -49,6 +53,7 @@
   overflow: auto;
   overflow-x: hidden;
   border: var(--el-border);
+  font-size: var(--el-font-size-small);
 }
 
 .list-item {
diff --git a/src/views/overlay-clue/components/ClueReport.vue b/src/views/overlay-clue/components/ClueReport.vue
index 609f7d9..7ed612e 100644
--- a/src/views/overlay-clue/components/ClueReport.vue
+++ b/src/views/overlay-clue/components/ClueReport.vue
@@ -1,270 +1,104 @@
 <template>
+  <!-- 娓呭崟璇︽儏 -->
   <el-descriptions
-    class="margin-top"
-    title="With border"
-    :column="3"
-    :size="size"
+    class=""
+    title="绾跨储娓呭崟璇︽儏"
+    :column="1"
+    size="small"
     border
   >
     <template #extra>
-      <el-button type="primary">Operation</el-button>
+      <el-button
+        type="primary"
+        text
+        size="small"
+        @click="openPDF"
+        >鏌ョ湅PDF</el-button
+      >
     </template>
     <el-descriptions-item>
       <template #label>
-        <div class="cell-item">
-          <el-icon :style="iconStyle">
-            <user />
-          </el-icon>
-          Username
-        </div>
+        <div class="cell-item">绾跨储缂栧彿</div>
       </template>
-      kooriookami
+      {{ clueData.cid }}
     </el-descriptions-item>
-
-    <el-descriptions-item>
+    <el-descriptions-item width="65px" min-width="50px">
       <template #label>
-        <div class="cell-item">
-          <el-icon :style="iconStyle">
-            <iphone />
-          </el-icon>
-          Telephone
-        </div>
+        <div class="cell-item">绾跨储鍚嶇О</div>
       </template>
-      18100000000
+      {{ clueData.cclueName }}
     </el-descriptions-item>
-    <el-descriptions-item>
+    <!-- <el-descriptions-item>
       <template #label>
-        <div class="cell-item">
-          <el-icon :style="iconStyle">
-            <location />
-          </el-icon>
-          Place
-        </div>
+        <div class="cell-item">鍒涘缓鏃堕棿</div>
       </template>
-      Suzhou
-    </el-descriptions-item>
-    <el-descriptions-item>
-      <template #label>
-        <div class="cell-item">
-          <el-icon :style="iconStyle">
-            <tickets />
-          </el-icon>
-          Remarks
-        </div>
-      </template>
-      <el-tag size="small">School</el-tag>
-    </el-descriptions-item>
-    <el-descriptions-item>
-      <template #label>
-        <div class="cell-item">
-          <el-icon :style="iconStyle">
-            <office-building />
-          </el-icon>
-          Address
-        </div>
-      </template>
-      No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province
-    </el-descriptions-item>
-  </el-descriptions>
-
-
-  <!-- 娓呭崟璇︽儏 -->
-<el-descriptions 
-class=""
-    title="绾跨储娓呭崟璇︽儏"
-    :column="1"
-    :size="size"
-    border
-    >
-    <!-- <el-descriptions-item class="inventory-descriptions-item"  v-for="item in 10" :key="item" >
-      <template #label>
-          <span>{{ item }}</span>
-      </template>
-      {{ item }}
+      {{ clueData.ccreateTime }}
     </el-descriptions-item> -->
     <el-descriptions-item>
       <template #label>
-        <div class="cell-item">
-          涓婚敭
-        </div>
+        <div class="cell-item">涓嬪彂鏃堕棿</div>
       </template>
-      {{ clueData.id }}
+      {{ clueData.creleaseTime }}
     </el-descriptions-item>
     <el-descriptions-item>
       <template #label>
-        <div class="cell-item">
-          绾跨储鍚嶇О
-        </div>
+        <div class="cell-item">鎶ヨ绔欑偣</div>
       </template>
-      {{ clueData.clueName }}
+      {{ clueData.csiteName }}
     </el-descriptions-item>
     <el-descriptions-item>
       <template #label>
-        <div class="cell-item">
-          绾跨储缁撹
-        </div>
+        <div class="cell-item">绔欑偣绫诲瀷</div>
       </template>
-      {{ clueData.conclusion }}
+      {{ clueData.csiteType }}
     </el-descriptions-item>
     <el-descriptions-item>
       <template #label>
-        <div class="cell-item">
-          鍒涘缓鏃堕棿
-        </div>
+        <div class="cell-item">绾跨储缁撹</div>
       </template>
-      {{ clueData.createTime }}
+      {{ clueData.cconclusion }}
     </el-descriptions-item>
-    <el-descriptions-item>
+    <!-- <el-descriptions-item>
       <template #label>
-        <div class="cell-item">
-          鎶ヨ绔欑偣鍚嶇О
-        </div>
+        <div class="cell-item">绔欑偣绫诲瀷閫夐」</div>
       </template>
-      {{ clueData.siteName }}
-    </el-descriptions-item>
-    <el-descriptions-item>
-      <template #label>
-        <div class="cell-item">
-          绔欑偣绫诲瀷
-        </div>
-      </template>
-      {{ clueData.sitetype }}
-    </el-descriptions-item>
-    <el-descriptions-item>
-      <template #label>
-        <div class="cell-item">
-          涓嬪彂鏃堕棿
-        </div>
-      </template>
-      {{ clueData.xfsj }}
-    </el-descriptions-item>
-
-    <el-descriptions-item>
-      <template #label>
-        <div class="cell-item">
-          绔欑偣绫诲瀷
-        </div>
-      </template>
-      {{ clueData.airCheckedOptions }}
-    </el-descriptions-item>
-</el-descriptions>
-
-<el-button type="primary" @click="reportButton">鍙嶉涓婃姤</el-button>
-<el-dialog  v-if="!this.isClueHave" v-model="isShow" width="50%"  :before-close="handleClose">
-  <template #title>
-    <span> 鍙嶉涓婃姤</span>
-  </template> 
-   <el-form label-width="120px" label-position="left">
-    <el-form-item label="闂绫诲瀷" >
-      <el-radio-group v-model="form.radio">
-          <el-radio label="鏈夐棶棰�">鏈夐棶棰�</el-radio>
-          <el-radio label="鏃犻棶棰�">鏃犻棶棰�</el-radio>
-          <el-radio label="宸茶В鍐�">宸茶В鍐�</el-radio> 
-      </el-radio-group>
-    </el-form-item>
-    <el-form-item label="绾跨储id">
-        <el-input v-model.number="form.id"></el-input>
-    </el-form-item>
-    <el-form-item label="绾跨储缁撹">
-        <el-input v-model="form.conclusion"></el-input>
-    </el-form-item>
-    <el-form-item label="璇︾粏鎻忚堪">
-      <el-input v-model="form.details" type="textarea" placeholder="璇疯緭鍏ヨ鎯�"></el-input>
-    </el-form-item>
-  </el-form>
-    <template #footer>
-      <el-button type="" @click="isShow=false">鍙栨秷</el-button>
-      <el-button @click="report">纭畾</el-button>
-    </template>
-</el-dialog>
-<el-button type="primary" @click="reportPhoto">鍥剧墖涓婁紶</el-button>
-<el-dialog v-model="isShowReportPhoto">
-<el-form>
-    <el-form-item label="绾跨储id">
-      <el-input v-model=""></el-input>
-    </el-form-item>
-</el-form>
-</el-dialog>
+      {{ clueData.cairCheckedOptions }}
+    </el-descriptions-item> -->
+  </el-descriptions>
+  <ClueReportConclusion :clueId="clueData.cid"></ClueReportConclusion>
+  <ClueReportQuestion :clueId="clueData.cid"></ClueReportQuestion>
 </template>
 
-
 <script>
-import axios from 'axios'
+import ClueReportConclusion from './ClueReportConclusion.vue';
+import ClueReportQuestion from './ClueReportQuestion.vue';
 export default {
-  // 甯冨皵绫诲瀷 鎺у埗鍙嶉涓婃姤鐨勬牱寮�
-  // fasle浠h〃鏃犳暟鎹� true浠h〃鏈夋暟鎹�
-  props:{
-    isClueHave:{
-      type:Boolean,
-      default:false
+  components: { ClueReportConclusion, ClueReportQuestion },
+  props: {
+    clueData: {
+      type: Object,
+      default: () => {
+        return {};
+      }
     }
   },
   name: 'HomePage',
-  data(){
-    return{
-      // 淇濆瓨涓嬪彂绾跨储鏌ヨ鍚庤繑鍥炵殑鏁扮粍鏁版嵁
-      // 鎴栬�呮槸鐐瑰嚮鐨勫睍绀虹粨鏋滃搴旂殑瀵硅薄鏁版嵁
-      clueData:{},
-      // 涓婃姤寮瑰嚭妗�
-      isShow:false,
+  data() {
+    return {
       // 鍥剧墖涓婁紶鐨勫脊鍑烘
-      isShowReportPhoto:false,
-      // 琛ㄥ崟涓�
-      form:{
-        // 闂绫诲瀷 鍗曢�夋寜閽殑鍊�
-        radio:'',
-        // 绾跨储id
-        id:0,
-        // 绾跨储缁撹
-        conclusion:'',
-        // 璇︾粏鎻忚堪
-        details:'',
-        // 淇濆瓨鎻愪氦鍚庣殑杩斿洖缁撴灉 
-        result:''
-      },
-      photoForm:{
-        clueId:0,
-        questionDescription:'',
-        
+      isShowReportPhoto: false,
+      photoForm: {
+        clueId: 0,
+        questionDescription: ''
       }
-    }
+    };
   },
-  methods:{
-    // 鐐瑰嚮涓婃姤鎸夐挳
-    reportButton(){
-      // 褰撹绾跨储id宸茬粡鏈夌粨璁轰簡
-      if(this.isClueHave === true){
-        alert('璇ョ嚎绱㈠凡缁忔湁缁撹浜嗭紝涓嶅彲閲嶅涓婃姤')
-      }
-      else if(this.isClueHave === false){
-        this.isShow = !this.isShow
-      }
-    },
-// 鐐瑰嚮鍙嶉涓婃姤鏃�
-report(){
-  //鍙戝嚭璇锋眰 
-  axios.post('').then(response =>{
-      // 鑾峰彇缁撴灉
-      this.result = response.data.data
-      if(this.result =='涓婃姤鎴愬姛'){
-        ElMessage.success(this.result);
-        // 鍙湁涓婁紶鎴愬姛鏃舵墠鍏抽棴瀵硅瘽妗�
-        this.isShow = false
-      }
-      else if(this.result =='涓婃姤澶辫触'){
-        ElMessage.error(this.result);
-        // 鍚﹀垯鏆傛椂涓嶅叧闂璇濇 
-      }
-  })
-
-},
-reportPhoto(){
-
-}
+  methods: {
+    openPDF() {
+      
+    }
   }
-}
+};
 </script>
 
-<style>
-
-</style>
\ No newline at end of file
+<style></style>
diff --git a/src/views/overlay-clue/components/ClueReportConclusion.vue b/src/views/overlay-clue/components/ClueReportConclusion.vue
new file mode 100644
index 0000000..a42a427
--- /dev/null
+++ b/src/views/overlay-clue/components/ClueReportConclusion.vue
@@ -0,0 +1,193 @@
+<template>
+  <div class="fy-h2">
+    绾跨储缁撹
+    <el-button
+      v-if="conclusion"
+      type="warning"
+      size="small"
+      plain
+      icon="Upload"
+      @click="pushConclusion"
+      :disabled="pushing ? true : conclusion.ccUploaded"
+      >{{ pushing ? '鎺ㄩ�佷腑' : pushText }}</el-button
+    >
+  </div>
+  <el-descriptions v-if="conclusion" :column="1" size="small" border>
+    <el-descriptions-item width="1px" min-width="30px">
+      <template #label>
+        <div class="descriptions-item">闂绫诲瀷</div>
+      </template>
+      {{ conclusion.ccQuestionType }}
+    </el-descriptions-item>
+    <el-descriptions-item label="绾跨储缁撹">
+      {{ conclusion.ccConclusion }}
+    </el-descriptions-item>
+    <el-descriptions-item label="璇︾粏鎻忚堪">
+      {{ conclusion.ccDetails }}
+    </el-descriptions-item>
+  </el-descriptions>
+  <div v-else class="fy-dashed-border">
+    <el-empty :image-size="50" description="绾跨储缁撹鏈笂浼�">
+      <el-button type="primary" @click="openDialog"
+        >鍙嶉涓婃姤</el-button
+      >
+    </el-empty>
+  </div>
+  <el-dialog
+    v-model="dialogShow"
+    width="50%"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+  >
+    <template #header>
+      <span> 鍙嶉缁撹</span>
+    </template>
+    <el-form
+      label-width="120px"
+      label-position="left"
+      :rules="rules"
+      :model="formObj"
+      ref="formRef"
+    >
+      <el-form-item label="闂绫诲瀷" prop="ccQuestionType">
+        <el-radio-group v-model="formObj.ccQuestionType">
+          <el-radio label="鏈夐棶棰�">鏈夐棶棰�</el-radio>
+          <el-radio label="鏃犻棶棰�">鏃犻棶棰�</el-radio>
+          <el-radio label="宸茶В鍐�">宸茶В鍐�</el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="绾跨储缁撹" prop="ccConclusion">
+        <el-input v-model="formObj.ccConclusion"></el-input>
+      </el-form-item>
+      <el-form-item label="璇︾粏鎻忚堪" prop="ccDetails">
+        <el-input
+          v-model="formObj.ccDetails"
+          type="textarea"
+          placeholder="璇疯緭鍏ヨ鎯�"
+        ></el-input>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="onCancel">鍙栨秷</el-button>
+      <el-button type="primary" :loading="loading" @click="onSubmit"
+        >纭畾</el-button
+      >
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { reactive, ref, watch, computed } from 'vue';
+import { useFormConfirm } from '@/composables/formConfirm';
+import clueConclusionApi from '@/api/clue/clueConclusionApi';
+
+const props = defineProps({
+  clueId: Number
+});
+
+watch(
+  () => props.clueId,
+  () => {
+    getConclusion();
+  }
+);
+
+// 鎺ㄩ�佺姸鎬�
+const pushing = ref(false);
+
+// 绾跨储缁撹
+const conclusion = ref({});
+
+// 涓婃姤寮瑰嚭妗�
+const dialogShow = ref(false);
+const { formObj, formRef, onSubmit, onCancel, clear } =
+  useFormConfirm({
+    submit: {
+      do: submit
+    },
+    cancel: {
+      do: cancel
+    }
+  });
+const loading = ref(false);
+// 琛ㄥ崟妫�鏌ヨ鍒�
+const rules = reactive({
+  ccQuestionType: [
+    {
+      required: true,
+      message: '闂绫诲瀷涓嶈兘涓虹┖',
+      trigger: 'change'
+    }
+  ],
+  ccConclusion: [
+    {
+      required: true,
+      message: '绾跨储缁撹涓嶈兘涓虹┖',
+      trigger: 'blur'
+    }
+  ],
+  ccDetails: [
+    {
+      required: true,
+      message: '璇︾粏鎻忚堪涓嶈兘涓虹┖',
+      trigger: 'blur'
+    }
+  ]
+});
+
+// 鎵撳紑涓婃姤鍙嶉瀵硅瘽妗�
+function openDialog() {
+  dialogShow.value = true;
+}
+
+function submit() {
+  formObj.value.cid = props.clueId;
+  return uploadConclusion();
+}
+function cancel() {
+  dialogShow.value = false;
+}
+
+/**
+ * 涓婁紶绾跨储缁撹
+ */
+function uploadConclusion() {
+  loading.value = true;
+  return clueConclusionApi
+    .uploadConclusion(formObj.value)
+    .then(() => {
+      dialogShow.value = false;
+      clear();
+      getConclusion();
+    })
+    .finally(() => {
+      loading.value = false;
+    });
+}
+
+/**
+ * 鑾峰彇绾跨储缁撹
+ */
+function getConclusion() {
+  clueConclusionApi.getConclusion(props.clueId).then((res) => {
+    conclusion.value = res;
+  });
+}
+
+function pushConclusion() {
+  clueConclusionApi
+    .pushConclusion([conclusion.value.ccId])
+    .then(() => {
+      pushing.value = true;
+    });
+}
+
+const pushText = computed(() => {
+  return conclusion.value.ccUploaded ? '宸叉帹閫�' : '鎺ㄩ�侀棶棰�';
+});
+</script>
+<style scoped>
+.descriptions-item {
+  /* background: aqua; */
+}
+</style>
diff --git a/src/views/overlay-clue/components/ClueReportQuestion.vue b/src/views/overlay-clue/components/ClueReportQuestion.vue
new file mode 100644
index 0000000..cc68613
--- /dev/null
+++ b/src/views/overlay-clue/components/ClueReportQuestion.vue
@@ -0,0 +1,139 @@
+<template>
+  <div class="fy-h2">绾跨储闂</div>
+  <template v-if="questionList.length > 0">
+    <el-descriptions
+      v-for="(item, index) in questionList"
+      :title="'闂 ' + item.cqUid"
+      :key="index"
+      :column="2"
+      size="small"
+      border
+    >
+      <template #extra>
+        <el-button-group>
+          <el-button
+            type="warning"
+            size="small"
+            plain
+            icon="Upload"
+            @click="pushQuestion(item)"
+            :disabled="item.pushing ? true : item.cqUploaded"
+            >{{
+              item.cqUploaded
+                ? '宸叉帹閫�'
+                : item.pushing
+                ? '鎺ㄩ�佷腑'
+                : '鎺ㄩ�侀棶棰�'
+            }}</el-button
+          >
+          <el-button
+            type="primary"
+            size="small"
+            @click="checkQuestion(item)"
+            >闂璇︽儏</el-button
+          >
+        </el-button-group>
+      </template>
+      <el-descriptions-item
+        width="1px"
+        min-width="70px"
+        label="闂缂栧彿"
+      >
+        {{ item.cqUid }}
+      </el-descriptions-item>
+      <el-descriptions-item label="鎵�鍦ㄨ闀�">
+        {{ item.cqStreet }}
+      </el-descriptions-item>
+      <el-descriptions-item label="闂鎻忚堪">
+        {{ item.cqDescription }}
+      </el-descriptions-item>
+      <!-- <el-descriptions-item label="璇︾粏鍦板潃">
+        {{ item.cqAddress }}
+      </el-descriptions-item>
+      <el-descriptions-item label="缁忓害">
+        {{ item.cqLongitude }}
+      </el-descriptions-item>
+      <el-descriptions-item label="绾害">
+        {{ item.cqLatitude }}
+      </el-descriptions-item>
+      <el-descriptions-item label="鍒涘缓鏃堕棿">
+        {{ item.cqCreateTime }}
+      </el-descriptions-item> -->
+    </el-descriptions>
+    <div class="btn-wrap">
+      <el-button type="primary" @click="openDialog"
+        >娣诲姞闂</el-button
+      >
+    </div>
+  </template>
+  <div v-else class="fy-dashed-border">
+    <el-empty :image-size="50" description="鏃犵嚎绱㈤棶棰�">
+      <el-button type="primary" @click="openDialog"
+        >鍙嶉涓婃姤</el-button
+      >
+    </el-empty>
+  </div>
+  <QuestionDetail
+    :clueId="clueId"
+    v-model:show="dialogShow"
+    :question="selectedQuestion"
+    @on-submit="getQuestion"
+  ></QuestionDetail>
+</template>
+
+<script setup>
+import { ref, watch, computed } from 'vue';
+import clueQuestionApi from '@/api/clue/clueQuestionApi';
+import QuestionDetail from './QuestionDetail.vue';
+
+const props = defineProps({
+  clueId: Number
+});
+
+// 绾跨储缁撹
+const questionList = ref([]);
+// 涓婃姤寮瑰嚭妗�
+const dialogShow = ref(false);
+const selectedQuestion = ref();
+
+watch(
+  () => props.clueId,
+  () => {
+    getQuestion();
+  }
+);
+
+// 鎵撳紑涓婃姤鍙嶉瀵硅瘽妗�
+function openDialog() {
+  selectedQuestion.value = undefined;
+  dialogShow.value = true;
+}
+
+// 鏌ョ湅闂璇︽儏
+function checkQuestion(item) {
+  selectedQuestion.value = item;
+  dialogShow.value = true;
+}
+/**
+ * 鑾峰彇绾跨储缁撹
+ */
+function getQuestion() {
+  clueQuestionApi.getQuestion(props.clueId).then((res) => {
+    questionList.value = res;
+  });
+}
+
+function pushQuestion(item) {
+  clueQuestionApi.pushQuestion([item.cqId]).then(() => {
+    item.pushing = true;
+  });
+}
+
+</script>
+<style scoped>
+.btn-wrap {
+  display: flex;
+  justify-content: center;
+  padding: 16px;
+}
+</style>
diff --git a/src/views/overlay-clue/components/QuestionDetail.vue b/src/views/overlay-clue/components/QuestionDetail.vue
new file mode 100644
index 0000000..ac67c93
--- /dev/null
+++ b/src/views/overlay-clue/components/QuestionDetail.vue
@@ -0,0 +1,264 @@
+<template>
+  <el-dialog
+    v-model="dialogShow"
+    width="50%"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    destroy-on-close
+  >
+    <template #header>
+      <span> 鍙嶉闂</span>
+    </template>
+    <el-form
+      label-width="90px"
+      label-position="left"
+      :rules="rules"
+      :model="formObj"
+      ref="formRef"
+    >
+      <el-form-item label="闂鎻忚堪" prop="cqDescription">
+        <el-input
+          v-model="formObj.cqDescription"
+          type="textarea"
+          placeholder="璇疯緭鍏ヨ鎯�"
+        ></el-input>
+      </el-form-item>
+      <el-form-item label="鎵�鍦ㄨ闀�" prop="cqStreet">
+        <el-select v-model="formObj.cqStreet" placeholder="鎵�鍦ㄨ闀�">
+          <el-option
+            v-for="s in streets"
+            :key="s.value"
+            :label="s.label"
+            :value="s.label"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="璇︾粏鍦板潃" prop="cqAddress">
+        <el-input v-model="formObj.cqAddress"></el-input>
+      </el-form-item>
+      <el-form-item label="鍧愭爣" prop="coordinate">
+        <el-input
+          style="width: 300px; margin-right: 8px"
+          v-model="formObj.coordinate"
+          placeholder="缁忕含搴﹀潗鏍囷紝鏍煎紡涓�121.123452,31.231235"
+        ></el-input>
+        <el-button plain type="primary" @click="openMapDialog"
+          >鍧愭爣鎷惧彇</el-button
+        >
+      </el-form-item>
+      <el-form-item label="闂鍥剧墖" prop="files">
+        <el-upload
+          ref="uploadRef"
+          :file-list="fileList"
+          action=""
+          :auto-upload="false"
+          list-type="picture-card"
+          name="images"
+          accept="image/png, image/jpeg"
+          :limit="3"
+          multiple
+          :on-remove="handleFileRemove"
+          :on-change="handleFileChange"
+        >
+          <el-icon><Plus /></el-icon>
+          <template #tip>
+            <div class="el-upload__tip">
+              璇烽�夋嫨灏忎簬500kb鐨刯pg/png鍥剧墖锛屾渶澶�3寮�
+            </div>
+          </template>
+        </el-upload>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="onCancel">鍙栨秷</el-button>
+      <el-button
+        :disabled="!edit"
+        type="primary"
+        :loading="loading"
+        @click="onSubmit"
+        >纭畾</el-button
+      >
+    </template>
+  </el-dialog>
+  <MapSearch
+    v-model:show="mapDialogShow"
+    @on-submit="selectAddress"
+  ></MapSearch>
+</template>
+
+<script setup>
+import { reactive, ref, watch, computed } from 'vue';
+import { useFormConfirm } from '@/composables/formConfirm';
+import { streets } from '@/constant/street';
+import clueQuestionApi from '@/api/clue/clueQuestionApi';
+import MapSearch from '@/components/map/MapSearch.vue';
+
+const props = defineProps({
+  clueId: Number,
+  show: Boolean,
+  question: Object
+});
+
+const emit = defineEmits(['update:show', 'onSubmit'], ['onClose']);
+
+// 涓婃姤寮瑰嚭妗�
+const dialogShow = ref(false);
+const mapDialogShow = ref(false);
+const uploadRef = ref();
+const fileList = ref([]);
+
+function handleFileRemove(file, fileList) {
+  formObj.value.files = fileList;
+}
+
+function handleFileChange(file, fileList) {
+  formObj.value.files = fileList;
+}
+
+const { formObj, formRef, edit, onSubmit, onCancel, clear } =
+  useFormConfirm({
+    submit: {
+      do: submit
+    },
+    cancel: {
+      do: cancel
+    }
+  });
+const loading = ref(false);
+// 琛ㄥ崟妫�鏌ヨ鍒�
+const rules = reactive({
+  cqDescription: [
+    {
+      required: true,
+      message: '闂鎻忚堪涓嶈兘涓虹┖',
+      trigger: 'blur'
+    }
+  ],
+  cqStreet: [
+    {
+      required: true,
+      message: '鎵�鍦ㄨ闀囦笉鑳戒负绌�',
+      trigger: 'change'
+    }
+  ],
+  cqAddress: [
+    {
+      required: true,
+      message: '璇︾粏鍦板潃涓嶈兘涓虹┖',
+      trigger: 'blur'
+    }
+  ],
+  coordinate: [
+    {
+      required: true,
+      message: '鍧愭爣涓嶈兘涓虹┖',
+      trigger: 'blur'
+    }
+  ],
+  // cqLongitude: [
+  //   {
+  //     required: true,
+  //     message: '缁忓害涓嶈兘涓虹┖',
+  //     trigger: 'blur'
+  //   }
+  // ],
+  // cqLatitude: [
+  //   {
+  //     required: true,
+  //     message: '缁村害涓嶈兘涓虹┖',
+  //     trigger: 'blur'
+  //   }
+  // ],
+  files: [
+    {
+      required: true,
+      message: '鍥剧墖涓嶈兘涓虹┖',
+      trigger: 'change'
+    }
+  ]
+});
+
+function submit() {
+  const coor = formObj.value.coordinate.split(',');
+  const q = {
+    cId: parseInt(props.clueId),
+    cqDescription: formObj.value.cqDescription,
+    cqStreet: formObj.value.cqStreet,
+    cqAddress: formObj.value.cqAddress,
+    cqLongitude: parseFloat(coor[0]),
+    cqLatitude: parseFloat(coor[1])
+  };
+  const files = [];
+  formObj.value.files.forEach((f) => {
+    files.push(f.raw);
+  });
+  return uploadQuestion(q, files);
+}
+
+function cancel() {
+  // clear();
+  dialogShow.value = false;
+}
+
+function openMapDialog() {
+  mapDialogShow.value = true;
+}
+
+function selectAddress(result) {
+  formObj.value.cqAddress = result.address;
+  formObj.value.coordinate = result.gpsLon + ',' + result.gpsLat;
+}
+
+/**
+ * 涓婁紶绾跨储缁撹
+ */
+function uploadQuestion(question, files) {
+  loading.value = true;
+
+  return clueQuestionApi
+    .uploadQuestion(question, files)
+    .then(() => {
+      dialogShow.value = false;
+      clear();
+      uploadRef.value.clearFiles();
+      emit('onSubmit');
+    })
+    .finally(() => {
+      loading.value = false;
+    });
+}
+
+function parseFormObj(question) {
+  question.coordinate =
+    question.cqLongitude + ',' + question.cqLatitude;
+  fileList.value = [];
+  question.cqFilePath.forEach((f, index) => {
+    fileList.value.push({
+      name: `${index}`,
+      url: f
+    });
+  });
+  return question;
+}
+
+watch(
+  () => props.show,
+  (val) => {
+    dialogShow.value = val;
+  }
+);
+watch(
+  () => props.question,
+  (val) => {
+    fileList.value = [];
+    if (val) {
+      formObj.value = parseFormObj(val);
+    } else {
+      formObj.value = {};
+    }
+  }
+);
+watch(dialogShow, (val) => {
+  emit('update:show', val);
+});
+</script>

--
Gitblit v1.9.3