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