<!-- 历史任务详情 -->
|
<template>
|
<el-dialog class="ztzf-dialog" append-to-body modal-class="detailsOfHistoricalTasks" v-model="isShow" title="历史任务详情"
|
:width="pxToRem(1800)" :close-on-click-modal="false" :destroy-on-close="true">
|
<div class="content">
|
<div class="contentLeft">
|
<div class="machineTableDetailsTitle"><img src="/src/assets/images/task/sign.svg" alt=""><span>详情</span></div>
|
<div class="infoBox">
|
<div class="itemBoxLeft">
|
<div v-for="(item, index) in infoList" :key="index" class="itemCon">
|
<div class="itemBox">
|
<div class="itemTitle">{{ item.name }}:</div>
|
<div truncated class="itemValue" v-if="item.name !== '飞行事件' && item.name !== '任务执行次数'">{{
|
item.value ? item.value : '--' }}</div>
|
<div class="itemValue" v-if="item.name === '任务执行次数'">{{ item.value ? item.value : '0'
|
}}次</div>
|
<!-- 飞行事件 -->
|
<div class="flightEvents" v-if="item.name === '飞行事件'">
|
<template v-if="flightEvents.length">
|
<img v-for="(item, index) in flightEvents" alt="" :src="item.img" :title="item.name"
|
:key="index"></img>
|
</template>
|
<div class="itemValue" v-else>--</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
|
</div>
|
<JobRelatedEvents :jobTimes="jobTimes" :batchNo="batchNo" />
|
<div class="devicetitle" v-if="isShow">
|
<div>
|
<img src="/src/assets/images/task/sign.svg" alt="">
|
<p>
|
成果数据
|
<span>{{ total }}</span>
|
个
|
</p>
|
</div>
|
<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">
|
<!-- 图片显示 -->
|
<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" />
|
|
|
</div>
|
|
</template>
|
|
</div>
|
<el-image-viewer v-if="showViewer" :url-list="previewUrls" :initial-index="activeIndex"
|
@close="showViewer = false" />
|
</div>
|
|
<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>
|
<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(1567), height: '80vh' }" controls preload="auto" @play="handleVideoPlay"
|
@ended="handleVideoEnded"></video>
|
</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, aLinkDownloadUtil } 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, getJobsAllFiles } from '@/api/job/task'
|
import DeviceJobDetailsMap from './DeviceJobDetailsMap.vue'
|
import { droneEventList } from '../const/drc'
|
import { ElMessage, ElLoading } from 'element-plus'
|
import { useStore } from 'vuex'
|
import EventBus from '@/utils/eventBus'
|
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 fullscreenVideo = ref(null)
|
// 视频播放事件处理
|
const handleVideoPlay = (event) => {
|
if (event.target.playbackRate !== 0.75) {
|
event.target.playbackRate = 0.75
|
|
}
|
}
|
const handleVideoEnded = index => {
|
// 获取当前视频
|
const video = fullscreenVideo.value
|
|
// 重置视频播放时间为 0
|
video.currentTime = 0
|
|
// 重新加载视频
|
video.load()
|
}
|
// 原图
|
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' },
|
{ name: '任务类型', value: '', field: 'industry_type_str' },
|
{ name: '飞行事件', value: '', field: 'event_number' },
|
{ name: '所属机巢', value: '', field: 'device_names' },
|
{ name: '创建人', value: '', field: 'creator_name' },
|
{ name: '所属部门', value: '', field: 'dept_name' },
|
{ name: '任务时间', value: '', field: 'cycle_time_value' },
|
{ name: '关联算法', value: '', field: 'ai_type_str' },
|
{ name: '自定义识别区', value: '', field: 'enable_custom_area' },
|
{ name: '任务描述', value: '', field: 'remark' },
|
// { name: '任务执行次数', value: '', field: 'job_num' },
|
])
|
// 机巢状态
|
const flystatus = ref('')
|
const detailsData = ref({
|
id: null,
|
remark: null,
|
is_monitoring: null,
|
industry_type_str: null,
|
area_code: null,
|
ai_type_str: null,
|
enable_custom_area: null,
|
begin_time: null,
|
end_time: null,
|
device_names: null,
|
create_time: null,
|
name: null,
|
event_number: null,
|
creator_name: null,
|
way_lines: [],
|
})
|
// 任务成果
|
const total = ref(0)
|
const achievementList = ref([])
|
const props = defineProps(['wayLineJobInfoId', 'waylineJobId', 'batchNo', 'jobId'])
|
const wayLineJobInfoId = computed(() => props.wayLineJobInfoId)
|
provide('wayLineJobInfoId', wayLineJobInfoId)
|
// 可见数量
|
const visibleCount = computed(() =>
|
showAll.value ? achievementList.value.length : Math.min(5, achievementList.value.length)
|
)
|
|
const jumpMore = () => {
|
ElMessage.warning('正在加急开发中...')
|
}
|
|
const flightEvents = ref([])
|
const jobTimes = ref([])
|
const getDetails = () => {
|
const params = {
|
wayLineJobInfoId: props.wayLineJobInfoId,
|
waylineJobId: props.waylineJobId,
|
batchNo: props.batchNo
|
}
|
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 = '', final_cycle_frequency = '' } = detailsData?.value || {}
|
item.value = rep_rule_type ? final_cycle_frequency : '1次'
|
} else {
|
item.value = detailsData.value?.[item.field] || '--'
|
}
|
if (item.name === '飞行事件') {
|
const { action_modes = [] } = detailsData?.value || {}
|
flightEvents.value = action_modes.flatMap(({ actionActuatorFunc }) =>
|
droneEventList.filter(({ value }) => actionActuatorFunc === value)
|
)
|
}
|
if (item.name === '自定义识别区') {
|
item.value = res.data.data.enable_custom_area ? '是' : '否'
|
}
|
|
})
|
|
getJobsAllFiles({
|
wayLineJobId: detailsData.value.way_lines.map(item => item.job_id).join(','),
|
resultTypes: [0, 1, 2, 4, 5],
|
orderByCreateTime: true,
|
}).then(res => {
|
if (res.data.code !== 200) return
|
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
|
|
yuanImages.value = res.data.data.records.filter(item => item.resultType !== 1)
|
})
|
// flystatus.value = res.data.data.ai_type_str
|
})
|
}
|
// 播放
|
const videoRef = ref(null)
|
const currentVideoIndex = ref(-1)
|
|
const enterFullScreen = (index) => {
|
currentVideoIndex.value = index
|
VideoShow.value = true
|
}
|
// 计算当前视频URL
|
const currentVideoUrl = computed(() => {
|
return achievementList.value[currentVideoIndex.value]?.link || ''
|
})
|
// 关闭弹窗处理
|
const handleClose = () => {
|
if (fullscreenVideo.value) {
|
fullscreenVideo.value.pause()
|
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.link.split('.').pop()
|
aLinkDownloadUtil(item.link, 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('取消成功')
|
}).catch(e => {
|
EventBus.emit('useGlobalWS-messageHandler', { biz_code: 'DOWNLOAD_PROGRESS', data: { status: 'CANCELLED', type: 'htlsrwxq' } })
|
})
|
}
|
onMounted(() => {
|
getDetails()
|
})
|
</script>
|
|
<style lang="scss">
|
.detailsOfHistoricalTasks {
|
.el-dialog {
|
--el-dialog-margin-top: 5vh;
|
}
|
|
.el-pagination {
|
justify-content: center;
|
padding: 20px;
|
}
|
|
.el-dialog__body {
|
height: 80vh;
|
}
|
}
|
</style>
|
|
<style lang="scss" scoped>
|
.content {
|
display: flex;
|
height: 100%;
|
|
.contentLeft {
|
margin-left: 35px;
|
margin-right: 24px;
|
width: 1150px;
|
overflow: auto;
|
|
.machineTableDetailsTitle {
|
margin-bottom: 10px;
|
// background: url('/src/assets/images/task/detailtitle.png') no-repeat center;
|
border-bottom: 2px solid #e4e7ed;
|
background-size: 100% 100%;
|
|
img {
|
width: 15px;
|
height: 15px;
|
}
|
|
span {
|
display: inline-block;
|
margin-left: 10px;
|
font-size: 16px;
|
// color: #0282ff;
|
color: #303133;
|
line-height: 20px;
|
text-align: left;
|
margin-bottom: 8px;
|
}
|
}
|
|
.devicetitle {
|
margin-bottom: 16px;
|
// background: url('/src/assets/images/task/detailtitle.png') no-repeat center;
|
border-bottom: 2px solid #e4e7ed;
|
background-size: 100% 100%;
|
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;
|
}
|
}
|
}
|
}
|
|
}
|
}
|
|
img {
|
width: 15px;
|
height: 15px;
|
}
|
|
p {
|
display: inline-block;
|
margin-left: 10px;
|
font-size: 16px;
|
color: #363636;
|
line-height: 20px;
|
text-align: left;
|
margin-bottom: 8px;
|
|
span {
|
font-size: 26px;
|
color: #0282ff;
|
font-weight: bold;
|
}
|
}
|
|
.more {
|
color: #2f9dff;
|
font-size: 14px;
|
padding-top: 5px;
|
cursor: pointer;
|
}
|
}
|
|
.infoBox {
|
display: flex;
|
justify-content: space-between;
|
|
.itemBoxLeft {
|
flex: 1;
|
display: grid;
|
grid-template-columns: repeat(2, 1fr);
|
column-gap: 10px;
|
/* 只设置列间距 */
|
row-gap: 0;
|
/* 取消行间距 */
|
padding: 10px;
|
font-size: 14px;
|
|
.itemBox {
|
display: flex;
|
align-items: center;
|
background: #fff;
|
height: 43px;
|
border-top: 2px solid #EFEFEF;
|
|
}
|
|
}
|
|
.itemCon:nth-last-child(-n+2) {
|
border-bottom: 2px solid #EFEFEF;
|
}
|
|
.itemBox:nth-last-child(-n+2) .itemTitle {
|
border-bottom: 1px solid #EFEFEF;
|
}
|
|
.itemTitle {
|
color: #0B1D38;
|
width: 26%;
|
text-align: right;
|
background: #F3F6FF;
|
height: 43px;
|
line-height: 43px;
|
padding-right: 10px;
|
border-top: 1px solid #EFEFEF;
|
}
|
|
.itemValue {
|
font-size: 14px;
|
color: #747e91;
|
text-align: center;
|
width: 74%;
|
|
}
|
|
.flightEvents {
|
display: flex;
|
flex-wrap: wrap;
|
gap: 10px 10px;
|
width: 74%;
|
justify-content: center;
|
|
img {
|
width: 30px;
|
height: 30px;
|
}
|
}
|
|
}
|
|
.imgListBox {
|
display: grid;
|
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;
|
width: 200px;
|
height: 200px;
|
|
}
|
|
.videotime {
|
position: relative;
|
}
|
|
}
|
|
.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;
|
}
|
}
|
}
|
}
|
</style>
|