From 4a836815f12e8ba717702cc8ed431e1b4f96134c Mon Sep 17 00:00:00 2001 From: riku <risaku@163.com> Date: 星期五, 25 四月 2025 13:55:34 +0800 Subject: [PATCH] 新增内部线索相关管理逻辑 --- src/api/clue/clueInternalApi.js | 33 ++ src/api/clue/clueTaskApi.js | 27 + src/model/clueQuestion.js | 2 src/api/clue/clueConclusionApi.js | 20 src/views/HomePage.vue | 2 src/views/overlay-clue/report/ClueReport.vue | 12 src/views/internal-clue/InternalClueManage.vue | 127 +++++++ src/api/clue/clueQuestionApi.js | 37 ++ src/components/map/MapSearch.vue | 84 +++- src/views/internal-clue/InternalClueLayout.vue | 45 ++ src/views/overlay-clue/report/components/ClueReportQuestion.vue | 16 src/views/overlay-clue/report/components/ClueReportClue.vue | 25 + src/views/overlay-clue/report/components/ClueReportConclusion.vue | 18 src/api/index.js | 2 src/components/map/baseMapUtil.js | 8 src/views/overlay-clue/report/components/QuestionDetail.vue | 196 ++++++++++-- src/views/internal-clue/InternalClueEdit.vue | 208 +++++++++++++ src/components/core/CoreHeader.vue | 3 src/views/overlay-clue/task/ClueTaskEdit.vue | 35 + src/api/config.js | 10 src/api/clue/clueApi.js | 16 21 files changed, 800 insertions(+), 126 deletions(-) diff --git a/src/api/clue/clueApi.js b/src/api/clue/clueApi.js index ce9c458..7087fe9 100644 --- a/src/api/clue/clueApi.js +++ b/src/api/clue/clueApi.js @@ -7,14 +7,14 @@ * @returns */ getClue({ sTime, eTime, pageNum = 1, pageSize = 30 }) { - let url = 'clue/fetch?'; - if (sTime) { - url += `sTime=${sTime}&`; - } - if (eTime) { - url += `eTime=${eTime}&`; - } - return $clue.get(`${url}pageNum=${pageNum}&pageSize=${pageSize}`); + return $clue.get(`clue/fetch`, { + params: { + sTime, + eTime, + pageNum, + pageSize + } + }); // .then((res) => res.data); }, diff --git a/src/api/clue/clueConclusionApi.js b/src/api/clue/clueConclusionApi.js index cdea07b..481d39c 100644 --- a/src/api/clue/clueConclusionApi.js +++ b/src/api/clue/clueConclusionApi.js @@ -5,25 +5,33 @@ * 鑾峰彇绾跨储缁撹 * @param {string} clueId 绾跨储id */ - getConclusion(clueId) { - return $clue.get(`clue/conclusion/fetch?clueId=${clueId}`).then((res) => res.data); + getConclusion(clueId, internal) { + return $clue + .get(`clue/conclusion/fetch`, { + params: { clueId, internal } + }) + .then((res) => res.data); }, /** * 鎻愪氦绾跨储缁撹 * @param {object} conclusion 绾跨储 - * @returns + * @returns */ uploadConclusion(conclusion) { - return $clue.post(`clue/conclusion/upload`, conclusion).then((res) => res.data); + return $clue + .post(`clue/conclusion/upload`, conclusion) + .then((res) => res.data); }, /** * 鎺ㄩ�佺嚎绱㈢粨璁鸿嚦绗笁鏂� * @param {Array} conclusionIdList 绾跨储id闆嗗悎 - * @returns + * @returns */ pushConclusion(conclusionIdList) { - return $clue.post(`clue/conclusion/push`, conclusionIdList).then((res) => res.data); + return $clue + .post(`clue/conclusion/push`, conclusionIdList) + .then((res) => res.data); } }; diff --git a/src/api/clue/clueInternalApi.js b/src/api/clue/clueInternalApi.js new file mode 100644 index 0000000..7e494de --- /dev/null +++ b/src/api/clue/clueInternalApi.js @@ -0,0 +1,33 @@ +import { $clue } from '../index'; + +export default { + /** + * 鏌ヨ绾跨储娓呭崟 + * @param {object} param0 + * @returns + */ + getInternalClue({ sTime, eTime, pageNum = 1, pageSize = 30 }) { + return $clue.get(`clue/internal/fetch`, { + params: { + sTime, + eTime, + pageNum, + pageSize + } + }); + }, + + createInternalClue(clueInternal) { + return $clue.put('clue/internal/create', clueInternal); + }, + + updateInternalClue(clueInternal) { + return $clue.post('clue/internal/update', clueInternal); + }, + + deleteInternalClue(clueInternal) { + return $clue.delete('clue/internal/delete', { + data: clueInternal + }); + } +}; diff --git a/src/api/clue/clueQuestionApi.js b/src/api/clue/clueQuestionApi.js index a5b99a9..7968167 100644 --- a/src/api/clue/clueQuestionApi.js +++ b/src/api/clue/clueQuestionApi.js @@ -6,9 +6,11 @@ * 鑾峰彇宸叉彁浜ょ殑绾跨储闂 * @param {string} clueId 绾跨储id */ - getQuestion(clueId) { + getQuestion(clueId, internal) { return $clue - .get(`clue/question/fetch?clueId=${clueId}`) + .get(`clue/question/fetch`, { + params: { clueId, internal } + }) .then((res) => { return getClueQuestionList(res.data); }); @@ -26,11 +28,34 @@ files.forEach((e) => { formData.append('images', e); }); - return $clue.post(`clue/question/upload`, formData).then((res) => res.data); + return $clue + .post(`clue/question/upload`, formData) + .then((res) => res.data); + }, + + /** + * 淇敼绾跨储闂 + * @param {object} question 闂鎻忚堪 + * @param {*} files 闂鍥剧墖 + * @param {Array} deleteImgUrl 鍒犻櫎鐨勫浘鐗囩浉瀵硅矾寰勶紝鐢�;鍒嗗壊 + * @returns + */ + updateQuestion(question, files, deleteImgUrl) { + const formData = new FormData(); + formData.append('question', JSON.stringify(question)); + formData.append('deleteImg', deleteImgUrl); + files.forEach((e) => { + formData.append('images', e); + }); + return $clue + .post(`clue/question/update`, formData) + .then((res) => res.data); }, deleteQuestion(questionId) { - return $clue.delete(`clue/question`, { params: { questionId } }).then((res) => res.data); + return $clue + .delete(`clue/question`, { params: { questionId } }) + .then((res) => res.data); }, uploadQuestionUrl() { @@ -43,6 +68,8 @@ * @returns */ pushQuestion(questionIdList) { - return $clue.post(`clue/question/push`, questionIdList).then((res) => res.data); + return $clue + .post(`clue/question/push`, questionIdList) + .then((res) => res.data); } }; diff --git a/src/api/clue/clueTaskApi.js b/src/api/clue/clueTaskApi.js index bd52d71..42c151e 100644 --- a/src/api/clue/clueTaskApi.js +++ b/src/api/clue/clueTaskApi.js @@ -2,13 +2,21 @@ export default { /** + * 鍒涘缓鍐呴儴绾跨储浠诲姟 + * @param {*} clueTask + * @returns + */ + createClueTaskInternal(clueTaskInternal) { + return $clue.put(`clue/task/create/internal`, clueTaskInternal); + }, + + /** * 鍒涘缓绾跨储浠诲姟 * @param {*} clueTask * @returns */ createClueTask(clueTask) { - return $clue - .put(`clue/task/create`, clueTask) + return $clue.put(`clue/task/create`, clueTask); }, /** @@ -17,8 +25,7 @@ * @returns */ updateClueTask(clueTask) { - return $clue - .post(`clue/task/update`, clueTask) + return $clue.post(`clue/task/update`, clueTask); }, /** @@ -27,7 +34,15 @@ * @returns */ fetchClueTask(clueTask) { - return $clue - .post(`clue/task/fetch`, clueTask) + return $clue.post(`clue/task/fetch`, clueTask); + }, + + /** + * 鍒犻櫎绾跨储浠诲姟 + * @param {*} clueTask + * @returns + */ + deleteClueTask(clueTask) { + return $clue.post(`clue/task/delete`, clueTask); } }; diff --git a/src/api/config.js b/src/api/config.js index e15bd82..3a06f16 100644 --- a/src/api/config.js +++ b/src/api/config.js @@ -11,11 +11,6 @@ // 鍦ㄥ彂閫佽姹備箣鍓�, 娣诲姞璇锋眰澶� // config.headers = addHeaders(config.headers); - console.log('==>璇锋眰寮�濮�'); - console.log(`${config.baseURL}${config.url}`); - if (config.data) { - console.log('==>璇锋眰鏁版嵁', config.data); - } return config; }, function (error) { @@ -35,6 +30,11 @@ function (response) { // 2xx 鑼冨洿鍐呯殑鐘舵�佺爜閮戒細瑙﹀彂璇ュ嚱鏁般�� // 瀵瑰搷搴旀暟鎹仛鐐逛粈涔� + const config = response.config; + console.log('==>璇锋眰寮�濮�', `${config.baseURL}${config.url}`); + if (config.data) { + console.log('==>璇锋眰鏁版嵁', config.data); + } console.log(response); console.log('==>璇锋眰缁撴潫'); if (response.status == 200) { diff --git a/src/api/index.js b/src/api/index.js index f88ce35..74120e3 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -1,7 +1,7 @@ import axios from 'axios'; import { setInterceptors } from "./config"; -const debug = true; +const debug = false; // let url1 = 'http://47.100.191.150:9031/'; // let url1_file = 'http://47.100.191.150:9031'; diff --git a/src/components/core/CoreHeader.vue b/src/components/core/CoreHeader.vue index 7465b24..7294f95 100644 --- a/src/components/core/CoreHeader.vue +++ b/src/components/core/CoreHeader.vue @@ -25,7 +25,8 @@ return { radioOptions: [ { name: '绾跨储绠$悊', label: 0 }, - { name: '缃戞牸绠$悊', label: 1 } + // { name: '缃戞牸绠$悊', label: 1 }, + { name: '鍐呴儴绾跨储', label: 2 }, ], radio1: 0 }; diff --git a/src/components/map/MapSearch.vue b/src/components/map/MapSearch.vue index e397bc5..624f146 100644 --- a/src/components/map/MapSearch.vue +++ b/src/components/map/MapSearch.vue @@ -1,9 +1,16 @@ <template> - <el-dialog v-model="dialogShow" width="70%" destroy-on-close> + <el-dialog + class="dialog" + v-model="dialogShow" + width="70%" + destroy-on-close + > <template #header> - <div> 鍧愭爣鎷惧彇</div> + <div>鍧愭爣鎷惧彇</div> </template> - <div class="fy-tip-red">宸﹂敭鐐瑰嚮鍦板浘閫夊彇鍧愭爣鐐癸紝鎴栬�呮牴鎹叧閿瓧鎼滅储鍦扮偣</div> + <div class="fy-tip-red"> + 宸﹂敭鐐瑰嚮鍦板浘閫夊彇鍧愭爣鐐癸紝鎴栬�呮牴鎹叧閿瓧鎼滅储鍦扮偣 + </div> <el-row> <el-col :span="10"> <el-form @@ -45,11 +52,14 @@ <span>{{ searchResult.address }}</span> <div> <span>{{ - '楂樺痉' + searchResult.lon + ', ' + searchResult.lat + '楂樺痉锛�' + searchResult.lon + ', ' + searchResult.lat }}</span> <el-divider direction="vertical" /> <span>{{ - 'GPS' + searchResult.gpsLon + ', ' + searchResult.gpsLat + 'GPS锛�' + + searchResult.gpsLon + + ', ' + + searchResult.gpsLat }}</span> </div> </div> @@ -94,7 +104,10 @@ }; }, props: { - show: Boolean + // 瀵硅瘽妗嗘樉绀洪殣钘� + show: Boolean, + // 榛樿鎼滅储鐐圭粡绾害锛孾lng, lat] + defaultCoor: Array }, data() { return { @@ -146,29 +159,21 @@ geocoder = new AMap.Geocoder({ city: '涓婃捣' // city 鎸囧畾杩涜缂栫爜鏌ヨ鐨勫煄甯傦紝鏀寔浼犲叆鍩庡競鍚嶃�乤dcode 鍜� citycode }); + if (this.defaultCoor) { + const [lng, lat] = baseMapUtil.wgs84togcj02( + this.defaultCoor[0], + this.defaultCoor[1] + ); + const lnglat = new AMap.LngLat(lng, lat); + this.setMarker(lnglat); + this.getAddress(lnglat); + this.map.setFitView(); + } 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; - } - }); + this.setMarker(ev.lnglat); + this.getAddress(ev.lnglat); }); }); // inited = true; @@ -199,6 +204,29 @@ } }); }, + getAddress(lnglat) { + geocoder.getAddress(lnglat, (status, result) => { + if (status === 'complete' && result.info === 'OK') { + this.searchResult.address = + result.regeocode.formattedAddress; + this.searchResult.lon = lnglat.getLng(); + this.searchResult.lat = lnglat.getLat(); + const [gpsLon, gpsLat] = baseMapUtil.gcj02towgs84( + this.searchResult.lon, + this.searchResult.lat + ); + this.searchResult.gpsLon = gpsLon; + this.searchResult.gpsLat = gpsLat; + } + }); + }, + setMarker(lnglat) { + this.map.clearMap(); + const marker = new AMap.Marker({ + position: lnglat + }); + this.map.add(marker); + }, submit() { this.$emit('onSubmit', this.searchResult); this.dialogShow = false; @@ -218,4 +246,8 @@ border-radius: var(--el-border-radius-round); box-shadow: var(--el-box-shadow); } + +.dialog { + pointer-events: auto; +} </style> diff --git a/src/components/map/baseMapUtil.js b/src/components/map/baseMapUtil.js index 02b6601..ce3ad05 100644 --- a/src/components/map/baseMapUtil.js +++ b/src/components/map/baseMapUtil.js @@ -129,8 +129,10 @@ /** * 楂樺痉鍦板浘鍧愭爣杞珿PS鍧愭爣绠楁硶 */ - gcj02towgs84(lng, lat) { + gcj02towgs84(_lng, _lat) { // lat = +latlng = +lng + const lng = parseFloat(_lng) + const lat = parseFloat(_lat) if (out_of_china(lng, lat)) { return [lng, lat]; } else { @@ -158,8 +160,10 @@ * @param lat * @returns {*[]} */ - wgs84togcj02(lng, lat) { + wgs84togcj02(_lng, _lat) { // lat = +latlng = +lng + const lng = parseFloat(_lng) + const lat = parseFloat(_lat) if (out_of_china(lng, lat)) { return [lng, lat]; } else { diff --git a/src/model/clueQuestion.js b/src/model/clueQuestion.js index 5616f09..338523f 100644 --- a/src/model/clueQuestion.js +++ b/src/model/clueQuestion.js @@ -1,7 +1,7 @@ import { $clue } from '@/api/index'; function getClueQuestion(data) { - data.cqFilePath = data.cqFilePath.split(';').map((val) => { + data.files = data.cqFilePath.split(';').map((val) => { return $clue.imgUrl + val; }); return data; diff --git a/src/views/HomePage.vue b/src/views/HomePage.vue index 4ede8f0..03c7e04 100644 --- a/src/views/HomePage.vue +++ b/src/views/HomePage.vue @@ -5,6 +5,7 @@ <!-- <router-view> --> <ClueLayout v-show="menuIndex == 0"></ClueLayout> <GridLayout v-show="menuIndex == 1"></GridLayout> + <InternalClueLayout v-show="menuIndex == 2"></InternalClueLayout> <!-- </router-view> --> </div> </template> @@ -14,6 +15,7 @@ import GridLayout from '@/views/overlay-grid/GridLayout.vue'; import ClueLayout from '@/views/overlay-clue/ClueLayout.vue'; +import InternalClueLayout from '@/views/internal-clue/InternalClueLayout.vue'; // 椁愬崟绱㈠紩 const menuIndex = ref(0); diff --git a/src/views/internal-clue/InternalClueEdit.vue b/src/views/internal-clue/InternalClueEdit.vue new file mode 100644 index 0000000..99d359f --- /dev/null +++ b/src/views/internal-clue/InternalClueEdit.vue @@ -0,0 +1,208 @@ +<template> + <el-dialog + style="pointer-events: auto" + :model-value="modelValue" + @update:modelValue="handleDialogChange" + width="50%" + :close-on-click-modal="false" + :close-on-press-escape="false" + destroy-on-close + > + <template #header> + <span> {{ create ? '鍙戝竷鍐呴儴绾跨储' : '鏇存柊鍐呴儴绾跨储' }}</span> + </template> + <el-form + label-width="120px" + label-position="right" + :rules="rules" + :model="formObj" + ref="formRef" + > + <el-form-item label="绾跨储鍚嶇О" prop="cclueName"> + <el-input + v-model="formObj.cclueName" + placeholder="璇疯緭鍏ョ嚎绱㈠悕绉�" + class="w-200" + ></el-input> + </el-form-item> + <el-form-item label="绾跨储鎻忚堪" prop="cconclusion"> + <el-input + v-model="formObj.cconclusion" + type="textarea" + placeholder="璇疯緭鍏ョ嚎绱㈡弿杩�" + ></el-input> + </el-form-item> + <el-form-item label="璇︾粏鍦板潃" prop="caddress"> + <el-input + v-model="formObj.caddress" + placeholder="璇疯緭鍏ュ湴鍧�鎴栬�呴�氳繃鈥滃潗鏍囨嬀鍙栤�濊嚜鍔ㄨ幏寰�" + ></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> + <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 { ref, reactive, watch, onMounted } from 'vue'; +import { useFormConfirm } from '@/composables/formConfirm'; +import clueInternalApi from '@/api/clue/clueInternalApi'; + +import MapSearch from '@/components/map/MapSearch.vue'; + +const props = defineProps({ + modelValue: Boolean, + clueData: Object, + create: { + type: Boolean, + default: true + }, + // 鑷畾涔夊垱寤烘柟娉� + onCreate: Function, + // 鑷畾涔夋洿鏂版柟娉� + onUpdate: Function +}); + +const emits = defineEmits(['update:modelValue', 'onSubmit']); + +function handleDialogChange(value) { + emits('update:modelValue', value); +} + +const { formObj, formRef, edit, onSubmit, onCancel, clear } = + useFormConfirm({ + submit: { + do: submit + }, + cancel: { + do: cancel + } + }); +const loading = ref(false); +// 琛ㄥ崟妫�鏌ヨ鍒� +const rules = reactive({ + cclueName: [ + { + required: true, + message: '绾跨储鍚嶇О涓嶈兘涓虹┖', + trigger: 'blur' + } + ], + caddress: [ + { + required: true, + message: '绾跨储鍦板潃涓嶈兘涓虹┖', + trigger: 'blur' + } + ], + coordinate: [ + { + required: true, + message: '绾跨储瀹氫綅涓嶈兘涓虹┖', + trigger: 'blur' + } + ], +}); + +// 瀵硅瘽妗嗙‘璁ゆ搷浣� +function submit() { + const param = getParams(); + return props.create + ? props.onCreate + ? props.onCreate(param) + : createClue(param) + : props.onUpdate + ? props.onUpdate(param) + : updateClue(param); +} + +// 瀵硅瘽妗嗗彇娑堟搷浣� +function cancel() { + emits('update:modelValue', false); +} + +// 鏂板缓鍐呴儴绾跨储 +function createClue(params) { + clueInternalApi + .createInternalClue(params) + .then(() => { + emits('update:modelValue', false); + emits('onSubmit'); + }) + .finally(() => { + loading.value = false; + }); +} + +// 鏇存柊鍐呴儴绾跨储 +function updateClue(params) { + clueInternalApi + .updateInternalClue(params) + .then(() => { + emits('update:modelValue', false); + emits('onSubmit'); + }) + .finally(() => { + loading.value = false; + }); +} + +// 鑾峰彇绾跨储瀵硅薄鍙傛暟 +function getParams() { + const coor = formObj.value.coordinate.split(','); + return { + cid: formObj.value.cid, + cclueName: formObj.value.cclueName, + cconclusion: formObj.value.cconclusion, + caddress: formObj.value.caddress, + clongitude: parseFloat(coor[0]), + clatitude: parseFloat(coor[1]) + }; +} + +// 鍒濆鍖栫嚎绱㈣〃鍗曞璞� +watch( + () => [props.modelValue, props.clueData], + (nV, oV) => { + const [m, d] = nV; + if (m) { + formObj.value = {}; + if (d) { + formObj.value = d; + formObj.value.coordinate = d.cLongitude + ',' + d.cLatitude; + } + } + } +); +/*********************************************************** */ +const mapDialogShow = ref(false); +function openMapDialog() { + mapDialogShow.value = true; +} +function selectAddress(result) { + formObj.value.caddress = result.address; + formObj.value.coordinate = result.gpsLon + ',' + result.gpsLat; +} +</script> diff --git a/src/views/internal-clue/InternalClueLayout.vue b/src/views/internal-clue/InternalClueLayout.vue new file mode 100644 index 0000000..f0c7550 --- /dev/null +++ b/src/views/internal-clue/InternalClueLayout.vue @@ -0,0 +1,45 @@ +<template> + <el-row class="fy-overlay-container" justify="space-between"> + <el-col :span="6"> + <InternalClueManage + @itemSelected="selectClue" + ></InternalClueManage> + </el-col> + <el-col :span="6"> + <ClueReport + v-model:show="show" + :clueData="selectedClue" + @pushed="(e) => (selectedClue.cuploaded = e)" + @onClueTaskChange="handleClueTaskChange" + ></ClueReport> + </el-col> + </el-row> +</template> + +<script setup> +import InternalClueManage from '@/views/internal-clue/InternalClueManage.vue'; +import ClueReport from '@/views/overlay-clue/report/ClueReport.vue'; +import { ref, provide } from 'vue'; + +// 娉ㄥ叆鍙傛暟 + +// 琛ㄦ槑褰撳墠鎿嶄綔鐨勬槸鍐呴儴绾跨储 +provide('isInternal', true); + +const selectedClue = ref(); +const show = ref(false); + +/** + * 閫夋嫨绾跨储浜嬩欢 + */ +const selectClue = function (clue) { + show.value = true; + selectedClue.value = clue; +}; + +function handleClueTaskChange() { + selectedClue.value.taskCount = 1; +} +</script> + +<style scoped></style> diff --git a/src/views/internal-clue/InternalClueManage.vue b/src/views/internal-clue/InternalClueManage.vue new file mode 100644 index 0000000..2cfb406 --- /dev/null +++ b/src/views/internal-clue/InternalClueManage.vue @@ -0,0 +1,127 @@ +<template> + <div class="fy-card"> + <div class="fy-h1">鍐呴儴绾跨储娓呭崟</div> + <div class="fy-flex-row"> + <span>鏃堕棿</span> + <el-date-picker + v-model="updateTime" + type="datetime" + placeholder="閫夋嫨鏃ユ湡鍜屾椂闂�" + /> + <el-button type="primary" @click="getClues">鏌ヨ</el-button> + <el-button type="success" @click="clueDialog = true" + >鏂板缓</el-button + > + </div> + <el-scrollbar height="70vh" class="p-h-1"> + <ClueList :dataList="clueList" @itemSelected="selectClue"> + </ClueList> + </el-scrollbar> + <el-row justify="space-between" class="p-8"> + <el-pagination + size="small" + v-model:current-page="currentPage" + v-model:page-size="pageSize" + :page-sizes="[10, 20, 50, 100]" + :background="true" + layout="total, sizes, pager" + :total="total" + /> + </el-row> + </div> + <InternalClueEdit + v-model="clueDialog" + :create="true" + :onCreate="createInternalClue" + ></InternalClueEdit> + <ClueTaskEdit + v-model="clueTaskDialog" + :create="true" + :onCreate="createInternalTask" + ></ClueTaskEdit> +</template> + +<script setup> +import { ref, onMounted, reactive } from 'vue'; +import moment from 'moment'; + +import clueInternalApi from '@/api/clue/clueInternalApi'; +import clueTaskApi from '@/api/clue/clueTaskApi'; +import { onMapMounted } from '@/components/map/baseMap'; + +import ClueList from '@/views/overlay-clue/list/components/ClueList.vue'; +import ClueTaskEdit from '@/views/overlay-clue/task/ClueTaskEdit.vue'; +import InternalClueEdit from '@/views/internal-clue/InternalClueEdit.vue'; + +const emits = defineEmits('itemSelected'); + +// 涓嬪彂鏃堕棿锛堟瘡娆℃煡璇㈠ぇ浜庢鏃堕棿鐨勬暟鎹級 +const updateTime = ref(); +// 绾跨储娓呭崟 +const clueList = ref([]); +const currentPage = ref(1); +const pageSize = ref(100); +const total = ref(0); + +/** + * 鏌ヨ宸蹭笅鍙戠殑绾跨储娓呭崟 + */ +const getClues = function () { + let sTime; + let eTime; + if (updateTime.value) { + const now = moment(updateTime.value); + sTime = now.format('YYYY-MM-DD HH:mm:ss'); + eTime = now.add(1, 'month').format('YYYY-MM-DD HH:mm:ss'); + } + onMapMounted(() => { + clueInternalApi + .getInternalClue({ + sTime, + eTime, + pageNum: currentPage.value, + pageSize: pageSize.value + }) + .then((res) => { + total.value = res.head.totalCount; + clueList.value = res.data; + }); + }); +}; + +/** + * 閫夋嫨绾跨储浜嬩欢 + */ +const selectClue = function (clue) { + emits('itemSelected', clue); +}; + +onMounted(() => { + getClues(); +}); + +/************************************************************** */ +const clueData = ref(); +const clueDialog = ref(false); +const clueTaskDialog = ref(false); + +function createInternalClue(clue) { + clueData.value = clue; + clueDialog.value = false; + clueTaskDialog.value = true; +} +function createInternalTask(clueTask) { + clueTaskApi + .createClueTaskInternal({ + clueTask: clueTask, + clueInternal: clueData.value + }) + .then((res) => { + if (res.success) { + clueTaskDialog.value = false; + getClues(); + } + }); +} +</script> +<style scoped></style> diff --git a/src/views/overlay-clue/report/ClueReport.vue b/src/views/overlay-clue/report/ClueReport.vue index 3f9ed43..d342985 100644 --- a/src/views/overlay-clue/report/ClueReport.vue +++ b/src/views/overlay-clue/report/ClueReport.vue @@ -2,6 +2,7 @@ <!-- 娓呭崟璇︽儏 --> <CloseButton v-show="show" @close="closeEdit"> <el-button + v-if="!isInternal" class="push-btn" :type="clueData.cuploaded ? 'success' : 'danger'" @click="pushCheck" @@ -76,6 +77,12 @@ import clueTaskApi from '@/api/clue/clueTaskApi'; export default { + inject: { + // 鏄惁鏄唴閮ㄧ嚎绱㈢浉鍏虫搷浣� + isInternal: { + default: false + } + }, components: { ClueReportClue, ClueReportConclusion, @@ -127,7 +134,10 @@ getClueTask() { clueTaskApi - .fetchClueTask({ clueId: this.clueData.cid }) + .fetchClueTask({ + clueId: this.clueData.cid, + internalTask: this.isInternal + }) .then((res) => { this.isCreateMode = res.data.length == 0; if (res.data.length > 0) { diff --git a/src/views/overlay-clue/report/components/ClueReportClue.vue b/src/views/overlay-clue/report/components/ClueReportClue.vue index 6010893..0e83ad2 100644 --- a/src/views/overlay-clue/report/components/ClueReportClue.vue +++ b/src/views/overlay-clue/report/components/ClueReportClue.vue @@ -2,7 +2,12 @@ <!-- 娓呭崟璇︽儏 --> <DescriptionsList title="绾跨储娓呭崟璇︽儏"> <template #extra> - <el-button type="primary" text size="small" @click="openPDF" + <el-button + v-if="!isInternal" + type="primary" + text + size="small" + @click="openPDF" >鏌ョ湅PDF</el-button > </template> @@ -11,8 +16,14 @@ label="绾跨储鍚嶇О" :content="clue.cclueName" /> - <DescriptionsListItem label="鍒涘缓鏃堕棿" :content="$tf(clue.ccreateTime)" /> - <DescriptionsListItem label="涓嬪彂鏃堕棿" :content="$tf(clue.creleaseTime)" /> + <DescriptionsListItem + label="鍒涘缓鏃堕棿" + :content="$tf(clue.ccreateTime)" + /> + <DescriptionsListItem + label="涓嬪彂鏃堕棿" + :content="$tf(clue.creleaseTime)" + /> <DescriptionsListItem label="鎶ヨ绔欑偣" :content="clue.csiteName" @@ -36,6 +47,12 @@ import clueApi from '@/api/clue/clueApi'; export default { + inject: { + // 鏄惁鏄唴閮ㄧ嚎绱㈢浉鍏虫搷浣� + isInternal: { + default: false + } + }, props: { clue: Object }, @@ -46,6 +63,6 @@ '_blank' ); } - }, + } }; </script> diff --git a/src/views/overlay-clue/report/components/ClueReportConclusion.vue b/src/views/overlay-clue/report/components/ClueReportConclusion.vue index f62719f..2e21ce5 100644 --- a/src/views/overlay-clue/report/components/ClueReportConclusion.vue +++ b/src/views/overlay-clue/report/components/ClueReportConclusion.vue @@ -74,10 +74,13 @@ </template> <script setup> -import { reactive, ref, watch, computed } from 'vue'; +import { reactive, ref, watch, computed, inject } from 'vue'; import { useCloned } from '@vueuse/core'; import { useFormConfirm } from '@/composables/formConfirm'; import clueConclusionApi from '@/api/clue/clueConclusionApi'; + +// 鍐冲畾褰撳墠鏄惁鏄唴閮ㄧ嚎绱㈢浉鍏虫搷浣� +const isInternal = inject('isInternal', false); const props = defineProps({ clueId: Number @@ -137,6 +140,7 @@ function submit() { formObj.value.cid = props.clueId; + formObj.value.ccInternal = isInternal; return uploadConclusion(); } function cancel() { @@ -164,11 +168,13 @@ * 鑾峰彇绾跨储缁撹 */ function getConclusion() { - clueConclusionApi.getConclusion(props.clueId).then((res) => { - conclusion.value = res; - formObj.value = res == null ? {} : res; - // formObj.value = useCloned(res, { manual: true }); - }); + clueConclusionApi + .getConclusion(props.clueId, isInternal) + .then((res) => { + conclusion.value = res; + formObj.value = res == null ? {} : res; + // formObj.value = useCloned(res, { manual: true }); + }); } </script> <style scoped></style> diff --git a/src/views/overlay-clue/report/components/ClueReportQuestion.vue b/src/views/overlay-clue/report/components/ClueReportQuestion.vue index ba7598f..35740f2 100644 --- a/src/views/overlay-clue/report/components/ClueReportQuestion.vue +++ b/src/views/overlay-clue/report/components/ClueReportQuestion.vue @@ -6,10 +6,10 @@ <template #extra> <div> <el-button + v-if="!clueData.cuploaded" type="danger" size="small" icon="Delete" - :disabled="clueData.cuploaded" @click="deleteQuestion(item)" ></el-button> <el-button @@ -55,6 +55,7 @@ </div> <QuestionDetail :clueData="clueData" + :uploaded="clueData.cuploaded" v-model:show="dialogShow" :question="selectedQuestion" @on-submit="getQuestion" @@ -62,13 +63,16 @@ </template> <script setup> -import { ref, watch, computed } from 'vue'; +import { ref, watch, computed, inject } from 'vue'; import clueQuestionApi from '@/api/clue/clueQuestionApi'; import QuestionDetail from './QuestionDetail.vue'; import { useMessageBoxTip, useMessageBox } from '@/composables/messageBox'; + +// 鍐冲畾褰撳墠鏄惁鏄唴閮ㄧ嚎绱㈢浉鍏虫搷浣� +const isInternal = inject('isInternal', false); const props = defineProps({ // clueId: Number, @@ -129,9 +133,11 @@ * 鑾峰彇绾跨储缁撹 */ function getQuestion() { - clueQuestionApi.getQuestion(props.clueData.cid).then((res) => { - questionList.value = res; - }); + clueQuestionApi + .getQuestion(props.clueData.cid, isInternal) + .then((res) => { + questionList.value = res; + }); } function pushQuestion(item) { diff --git a/src/views/overlay-clue/report/components/QuestionDetail.vue b/src/views/overlay-clue/report/components/QuestionDetail.vue index f0f6603..7352c04 100644 --- a/src/views/overlay-clue/report/components/QuestionDetail.vue +++ b/src/views/overlay-clue/report/components/QuestionDetail.vue @@ -7,7 +7,11 @@ destroy-on-close > <template #header> - <span> 娣诲姞闂</span> + <span> + {{ + uploaded ? '闂璇︽儏' : createMode ? '娣诲姞闂' : '淇敼闂' + }}</span + > </template> <el-form label-width="90px" @@ -18,19 +22,25 @@ > <el-form-item label="闂鍚嶇О" prop="cqName"> <el-input + :disabled="uploaded" v-model="formObj.cqName" placeholder="璇疯緭鍏ラ棶棰樺悕绉�" ></el-input> </el-form-item> <el-form-item label="闂鎻忚堪" prop="cqDescription"> <el-input + :disabled="uploaded" 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-select + v-model="formObj.cqStreet" + placeholder="鎵�鍦ㄨ闀�" + :disabled="uploaded" + > <el-option v-for="s in streets" :key="s.value" @@ -41,22 +51,29 @@ </el-form-item> <el-form-item label="璇︾粏鍦板潃" prop="cqAddress"> <el-input + :disabled="uploaded" v-model="formObj.cqAddress" placeholder="璇疯緭鍏ュ湴鍧�鎴栬�呴�氳繃鈥滃潗鏍囨嬀鍙栤�濊嚜鍔ㄨ幏寰�" ></el-input> </el-form-item> <el-form-item label="鍧愭爣" prop="coordinate"> <el-input + :disabled="uploaded" 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 + :disabled="uploaded" + plain + type="primary" + @click="openMapDialog" >鍧愭爣鎷惧彇</el-button > </el-form-item> <el-form-item label="闂鍥剧墖" prop="files"> <el-upload + :class="uploadableClz" ref="uploadRef" :file-list="fileList" action="" @@ -64,16 +81,49 @@ list-type="picture-card" name="images" accept="image/png, image/jpeg" - :limit="3" + :limit="maxImageCount" multiple :on-preview="handleFilePreview" :on-remove="handleFileRemove" :on-change="handleFileChange" > <el-icon><Plus /></el-icon> + <template #file="{ file }"> + <div> + <img + class="el-upload-list__item-thumbnail" + :src="file.url" + alt="" + /> + <span class="el-upload-list__item-actions"> + <span + class="el-upload-list__item-preview" + @click="handleFilePreview(file)" + > + <el-icon><zoom-in /></el-icon> + </span> + <!-- <span + v-if="!disabled" + class="el-upload-list__item-delete" + @click="handleDownload(file)" + > + <el-icon><Download /></el-icon> + </span> --> + <span + v-if="!uploaded" + class="el-upload-list__item-delete" + @click="handleFileRemove(file)" + > + <el-icon><Delete /></el-icon> + </span> + </span> + </div> + </template> <template #tip> <div class="el-upload__tip"> - 璇烽�夋嫨灏忎簬500kb鐨刯pg/png鍥剧墖锛屾渶澶�3寮� + {{ + `璇烽�夋嫨灏忎簬500kb鐨刯pg/png鍥剧墖锛屾渶澶�${maxImageCount}寮燻 + }} </div> </template> </el-upload> @@ -99,47 +149,78 @@ ></el-image-viewer> <MapSearch v-model:show="mapDialogShow" + :defaultCoor=" + formObj.coordinate + ? formObj.coordinate.split(',') + : undefined + " @on-submit="selectAddress" ></MapSearch> </template> <script setup> -import { reactive, ref, watch, computed } from 'vue'; +import { reactive, ref, watch, computed, inject } from 'vue'; +import { ElMessage } from 'element-plus'; import { useFormConfirm } from '@/composables/formConfirm'; import { streets } from '@/constant/street'; import clueQuestionApi from '@/api/clue/clueQuestionApi'; +import { $clue } from '@/api/index'; import MapSearch from '@/components/map/MapSearch.vue'; +// 鍐冲畾褰撳墠鏄惁鏄唴閮ㄧ嚎绱㈢浉鍏虫搷浣� +const isInternal = inject('isInternal', false); + const props = defineProps({ - clueId: Number, + // 搴旀�ョ嚎绱㈠璞� clueData: { type: Object, default: () => { return {}; } }, + // 瀵硅瘽妗嗘樉绀烘帶鍒� show: Boolean, + // 绾跨储闂瀵硅薄 question: Object, - create: { + // 闂鏄惁宸蹭笂浼� + uploaded: { type: Boolean, - default: true + default: false + }, + maxImageCount: { + type: Number, + default: 3 } }); const emit = defineEmits(['update:show', 'onSubmit', 'onClose']); + +// 鍒涘缓鎴栨槸淇敼妯″紡 +const createMode = ref(true); // 涓婃姤寮瑰嚭妗� const dialogShow = ref(false); const mapDialogShow = ref(false); const uploadRef = ref(); const fileList = ref([]); +// 鏇存柊妯″紡涓嬶紝璁板綍琚垹闄ょ殑鍘熸湁鍥剧墖 +let deletedFileList = []; +// 鍐冲畾鏄惁鑳戒笂浼犲浘鐗� +const uploadableClz = computed(() => { + return props.uploaded || + (fileList.value && fileList.value.length >= props.maxImageCount) + ? 'question-not-upload' + : ''; +}); const previewShow = ref(false); const initialIndex = ref(0); const urlList = computed(() => - fileList.value.map((value) => { - return value.url; - }) + fileList.value + ? fileList.value.map((value) => { + return value.url; + }) + : [] ); function handleFilePreview(file) { @@ -151,12 +232,22 @@ previewShow.value = false; } -function handleFileRemove(file, fileList) { - formObj.value.files = fileList; +function handleFileRemove(file, files) { + if (!createMode.value) { + if (file.url.indexOf($clue.imgUrl) != -1) { + const originUrl = file.url.replace($clue.imgUrl, ''); + deletedFileList.push(originUrl); + } + } + const index = fileList.value.indexOf(file); + fileList.value.splice(index, 1); + // fileList.value = fileList; + edit.value = true; } -function handleFileChange(file, fileList) { - formObj.value.files = fileList; +function handleFileChange(file, files) { + fileList.value = files; + edit.value = true; } const { formObj, formRef, edit, onSubmit, onCancel, clear } = @@ -230,23 +321,38 @@ }); function submit() { + if (!fileList.value || fileList.value.length == 0) { + ElMessage({ + message: '鑷冲皯涓婁紶涓�寮犲浘鐗�', + type: 'error' + }); + return; + } const coor = formObj.value.coordinate.split(','); const q = { - cid: parseInt(props.clueData.cid), - cqName: formObj.value.cqName, - cqDescription: formObj.value.cqDescription, - cqStreet: formObj.value.cqStreet, - cqAddress: formObj.value.cqAddress, + ...formObj.value, + // cqId: formObj.value.cqId, + cId: parseInt(props.clueData.cid), + // cqName: formObj.value.cqName, + // cqDescription: formObj.value.cqDescription, + // cqStreet: formObj.value.cqStreet, + // cqAddress: formObj.value.cqAddress, cqLongitude: parseFloat(coor[0]), - cqLatitude: parseFloat(coor[1]) + cqLatitude: parseFloat(coor[1]), + cqInternal: isInternal + // cqFilePath: formObj.value.cqFilePath }; const files = []; - if (formObj.value.files) { - formObj.value.files.forEach((f) => { - files.push(f.raw); + if (fileList.value) { + fileList.value.forEach((f) => { + if (f.url.indexOf($clue.imgUrl) == -1) { + files.push(f.raw); + } }); } - return props.create ? uploadQuestion(q, files) : updateQuestion(q, ); + return createMode.value + ? uploadQuestion(q, files) + : updateQuestion(q, files); } function cancel() { @@ -268,12 +374,11 @@ */ function uploadQuestion(question, files) { loading.value = true; - return clueQuestionApi .uploadQuestion(question, files) .then(() => { dialogShow.value = false; - clear(); + // clear(); uploadRef.value.clearFiles(); emit('onSubmit'); }) @@ -282,19 +387,30 @@ }); } -function updateQuestion(question, newFiles, deleteFiles) { - +function updateQuestion(question, newFiles) { + loading.value = true; + const deleteImgUrl = deletedFileList.join(';'); + return clueQuestionApi + .updateQuestion(question, newFiles, deleteImgUrl) + .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({ + fileList.value = question.files.map((f, index) => { + return { name: `${index}`, url: f - }); + }; }); return { ...question }; } @@ -305,16 +421,28 @@ dialogShow.value = val[0]; if (val[0]) { fileList.value = []; + deletedFileList = []; if (val[1]) { + createMode.value = false; formObj.value = parseFormObj(val[1]); } else { + createMode.value = true; formObj.value = {}; } + // edit.value = false } } ); watch(dialogShow, (val) => { + if (!val) { + clear(); + } emit('update:show', val); }); </script> +<style scoped> +:deep(.question-not-upload .el-upload-list > .el-upload) { + display: none; +} +</style> diff --git a/src/views/overlay-clue/task/ClueTaskEdit.vue b/src/views/overlay-clue/task/ClueTaskEdit.vue index 26aca1b..ee4a4dc 100644 --- a/src/views/overlay-clue/task/ClueTaskEdit.vue +++ b/src/views/overlay-clue/task/ClueTaskEdit.vue @@ -85,17 +85,16 @@ const props = defineProps({ modelValue: Boolean, - clueData: { - type: Object, - default: () => { - return {}; - } - }, + clueData: Object, clueTask: Object, create: { type: Boolean, default: true - } + }, + // 鑷畾涔夊垱寤烘柟娉� + onCreate: Function, + // 鑷畾涔夋洿鏂版柟娉� + onUpdate: Function }); const emits = defineEmits(['update:modelValue', 'onSubmit']); @@ -116,13 +115,13 @@ const loading = ref(false); // 琛ㄥ崟妫�鏌ヨ鍒� const rules = reactive({ - clueId: [ - { - required: true, - message: '绾跨储缂栧彿涓嶈兘涓虹┖', - trigger: 'blur' - } - ], + // clueId: [ + // { + // required: true, + // message: '绾跨储缂栧彿涓嶈兘涓虹┖', + // trigger: 'blur' + // } + // ], taskTime: [ { required: true, @@ -155,7 +154,13 @@ function submit() { const param = getParams(); - return props.create ? createClueTask(param) : updateClueTask(param); + return props.create + ? props.onCreate + ? props.onCreate(param) + : createClueTask(param) + : props.onUpdate + ? props.onUpdate(param) + : updateClueTask(param); } function cancel() { -- Gitblit v1.9.3