<!-- 历史任务详情 -->
|
<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"><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">
|
<p>
|
成果数据
|
<span>{{ total }}</span>
|
个
|
</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">
|
<!-- 图片显示 -->
|
<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
|
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>
|
|
<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 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,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';
|
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' },
|
{ name: '任务类型', value: '', field: 'industry_type_str' },
|
{ name: '飞行事件', value: '', field: 'event_number' },
|
{ name: '所属机巢', value: '', field: 'device_names' },
|
{ name: '所属单位', value: '', field: 'dept_name' },
|
{ name: '任务时间', value: '', field: 'cycle_time_value' },
|
{ name: '关联算法', value: '', field: 'ai_type_str' },
|
{ 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,
|
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(['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.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,
|
}
|
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 flightEvents = ref([])
|
const jobTimes = ref([])
|
const getDetails = () => {
|
const params = {
|
wayLineJobInfoId: props.wayLineJodInfoId,
|
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 = '', rep_rule_val = '' } = detailsData?.value || {}
|
item.value = rep_rule_type + ' -- ' + rep_rule_val
|
} 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)
|
)
|
}
|
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
|
})
|
}
|
// 播放
|
const videoRef = ref(null);
|
const currentVideoIndex = ref(-1)
|
const fullscreenVideo = ref(null)
|
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.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()
|
})
|
</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;
|
background-size: 100% 100%;
|
|
span {
|
display: inline-block;
|
margin-left: 30px;
|
font-size: 16px;
|
color: #0282ff;
|
line-height: 20px;
|
text-align: left;
|
margin-bottom: 8px;
|
}
|
}
|
|
.devicetitle {
|
margin-bottom: 16px;
|
background: url('/src/assets/images/task/detailtitle.png') no-repeat center;
|
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;
|
}
|
}
|
}
|
}
|
|
}
|
}
|
p {
|
display: inline-block;
|
margin-left: 30px;
|
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>
|