From 73e5d0d8bd018cde09c541988d82e6bbb880f133 Mon Sep 17 00:00:00 2001
From: 张含笑 <zhx18749296735@163.com>
Date: Wed, 13 Aug 2025 09:08:19 +0800
Subject: [PATCH] feat:成果数据展示所有数据

---
 src/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue |    4 
 src/api/job/task.js                                                          |    7 +
 src/views/job/components/DeviceJobDetailsMap.vue                             |   14 -
 src/views/job/components/DeviceJobDetails.vue                                |  358 ++++++++++++++++++++++++++++++++++++++++++++-------
 4 files changed, 320 insertions(+), 63 deletions(-)

diff --git a/src/api/job/task.js b/src/api/job/task.js
index fda4ed7..b1eb8fb 100644
--- a/src/api/job/task.js
+++ b/src/api/job/task.js
@@ -239,3 +239,10 @@
 		},
 	})
 }
+export const getJobsAllFiles = data => {
+	return request({
+		url: `/blade-resource/attach/aiImagesPage?current=1&size=9999`,
+		method: 'post',
+		data: data,
+	})
+}
\ No newline at end of file
diff --git a/src/views/job/components/DeviceJobDetails.vue b/src/views/job/components/DeviceJobDetails.vue
index d31673a..3b0e909 100644
--- a/src/views/job/components/DeviceJobDetails.vue
+++ b/src/views/job/components/DeviceJobDetails.vue
@@ -11,7 +11,7 @@
 							<div class="itemBox">
 								<div class="itemTitle">{{ item.name }}:</div>
 								<div truncated class="itemValue" v-if="item.name !== '飞行事件' && item.name !== '任务执行次数'">{{
-		item.value ? item.value : '--' }}</div>
+										item.value ? item.value : '--' }}</div>
 								<div class="itemValue" v-if="item.name === '任务执行次数'">{{ item.value ? item.value : '0'
 									}}次</div>
 								<!-- 飞行事件 -->
@@ -34,49 +34,133 @@
 						<span>{{ total }}</span>
 						个
 					</p>
-					<p class="more" @click="jumpMore">更多</p>
+					<div class="rightBox">
+						<div class="downloadBtn" @click="htlsrwxq === 100 && downloadFun()">
+							<el-progress v-if="htlsrwxq !== 100" :percentage="htlsrwxq" :show-text="false" striped striped-flow :duration="1" />
+							<div class="downloadBtnText">
+								<span v-if="htlsrwxq === 100">下载</span>
+								<template v-else>
+									处理中<el-icon @click="cancelDownload"><CircleClose /></el-icon>
+								</template>
+							</div>
+						</div>
+						<div class="downloadBtn" @click="showAll = !showAll" v-if="total > 5">
+							{{ showAll ? '收起' : '更多' }}
+						</div>
+					</div>
 				</div>
 				<div class="imgListBox">
 					<!-- 图片显示 -->
-					<!-- :preview-src-list="achievementList.map(i => i.url)" -->
-					<template v-for="(item, index) in achievementList" :key="index">
-						<el-image v-if="item.result_type === 0" class="imgItem" :src="getSmallImg(item.url)"
-							:preview-src-list="achievementList.map(i => i.url)" show-progress preview-teleported
-							:initial-index="index" fit="cover" />
-						<div v-if="item.result_type === 1" class="videotime">
-							<video ref="videoRef" class="videoDisplay" :src="item.url"></video>
-							<img @click="enterFullScreen(index)" class="videobutton"
-								src="@/assets/images/task/videoshow.png" alt="">
-						</div>
-						<el-dialog class="ztzf-dialog " append-to-body modal-class="detailsOfHistoricalTasks"
-							v-model="VideoShow" width="1600" :close-on-click-modal="false"
-							:destroy-on-close="true">
-							<div class="fullscreen">
-
-								<video ref="fullscreenVideo" class="fullscreen-video" :src="currentVideoUrl"
-									:style="{ width: 1600, height: '80vh' }" controls autoplay></video>
+					<template v-for="(item, index) in achievementList.slice(0, visibleCount)" :key="index">
+					<div class="result-item">
+						<el-checkbox v-model="item.checked" />
+						<div class="itemName">{{ item.createTime }}</div>
+						<!-- 正射 -->
+							<el-image
+								v-if="item.resultType === 4"
+								:src="item.smallUrl"
+								:preview-src-list="[item.showUrl]"
+								fit="cover"
+								preview-teleported
+							/>
+							<!-- 全景 -->
+							<img
+								v-else-if="item.resultType === 5"
+								class="quanjing"
+								@click="clickpanorama(item)"
+								:src="item?.smallUrl"
+								alt=""
+							/>
+						<!-- 视频 -->
+							<div v-else-if="item.resultType === 1" class="videotime">
+								<img
+									class="videoDisplay"
+									:src="convertVideoUrlToThumbnail(item.link)"
+									alt=""
+									@click="enterFullScreen(index)"
+								/>
+								<img
+									@click="enterFullScreen(index)"
+									class="videobutton"
+									src="@/assets/images/task/videoshow.png"
+									alt=""
+								/>
 							</div>
-
-						</el-dialog>
+								<!-- 图片 -->
+							<el-image
+								v-else
+								:src="item.smallUrl"
+								:preview-src-list="[item.showUrl]"
+								preview-teleported
+								:initial-index="index"
+								fit="cover"
+							/>
+						
+						<el-dialog
+              class="ztzf-dialog"
+              append-to-body
+              modal-class="detailsOfHistoricalTasks"
+              v-model="VideoShow"
+              :width="pxToRem(1600)"
+              :close-on-click-modal="false"
+              :destroy-on-close="true"
+			>
+				<div class="fullscreen">
+					<video
+						ref="fullscreenVideo"
+						class="fullscreen-video"
+						:src="currentVideoUrl"
+						:style="{ width: pxToRem(1600), height: '80vh' }"
+						controls
+						autoplay
+					></video>
+				</div>
+			</el-dialog>
+						</div>
+					
 					</template>
 
 				</div>
 			</div>
-			<DeviceJobDetailsMap v-if="isShow" class="contentRight" :detailsData="detailsData" />
+			<DeviceJobDetailsMap v-if="isShow" :yuanImages="yuanImages" class="contentRight" :detailsData="detailsData" />
+			
 		</div>
 	</el-dialog>
-
+  <!-- 全景360 -->
+  <PanoramaPopup
+    v-if="'全景'"
+    v-model:panoramaParamsShow="panoramaParamsShow"
+    v-model:panoramaParamsUrl="panoramaParamsUrl"
+  />
 </template>
 
 <script setup>
+import PanoramaPopup from '@/components/PanoramaPopup/PanoramaPopup.vue';
+import { getShowImg, getSmallImg, getzsSmallImg,getzsShowImg } from '@/utils/util';
+import { getaiImagesPageAPI,cancelDownloadApi,getDownloadStatusApi,attachDownload} from '@/api/dataCenter/dataCenter';
 import { pxToRem } from '@/utils/rem'
 import JobRelatedEvents from './JobRelatedEvents.vue'
-import { getEventMediaListApi, getJobDetails, getJobInfoFiles } from '@/api/job/task'
+import { getEventMediaListApi, getJobDetails, getJobInfoFiles,getJobsAllFiles } from '@/api/job/task'
 import DeviceJobDetailsMap from './DeviceJobDetailsMap.vue'
 import { droneEventList } from '../const/drc'
-import { ElMessage } from 'element-plus'
+import { ElMessage,ElLoading } from 'element-plus'
+import { useStore } from 'vuex';
+const store = useStore();
+const htlsrwxq = computed(() => store.state.common.downloadProgress?.htlsrwxq || 100)
 const isShow = defineModel('show')
 const VideoShow = ref(false)
+const showAll = ref(false) // 是否展示全部
+let loadingData
+// 原图
+const yuanImages = ref([])
+function convertVideoUrlToThumbnail(videoUrl) {
+	// 检查是否是有效的视频URL
+	if (!videoUrl || typeof videoUrl !== 'string') {
+		return videoUrl
+	}
+	// 替换文件扩展名
+	return videoUrl.replace(/\.mp4$/, '_small.jpg')
+}
 const infoList = ref([
 	{ name: '任务编号', value: '', field: 'job_info_num' },
 	{ name: '任务名称', value: '', field: 'name' },
@@ -110,26 +194,40 @@
 // 任务成果
 const total = ref(0)
 const achievementList = ref([])
-const props = defineProps(['wayLineJodInfoId', 'waylineJobId', 'batchNo'])
+const props = defineProps(['wayLineJodInfoId', 'waylineJobId', 'batchNo','jobId'])
 const wayLineJodInfoId = computed(() => props.wayLineJodInfoId)
 provide('wayLineJodInfoId', wayLineJodInfoId)
+// 可见数量
+const visibleCount = computed(() =>
+	showAll.value ? achievementList.value.length : Math.min(5, achievementList.value.length)
+)
 const getAchievement = () => {
-	if (!props.wayLineJodInfoId) return
-	const params = {
-		"way_line_jod_info_id": props.wayLineJodInfoId,
-		"batch_no": props.batchNo
+	if (!props.jobId) return
+	
+	const attachmentsParams = {
+		'wayLineJobId': props.jobId,
+		'resultTypes': [0, 1, 3, 4, 5],
+		'orderByCreateTime': true,
 	}
-	getEventMediaListApi(params).then(res => {
-		achievementList.value = res.data.data.slice(0, 5)
-		total.value = res.data.data.length
+	const pageParams = {
+		current: 1,
+		size: 2000,
+	}
+		getaiImagesPageAPI(attachmentsParams,pageParams ).then(res => {
+		achievementList.value = res.data.data.records.map(i => ({
+			...i,
+			checked: false,
+			smallUrl: i.resultType === 4 ? getzsSmallImg(i.link) : getSmallImg(i.link),
+			showUrl: i.resultType === 4 ? getzsShowImg(i.link) : getShowImg(i.link),
+		}))
+		total.value = res.data.data.total
+		console.log('成果数据', res.data.data)
 	})
 }
 const jumpMore = () => {
 	ElMessage.warning('正在加急开发中...')
 }
-const getSmallImg = url => {
-	return url ? url.substring(0, url.lastIndexOf('.')) + '_small' + url.substring(url.lastIndexOf('.')) : ''
-}
+
 const flightEvents = ref([])
 const jobTimes = ref([])
 const getDetails = () => {
@@ -141,7 +239,6 @@
 	getJobDetails(params).then(res => {
 		detailsData.value = res.data.data
 		jobTimes.value = res.data.data.job_times
-
 		infoList.value.forEach(item => {
 			if (item.name === '任务频次') {
 				const { rep_rule_type = '', rep_rule_val = '' } = detailsData?.value || {}
@@ -155,6 +252,15 @@
 					droneEventList.filter(({ value }) => actionActuatorFunc === value)
 				)
 			}
+				getJobsAllFiles({
+			wayLineJobId: detailsData.value.way_lines.map(item => item.job_id).join(','),
+			resultTypes: [0, 2, 4, 5],
+		}).then(result => {
+			if (result.data.code !== 200) return
+			yuanImages.value = result.data.data.records
+			console.log('ssssss',yuanImages.value);
+			
+		})
 		})
 		// flystatus.value = res.data.data.ai_type_str
 	})
@@ -169,7 +275,7 @@
 }
 // 计算当前视频URL
 const currentVideoUrl = computed(() => {
-	return achievementList.value[currentVideoIndex.value]?.url || ''
+	return achievementList.value[currentVideoIndex.value]?.link || ''
 })
 // 关闭弹窗处理
 const handleClose = () => {
@@ -178,7 +284,41 @@
 		fullscreenVideo.value.currentTime = 0
 	}
 }
-
+// 下载
+const downloadFun = async () => {
+	const list = achievementList.value.filter(i => i.checked)	
+	console.log('list',list);
+	if (!list?.length) return ElMessage.warning('请选择文件')
+	if (list.length === 1) {
+		list.forEach((item, index) => {
+			const suffix = item.url.split('.').pop()
+			aLinkDownloadUtil(item.url, item.nickName + '.' + suffix)
+		})
+	} else {
+		loadingData = ElLoading.service({ background: 'rgba(0, 0, 0, 0.5)', text: '打包中,请稍等...' })
+		const fileIds = list.map(i => i.id)
+		const res = await getDownloadStatusApi({type: 'htlsrwxq'})
+		if (!['CANCELLED','COMPLETED'].includes(res.data.data?.status || 'COMPLETED')){
+			return ElMessage.warning('还有正在处理的')
+		}
+		attachDownload({ attachIds: fileIds,type:'htlsrwxq' }).finally(()=>{
+			loadingData.close()
+		})
+	}
+}
+// 全景预览
+const panoramaParamsShow = ref(false)
+const panoramaParamsUrl = ref(null)
+const clickpanorama = val => {
+	panoramaParamsShow.value = true
+	panoramaParamsUrl.value = val.link
+}
+// 取消下载
+function cancelDownload() {
+	cancelDownloadApi({ type:'htlsrwxq' }).then(res =>{
+		ElMessage.success('取消成功')
+	})
+}
 onMounted(() => {
 	getDetails()
 	getAchievement()
@@ -235,7 +375,52 @@
 			display: flex;
 			justify-content: space-between;
 			align-content: center;
+.rightBox{
+				display: flex;
+				align-items: center;
+				padding-right: 10px;
+				gap: 0 10px;
 
+				.downloadBtn{
+					position: relative;
+					display: flex;
+					align-items: center;
+					justify-content: center;
+					height: 28px;
+					padding: 0 15px;
+					background: rgba(28, 92, 255, 0.14);
+					border-radius: 4px 4px 4px 4px;
+					border: 1px solid #1c5cff;
+					cursor: pointer;
+					    color: #1C5CFF;
+
+					.downloadBtnText{
+						display: flex;
+						align-items: center;
+						gap: 5px;
+						z-index: 5;
+					}
+					.el-progress{
+						position: absolute;
+						left: 0;
+						top: 0;
+						width: 100%;
+
+						:deep(){
+							.el-progress-bar__outer{
+								height: 26px !important;
+								border-radius: 0 !important;
+								background: transparent !important;
+
+								.el-progress-bar__inner{
+									border-radius: 0 !important;
+								}
+							}
+						}
+					}
+
+				}
+			}
 			p {
 				display: inline-block;
 				margin-left: 30px;
@@ -333,8 +518,92 @@
 			grid-template-columns: repeat(5, 1fr);
 			gap: 10px;
 			margin-bottom: 49px;
+
+			> * {
+				width: 100%;
+				height: 200px;
+				position: relative;
+				border-radius: 0px 0px 4px 4px;
+				overflow: hidden;
+			}
+			:deep(.el-checkbox__inner) {
+				width: 17px !important;
+				height: 17px !important;
+				background: rgba(37, 36, 36, 0.63) !important;
+				border-radius: 4px 4px 4px 4px;
+				border: 1px solid #ffffff !important;
+			}
+			.el-checkbox {
+				position: absolute;
+				top: 0;
+				left: 6px;
+			}
+			:deep(.el-checkbox__inner:after) {
+				position: absolute;
+				left: 50% !important;
+				top: 40% !important;
+				transform: translate(-50%, -50%) rotate(45deg) !important;
+			}
+			:deep(.el-checkbox.is-checked .el-checkbox__inner) {
+				background-color: #09297b !important;
+				border-color: #1c5cff !important;
+			}
+			.el-image {
+				width: 100%;
+				height: 100%;
+				cursor: pointer;
+				position: relative;
+			}
+
+			.quanjing {
+				width: 100%;
+				height: 100%;
+				object-fit: cover;
+				cursor: pointer;
+				position: relative;
+			}
+
+			.videotime {
+				width: 100%;
+				height: 100%;
+				position: relative;
+
+				.videoDisplay {
+					width: 100%;
+					height: 100%;
+					object-fit: cover;
+				}
+			}
+			.itemName {
+				position: absolute;
+				bottom: 0;
+				left: 0;
+				height: 23px;
+				width: 100%;
+				background: rgba(22, 22, 22, 0.68);
+				border-radius: 0px 0px 4px 4px;
+				font-family: Source Han Sans CN, Source Han Sans CN;
+				font-weight: 400;
+				font-size: 14px;
+				color: #ffffff;
+				line-height: 23px;
+				z-index: 9;
+				padding: 0 6px;
+			}
 		}
 
+.videobutton {
+			position: absolute;
+			top: 40%;
+			left: 50%;
+			transform: translate(-50%);
+			cursor: pointer;
+			width: 40px;
+			height: 40px;
+			display: inline-block;
+			background: #999;
+			border-radius: 50%;
+		}
 		.videoDisplay {
 			display: flex;
 			flex-wrap: wrap;
@@ -346,17 +615,6 @@
 		.videotime {
 			position: relative;
 		}
-
-		// .videobutton {
-		// 	position: absolute;
-		// 	top: 40%;
-		// 	left: 45%;
-		// 	transform: translate(-50%);
-		// 	cursor: pointer;
-		// 	width: 40px;
-		// 	height: 40px;
-		// 	display: inline-block;
-		// }
 
 	}
 	.contentRight {
diff --git a/src/views/job/components/DeviceJobDetailsMap.vue b/src/views/job/components/DeviceJobDetailsMap.vue
index 1c6cdb5..2bc98ee 100644
--- a/src/views/job/components/DeviceJobDetailsMap.vue
+++ b/src/views/job/components/DeviceJobDetailsMap.vue
@@ -1,13 +1,3 @@
-<!--
- * @Author       : yuan
- * @Date         : 2025-05-26 17:30:33
- * @LastEditors  : yuan
- * @LastEditTime : 2025-06-10 15:46:18
- * @FilePath     : \src\views\job\components\DeviceJobDetailsMap.vue
- * @Description  : 
- * Copyright 2025 OBKoro1, All Rights Reserved. 
- * 2025-05-26 17:30:33
--->
 <template>
 	<div id="deviceJobDetailsMap" class="ztzf-cesium">
 		<PlanarRouteLineList :curRouteLineData="curRouteLineData" @routeLineListClick="routeLineListClick" />
@@ -18,8 +8,8 @@
 
 import { PublicCesium } from '@/utils/cesium/publicCesium'
 
-const props = defineProps(['detailsData'])
-
+const props = defineProps(['detailsData','yuanImages'])
+console.log('props.yua',props.yuanImages);
 import { useRouteLine } from '@/hooks/useRouteLine/useRouteLine.js'
 
 // 加载航线hook
diff --git a/src/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue b/src/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue
index 8a16eff..c8e910a 100644
--- a/src/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue
+++ b/src/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue
@@ -104,7 +104,7 @@
 	/> -->
 	<!-- 历史任务详情 -->
 	<DeviceJobDetails v-if="isShowDeviceJobDetails" v-model:show="isShowDeviceJobDetails" :wayLineJodInfoId="rowData.id"
-		:batchNo="rowData.batch_no" />
+		:batchNo="rowData.batch_no" :jobId="jobId"/>
 	<CancelTaskDialog ref="cancelTaskDialogRef" v-model:isShowCancelTask="isShowCancelTask" :row-data="rowData"
 		@refresh="getJobList" />
 </template>
@@ -166,10 +166,12 @@
 
 // 查看当前任务详情 如果是一台机则显示详情 如果是多台机则进入集群调度(暂未开发)
 let rowData = ref({})
+const jobId = ref('')
 const handleDetail = row => {
 	if (!row.device_sns.length) return ElMessage.warning('没有device_sns')
 	if (row.device_sns.length !== 1) return ElMessage.success('即将跳转到集群调度')
 	rowData.value = row ? row : {}
+jobId.value = rowData.value?.job_id
 	if (row.status === 2 || row.status === 1) {
 		// isShowCurrentTaskDetails.value = true
 		// 跳转大屏当前任务详情

--
Gitblit v1.9.3