| | |
| | | <el-table-column label="线索缩略图" width="120"> |
| | | <template v-slot="{ row }"> |
| | | <el-image |
| | | v-if="row.attachmentType === 1 && row.resultUrl" |
| | | :src="row.resultUrl" |
| | | :preview-src-list="[row.resultUrl]" |
| | | v-if="row.attachmentType === 1 || row.attachmentType === 2" |
| | | :src="row.attachmentType === 1 ? row.resultUrl : getAiImg(row.resultUrl)" |
| | | :preview-src-list="[row.attachmentType === 1 ? row.resultUrl : getAiImg(row.resultUrl)]" |
| | | fit="cover" |
| | | style="width: 80px; height: 80px; border-radius: 4px" |
| | | preview-teleported |
| | | /> |
| | | <el-image |
| | | v-if="row.attachmentType === 2 && row.resultUrl" |
| | | :src="row.resultUrl" |
| | | :preview-src-list="[row.resultUrl]" |
| | | fit="cover" |
| | | style="width: 80px; height: 80px" |
| | | preview-teleported |
| | | /> |
| | | </template> |
| | |
| | | import { ref } from 'vue' |
| | | import { gdTaskResultListApi } from './achievementApi' |
| | | import DistributeDiaLog from './DistributeDiaLog.vue' |
| | | import { getAiImg } from '@ztzf/utils' |
| | | |
| | | const store = useStore() |
| | | const requester = computed(() => store.state.user.userInfo?.role_id === '2014158512610869250') |
| | |
| | | try { |
| | | const res = await gdTaskResultListApi({ patrolTaskId: currentRow.value.id }) |
| | | list.value = res?.data?.data ?? [] |
| | | list.value = await Promise.all( |
| | | list.value.map(async i => { |
| | | const aiImg = await getAiImg(i.resultUrl) |
| | | return { ...i, aiImg } |
| | | }) |
| | | ) |
| | | } finally { |
| | | loading.value = false |
| | | } |
| | | } |
| | | |
| | | const aiFrame = [ |
| | | '{"score":0.91357421875,"bbox":{"x_cen":1246.0,"y_cen":209.0,"width":166.0,"height":334.0},"class_name":"car","algorithmId":"e71116098eeb1d60cfebd04d30653b151"}', |
| | | '{"score":0.89697265625,"bbox":{"x_cen":370.0,"y_cen":694.5,"width":162.0,"height":331.0},"class_name":"car","algorithmId":"e71116098eeb1d60cfebd04d30653b151"}', |
| | | '{"score":0.89501953125,"bbox":{"x_cen":396.0,"y_cen":343.0,"width":168.0,"height":330.0},"class_name":"car","algorithmId":"e71116098eeb1d60cfebd04d30653b151"}', |
| | | '{"score":0.79296875,"bbox":{"x_cen":409.5,"y_cen":52.5,"width":167.0,"height":105.0},"class_name":"car","algorithmId":"e71116098eeb1d60cfebd04d30653b151"}', |
| | | ] |
| | | |
| | | function getAiImg(url) { |
| | | if (!url) return '' |
| | | const img = new Image() |
| | | img.crossOrigin = 'anonymous' |
| | | return new Promise(resolve => { |
| | | img.onload = () => { |
| | | if (!img.naturalWidth || !img.naturalHeight) { |
| | | resolve('') |
| | | return |
| | | } |
| | | |
| | | const canvas = document.createElement('canvas') |
| | | const ctx = canvas.getContext('2d') |
| | | if (!ctx) { |
| | | resolve('') |
| | | return |
| | | } |
| | | |
| | | canvas.width = img.naturalWidth |
| | | canvas.height = img.naturalHeight |
| | | ctx.drawImage(img, 0, 0, canvas.width, canvas.height) |
| | | |
| | | aiFrame.forEach(item => { |
| | | let target = item |
| | | try { |
| | | target = typeof item === 'string' ? JSON.parse(item) : item |
| | | } catch (error) { |
| | | return |
| | | } |
| | | |
| | | const { x_cen, y_cen, width, height } = target.bbox || {} |
| | | if ([x_cen, y_cen, width, height].some(value => typeof value !== 'number')) return |
| | | |
| | | const x = x_cen - width / 2 |
| | | const y = y_cen - height / 2 |
| | | const label = target.class_name || '' |
| | | const fontSize = Math.max(18, Math.round(canvas.width / 80)) |
| | | const labelHeight = fontSize + 10 |
| | | const labelY = y - labelHeight >= 0 ? y - labelHeight : y |
| | | |
| | | ctx.strokeStyle = '#FF3B30' |
| | | ctx.lineWidth = Math.max(3, Math.round(canvas.width / 640)) |
| | | ctx.strokeRect(x, y, width, height) |
| | | |
| | | if (label) { |
| | | ctx.font = `${fontSize}px Arial` |
| | | const labelWidth = ctx.measureText(label).width + 16 |
| | | ctx.fillStyle = '#FF3B30' |
| | | ctx.fillRect(x, labelY, labelWidth, labelHeight) |
| | | ctx.fillStyle = '#FFFFFF' |
| | | ctx.textBaseline = 'middle' |
| | | ctx.fillText(label, x + 8, labelY + labelHeight / 2) |
| | | } |
| | | }) |
| | | |
| | | try { |
| | | resolve(canvas.toDataURL('image/jpeg', 0.92)) |
| | | } catch (error) { |
| | | console.log(error) |
| | | resolve('') |
| | | } |
| | | } |
| | | img.onerror = () => resolve('') |
| | | img.src = url |
| | | }) |
| | | } |
| | | |
| | | // 打开分发弹框 |
| | |
| | | 任务成果({{ taskResultList.length || 0 }}条) |
| | | </div> |
| | | <div class="imgBox"> |
| | | <div v-for="item in taskResultList.filter(item => item.resultUrl && [1,2,3].includes( item.attachmentType))"> |
| | | <div |
| | | v-for="item in taskResultList.filter(item1 => item1.resultUrl && [1, 2, 3].includes(item1.attachmentType))" |
| | | > |
| | | <el-image |
| | | v-if="item.attachmentType === 1" |
| | | :src="item.resultUrl" |
| | | :preview-src-list="[item.resultUrl]" |
| | | v-if="item.attachmentType === 1 || item.attachmentType === 2" |
| | | :src="item.attachmentType === 1 ? item.resultUrl : getAiImg(item.resultUrl)" |
| | | :preview-src-list="[item.attachmentType === 1 ? item.resultUrl : getAiImg(item.resultUrl)]" |
| | | fit="cover" |
| | | preview-teleported |
| | | /> |
| | | <el-image |
| | | v-if="item.attachmentType === 2" |
| | | :src="item.resultUrl" |
| | | :preview-src-list="[item.resultUrl]" |
| | | fit="cover" |
| | | style="width: 80px; height: 80px" |
| | | preview-teleported |
| | | /> |
| | | <div class="video-btn" v-if="item.attachmentType === 3" @click="videoClick(item)"> |
| | |
| | | popper-class="gd-select-popper" |
| | | v-model="formData.patrolTaskType" |
| | | :options="workOrderTypeXT" |
| | | :props="{...taskTypeCascaderProps,multiple:true}" |
| | | :props="{ ...taskTypeCascaderProps, multiple: true }" |
| | | placeholder="请选择" |
| | | collapse-tags |
| | | clearable |
| | |
| | | v-if="VideoShow" |
| | | v-model="VideoShow" |
| | | :playUrl="currentVideo.resultUrl" |
| | | > |
| | | </VideoPlayDialog> |
| | | ></VideoPlayDialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed, ref, onMounted, inject } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import { fieldRules, flyVisual, getDictLabel, geomAnalysis } from '@ztzf/utils' |
| | | import { fieldRules, flyVisual, geomAnalysis, getAiImg } from '@ztzf/utils' |
| | | import { |
| | | gdPatrolTaskRepublish, |
| | | gdFlyerPageApi, |
| | | gdPatrolTaskAuditApi, |
| | | gdPatrolTaskDetailApi, |
| | | } from './inspectionRequestApi' |
| | | import { gdWorkOrderFlowListApi, gdWorkOrderFlowPatrolListApi, gdWorkOrderDetailApi } from '../orderManage/orderManageApi' |
| | | import { |
| | | gdWorkOrderFlowListApi, |
| | | gdWorkOrderFlowPatrolListApi, |
| | | gdWorkOrderDetailApi, |
| | | } from '../orderManage/orderManageApi' |
| | | import { gdManageDeviceListApi } from '../orderManage/gdManageDeviceApi' |
| | | import { pxToRem } from '@/utils/rem' |
| | | import CommonCesiumMap from '@/components/map-container/common-cesium-map.vue' |
| | |
| | | children: (group.algorithms || []).map(alg => ({ |
| | | id: alg.id, |
| | | name: alg.name, |
| | | })) |
| | | })), |
| | | })) |
| | | } catch (e) { |
| | | console.error('获取算法列表失败', e) |
| | |
| | | function getAlgorithmNames(algorithmIds) { |
| | | if (!algorithmIds || !algorithmIds.length) return '' |
| | | const allAlgorithms = algorithmTreeData.value.flatMap(group => group.children || []) |
| | | return algorithmIds.map(id => { |
| | | const item = allAlgorithms.find(alg => alg.id === id) |
| | | return item ? item.name : id |
| | | }).join(', ') |
| | | return algorithmIds |
| | | .map(id => { |
| | | const item = allAlgorithms.find(alg => alg.id === id) |
| | | return item ? item.name : id |
| | | }) |
| | | .join(', ') |
| | | } |
| | | |
| | | const gdStatusObj = { |
| | |
| | | function getAirDetails() { |
| | | const dockHeight = formData.value.height |
| | | queryAirById(formData.value.patrolRouteUrl).then(res => { |
| | | const { airlineWaypoints, airlineSetting } = res.data.data; |
| | | const { airlineWaypoints, airlineSetting } = res.data.data |
| | | // 使用空值合并运算符 ?? 或 逻辑或 || 兜底 |
| | | const { globalAirlineHeight = 0, airlineHeightMode } = airlineSetting || {}; |
| | | const { globalAirlineHeight = 0, airlineHeightMode } = airlineSetting || {} |
| | | const relative = airlineHeightMode === 'relativeToStartPoint' |
| | | const list = airlineWaypoints |
| | | if (!list.length) return mapRef.value?.flyBoundary() |
| | | const result = list.map(item => { |
| | | let height = item?.globalHeight || globalAirlineHeight |
| | | if (relative){ |
| | | if (relative) { |
| | | height = height + Number(dockHeight) |
| | | } |
| | | return [Number(item.longitude), Number(item.latitude),height || 0] |
| | | return [Number(item.longitude), Number(item.latitude), height || 0] |
| | | }) |
| | | viewer.entities.add({ |
| | | polyline: { |
| | |
| | | getAlgorithmList() |
| | | // 获取工单详情并根据geom范围获取航线列表 |
| | | await getWorkOrderDetail(formData.value.workOrderId) |
| | | ;['6', '7', '8'].includes(row.taskStatus) && await getTaskResultList() |
| | | ;['6', '7', '8'].includes(row.taskStatus) && (await getTaskResultList()) |
| | | loadList() |
| | | initMap() |
| | | getAirDetails() |
| | |
| | | transition: box-shadow 0.16s ease; |
| | | |
| | | &:hover { |
| | | box-shadow: |
| | | inset 0 0 0 2px rgba(76, 52, 255, 0.48), |
| | | 0 8px 20px rgba(76, 52, 255, 0.24); |
| | | box-shadow: inset 0 0 0 2px rgba(76, 52, 255, 0.48), 0 8px 20px rgba(76, 52, 255, 0.24); |
| | | } |
| | | } |
| | | .el-image { |
| | |
| | | overflow: hidden; |
| | | //border: 1px solid #D8D6FF; |
| | | border-radius: 4px; |
| | | background: |
| | | linear-gradient(135deg, rgba(76, 52, 255, 0.14), rgba(76, 52, 255, 0) 48%), |
| | | linear-gradient(180deg, #F4F5FF 0%, #E9ECFF 100%); |
| | | background: linear-gradient(135deg, rgba(76, 52, 255, 0.14), rgba(76, 52, 255, 0) 48%), |
| | | linear-gradient(180deg, #f4f5ff 0%, #e9ecff 100%); |
| | | box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.7); |
| | | display: flex; |
| | | align-items: center; |
| | |
| | | cursor: pointer; |
| | | |
| | | &::before { |
| | | content: ""; |
| | | content: ''; |
| | | position: absolute; |
| | | inset: 0; |
| | | background-image: linear-gradient( |
| | |
| | | <el-table-column type="index" label="序号" width="80" /> |
| | | <el-table-column label="图片/视频" > |
| | | <template v-slot="{ row }"> |
| | | <el-image |
| | | v-if="row.attachmentType ===1 && row.resultUrl" |
| | | :src="row.resultUrl" |
| | | :preview-src-list="[row.resultUrl]" |
| | | fit="cover" |
| | | style="width: 80px; height: 80px; border-radius: 4px;" |
| | | preview-teleported |
| | | /> |
| | | <el-image |
| | | v-if="row.attachmentType === 2 && row.resultUrl" |
| | | :src="row.resultUrl" |
| | | :preview-src-list="[row.resultUrl]" |
| | | fit="cover" |
| | | preview-teleported |
| | | /> |
| | | <el-image |
| | | v-if="row.attachmentType === 1 || row.attachmentType === 2" |
| | | :src="row.attachmentType === 1 ? row.resultUrl : getAiImg(row.resultUrl)" |
| | | :preview-src-list="[row.attachmentType === 1 ? row.resultUrl : getAiImg(row.resultUrl)]" |
| | | fit="cover" |
| | | style="width: 80px; height: 80px" |
| | | preview-teleported |
| | | /> |
| | | <div class="video-btn" v-if="row.attachmentType === 3 && row.resultUrl" @click="videoClick(row)"> |
| | | <el-icon :size="30" color="#fff"> |
| | | <VideoPlay /> |
| | |
| | | import {gdTaskResultPageApi, gdTaskResultDownloadApi, gdTaskResultRemoveApi,listByWorkOrderId}from './orderManageApi' |
| | | import { Search, RefreshRight, Download } from '@element-plus/icons-vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { getDictLabel } from '@ztzf/utils' |
| | | import { getAiImg, getDictLabel } from '@ztzf/utils' |
| | | import dayjs from 'dayjs' |
| | | import VideoPlayDialog from '@/components/VideoPlayDialog.vue' |
| | | |
| | |
| | | return { longitude, latitude } |
| | | }) |
| | | } |
| | | |
| | | // 图片转带ai框的图片 |
| | | export function getAiImg(url, aiFrameSource) { |
| | | if (!url) return '' |
| | | const aiFrame = aiFrameSource || [ |
| | | '{"score":0.91357421875,"bbox":{"x_cen":1246.0,"y_cen":209.0,"width":166.0,"height":334.0},"class_name":"car","algorithmId":"e71116098eeb1d60cfebd04d30653b151"}', |
| | | '{"score":0.89697265625,"bbox":{"x_cen":370.0,"y_cen":694.5,"width":162.0,"height":331.0},"class_name":"car","algorithmId":"e71116098eeb1d60cfebd04d30653b151"}', |
| | | '{"score":0.89501953125,"bbox":{"x_cen":396.0,"y_cen":343.0,"width":168.0,"height":330.0},"class_name":"car","algorithmId":"e71116098eeb1d60cfebd04d30653b151"}', |
| | | '{"score":0.79296875,"bbox":{"x_cen":409.5,"y_cen":52.5,"width":167.0,"height":105.0},"class_name":"car","algorithmId":"e71116098eeb1d60cfebd04d30653b151"}', |
| | | ] |
| | | const img = new Image() |
| | | img.crossOrigin = 'anonymous' |
| | | return new Promise(resolve => { |
| | | img.onload = () => { |
| | | if (!img.naturalWidth || !img.naturalHeight) { |
| | | resolve('') |
| | | return |
| | | } |
| | | |
| | | const canvas = document.createElement('canvas') |
| | | const ctx = canvas.getContext('2d') |
| | | if (!ctx) { |
| | | resolve('') |
| | | return |
| | | } |
| | | |
| | | canvas.width = img.naturalWidth |
| | | canvas.height = img.naturalHeight |
| | | ctx.drawImage(img, 0, 0, canvas.width, canvas.height) |
| | | |
| | | aiFrame.forEach(item => { |
| | | let target = item |
| | | try { |
| | | target = typeof item === 'string' ? JSON.parse(item) : item |
| | | } catch (error) { |
| | | return |
| | | } |
| | | |
| | | const { x_cen, y_cen, width, height } = target.bbox || {} |
| | | if ([x_cen, y_cen, width, height].some(value => typeof value !== 'number')) return |
| | | |
| | | const x = x_cen - width / 2 |
| | | const y = y_cen - height / 2 |
| | | const label = target.class_name || '' |
| | | const fontSize = Math.max(18, Math.round(canvas.width / 80)) |
| | | const labelHeight = fontSize + 10 |
| | | const labelY = y - labelHeight >= 0 ? y - labelHeight : y |
| | | |
| | | ctx.strokeStyle = '#FF3B30' |
| | | ctx.lineWidth = Math.max(3, Math.round(canvas.width / 640)) |
| | | ctx.strokeRect(x, y, width, height) |
| | | |
| | | if (label) { |
| | | ctx.font = `${fontSize}px Arial` |
| | | const labelWidth = ctx.measureText(label).width + 16 |
| | | ctx.fillStyle = '#FF3B30' |
| | | ctx.fillRect(x, labelY, labelWidth, labelHeight) |
| | | ctx.fillStyle = '#FFFFFF' |
| | | ctx.textBaseline = 'middle' |
| | | ctx.fillText(label, x + 8, labelY + labelHeight / 2) |
| | | } |
| | | }) |
| | | |
| | | try { |
| | | resolve(canvas.toDataURL('image/jpeg', 0.92)) |
| | | } catch (error) { |
| | | console.log(error) |
| | | resolve('') |
| | | } |
| | | } |
| | | img.onerror = () => resolve('') |
| | | img.src = url |
| | | }) |
| | | } |