From 02409bfbe15f22fc3b5dccadabfd860a660a49d9 Mon Sep 17 00:00:00 2001
From: 罗广辉 <guanghui.luo@foxmail.com>
Date: Sat, 11 Oct 2025 10:37:14 +0800
Subject: [PATCH] feat: 名称修改

---
 src/views/job/components/DeviceJobDetails.vue |  433 +++++++++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 360 insertions(+), 73 deletions(-)

diff --git a/src/views/job/components/DeviceJobDetails.vue b/src/views/job/components/DeviceJobDetails.vue
index c1aa005..c2ecc4d 100644
--- a/src/views/job/components/DeviceJobDetails.vue
+++ b/src/views/job/components/DeviceJobDetails.vue
@@ -1,7 +1,7 @@
 <!-- 历史任务详情 -->
 <template>
 	<el-dialog class="ztzf-dialog" append-to-body modal-class="detailsOfHistoricalTasks" v-model="isShow" title="历史任务详情"
-		width="1800" :close-on-click-modal="false" :destroy-on-close="true">
+		:width="pxToRem(1800)" :close-on-click-modal="false" :destroy-on-close="true">
 		<div class="content">
 			<div class="contentLeft">
 				<div class="machineTableDetailsTitle"><span>详情</span></div>
@@ -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,147 @@
 						<span>{{ total }}</span>
 						个
 					</p>
-					<p class="more" @click="jumpMore">更多</p>
+					<div class="rightBox" v-if="total">
+						<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-image
+								v-else
+								:src="item.smallUrl"
+								:preview-src-list="[item.showUrl]"
+								preview-teleported
+								:initial-index="index"
+								fit="cover"
+							/>
 
-						</el-dialog>
+						<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>
+				<el-image-viewer
+					v-if="showViewer"
+					:url-list="previewUrls"
+					:initial-index="activeIndex"
+					@close="showViewer = false"
+				/>
 			</div>
-			<DeviceJobDetailsMap v-if="isShow" class="contentRight" :detailsData="detailsData" />
+
+			<div class="content-right" v-if="isShow">
+				<DeviceJobDetailsMap
+					:detailsData="detailsData"
+					:yuanImages="yuanImages"
+					@showImageeclick="showImageeclick"
+					:jobId="props.jobId"
+				/>
+				<div class="content-map-popups"></div>
+			</div>
 		</div>
 	</el-dialog>
-
+  <!-- 全景360 -->
+  <PanoramaPopup
+    v-if="'全景'"
+    v-model:panoramaParamsShow="panoramaParamsShow"
+    v-model:panoramaParamsUrl="panoramaParamsUrl"
+  />
 </template>
 
 <script setup>
-// import { pxToRem } from '@/utils/rem'
+import PanoramaPopup from '@/components/PanoramaPopup/PanoramaPopup.vue';
+import { getShowImg, getSmallImg, getzsSmallImg,getzsShowImg } from '@/utils/util';
+import { getaiImagesPageAPI,cancelDownloadApi,getDownloadStatusApi,attachDownload,aiImagesPage} 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 +208,42 @@
 // 任务成果
 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, 2, 4, 5],
+    'orderByCreateTime': true,
+    current: 1,
+    size: 2000,
+  }
+	const pageParams = {
+		current: 1,
+		size: 2000,
 	}
-	getEventMediaListApi(params).then(res => {
-		achievementList.value = res.data.data.slice(0, 5)
-		total.value = res.data.data.length
+  aiImagesPage(attachmentsParams ).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 +255,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 +268,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
+
+
+		})
 		})
 		// flystatus.value = res.data.data.ai_type_str
 	})
@@ -169,7 +291,7 @@
 }
 // 计算当前视频URL
 const currentVideoUrl = computed(() => {
-	return achievementList.value[currentVideoIndex.value]?.url || ''
+	return achievementList.value[currentVideoIndex.value]?.link || ''
 })
 // 关闭弹窗处理
 const handleClose = () => {
@@ -178,7 +300,55 @@
 		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
+}
+// 从地图点击图片预览
+const showViewer = ref(false)
+const activeIndex = ref(0)
+const previewUrls = ref([])
+const showImageeclick = (list, index, categoriestype) => {
+	if (categoriestype === 5) {
+		panoramaParamsShow.value = true
+		panoramaParamsUrl.value = list[0]
+	} else {
+		previewUrls.value = list
+		activeIndex.value = 0
+		showViewer.value = true
+	}
+}
+// 取消下载
+function cancelDownload() {
+	cancelDownloadApi({ type:'htlsrwxq' }).then(res =>{
+		ElMessage.success('取消成功')
+	})
+}
 onMounted(() => {
 	getDetails()
 	getAchievement()
@@ -206,8 +376,7 @@
 .content {
 	display: flex;
 	height: 100%;
-
-	.contentLeft {
+.contentLeft {
 		margin-left: 35px;
 		margin-right: 24px;
 		width: 1150px;
@@ -222,7 +391,7 @@
 				display: inline-block;
 				margin-left: 30px;
 				font-size: 16px;
-				color: #363636;
+				color: #0282ff;
 				line-height: 20px;
 				text-align: left;
 				margin-bottom: 8px;
@@ -236,7 +405,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: 28px !important;
+								border-radius: 0 !important;
+								background: transparent !important;
+
+								.el-progress-bar__inner{
+									border-radius: 0 !important;
+								}
+							}
+						}
+					}
+
+				}
+			}
 			p {
 				display: inline-block;
 				margin-left: 30px;
@@ -279,36 +493,36 @@
 				.itemBox {
 					display: flex;
 					align-items: center;
-					// background: #152743;
+					background: #fff;
 					height: 43px;
-					// border-top: 2px solid #eeeeee;
+					border-top: 2px solid #EFEFEF;
 
 				}
 
 			}
 
 			.itemCon:nth-last-child(-n+2) {
-				border-bottom: 2px solid #eeeeee;
+				border-bottom: 2px solid #EFEFEF;
 			}
 
 			.itemBox:nth-last-child(-n+2) .itemTitle {
-				border-bottom: 1px solid #eeeeee;
+				border-bottom: 1px solid #EFEFEF;
 			}
 
 			.itemTitle {
-				color: #363636;
+				color: #0B1D38;
 				width: 26%;
 				text-align: right;
-				// background: #0B1D38;
+				background: #F3F6FF;
 				height: 43px;
 				line-height: 43px;
 				padding-right: 10px;
-				border-top: 1px solid #eeeeee
+				border-top: 1px solid #EFEFEF;
 			}
 
 			.itemValue {
 				font-size: 14px;
-				color: #363636;
+				color: #747e91;
 				text-align: center;
 				width: 74%;
 
@@ -334,8 +548,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;
@@ -348,34 +646,23 @@
 			position: relative;
 		}
 
-		.videobutton {
-			position: absolute;
-			top: 40%;
-			left: 45%;
-			transform: translate(-50%);
-			cursor: pointer;
-			width: 40px;
-			height: 40px;
-			display: inline-block;
-		}
-
 	}
 
-	.contentRight {
+		.content-right {
+		position: relative;
 		width: 0;
 		flex-grow: 1;
 		background: #19ad8d;
 		margin-right: 17px;
+		overflow: hidden;
+
+		.content-map-popups {
+			pointer-events: none;
+
+			> * {
+				pointer-events: auto !important;
+			}
+		}
 	}
-
-
-
-	// .fullscreen-video {
-	//   width: 100%;
-	//   height: 100%;
-	//   max-width: 800px;
-	//   max-height: 800px;
-	//   object-fit: contain;
-	// }
 }
 </style>

--
Gitblit v1.9.3