Merge remote-tracking branch 'origin/test' into test
25 files modified
37 files added
| | |
| | | |
| | | # 服务地址 |
| | | VITE_APP_URL = https://wrj.shuixiongit.com/api |
| | | # VITE_APP_URL= http://192.168.1.7 |
| | | |
| | | #VITE_APP_URL= http://192.168.1.168 rjg |
| | | #VITE_APP_URL= http://192.168.1.33 |
| | | #新大屏地址 |
| | | VITE_APP_DASHBOARD_URL = 'https://wrj.shuixiongit.com/command-center-dashboard/' |
| | | |
| | |
| | | VITE_APP_AIRLINE_URL = https://wrj.shuixiongit.com/minio/cloud-bucket |
| | | |
| | | # 图片存放地址 |
| | | VITE_APP_TERRAIN_URL = https://wrj.shuixiongit.com/aiskyminio/cloud-bucket |
| | | VITE_APP_TERRAIN_URL = https://wrj.shuixiongit.com/aiskyminio/cloud-bucket/ztzf_terrain/all_terrain |
| | | |
| | | # 算法仓库图片地址 |
| | | VITE_APP_PICTURE_URL = https://wrj.shuixiongit.com/aiskyminio/cloud-bucket |
| | | |
| | | # 是否在打包时开启压缩,支持 gzip 和 brotli |
| | | VITE_BUILD_COMPRESS = gzip |
| New file |
| | |
| | | NODE_ENV = 'development' |
| | | |
| | | #开发环境配置 |
| | | VITE_APP_ENV = 'development' |
| | | |
| | | #接口地址 |
| | | VITE_APP_API=/api |
| | | |
| | | #页面基础路径 |
| | | VITE_APP_BASE=/manage |
| | | |
| | | # 服务地址 |
| | | VITE_APP_URL = http://192.168.1.227:80/api |
| | | #VITE_APP_URL= http://192.168.1.168 rjg |
| | | #VITE_APP_URL= http://192.168.1.33 |
| | | #新大屏地址 |
| | | VITE_APP_DASHBOARD_URL = 'http://192.168.1.227:80/command-center-dashboard/' |
| | | |
| | | # 域名 |
| | | VITE_APP_AREA_NAME = http://192.168.1.227:80 |
| | | |
| | | # ws地址 |
| | | VITE_APP_WS_API_URL = wss://192.168.1.227:80 |
| | | |
| | | # 航线文件地址 |
| | | VITE_APP_AIRLINE_URL = http://192.168.1.227:80/minio/cloud-bucket |
| | | |
| | | # 图片存放地址 |
| | | VITE_APP_TERRAIN_URL = https://wrj.shuixiongit.com/aiskyminio/cloud-bucket/ztzf_terrain/all_terrain |
| | | |
| | | # 算法仓库图片地址 |
| | | VITE_APP_PICTURE_URL = http://192.168.1.227:80/aiskyminio/cloud-bucket |
| | | |
| | | # 是否在打包时开启压缩,支持 gzip 和 brotli |
| | | VITE_BUILD_COMPRESS = gzip |
| | | |
| | | #页面基础路径 |
| | | # VITE_APP_BASE_COMMAND = /drone-web-manage/ |
| | | #天地图token 浏览器端口 |
| | | VITE_APP_TDT_TOKEN = e110584a27d506da2740edca951683f4 |
| | | VITE_APP_CESIUM_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJkYTZlNGNlYS01NTU1LTQ1MGEtYmNlZS0yNTE2NDk5YWM2MjEiLCJpZCI6MTc5Njk2LCJpYXQiOjE3MDA1NDcwMjV9.qcl4AH2731cfFd0-I1ZLUINPXqvglLkDFD-UGR2zU5M |
| | |
| | | VITE_APP_AIRLINE_URL = https://wrj.shuixiongit.com/aiskyminio/cloud-bucket |
| | | |
| | | # 图片存放地址 |
| | | VITE_APP_TERRAIN_URL = https://wrj.shuixiongit.com/aiskyminio/cloud-bucket |
| | | VITE_APP_TERRAIN_URL = https://wrj.shuixiongit.com/aiskyminio/cloud-bucket/ztzf_terrain/all_terrain |
| | | |
| | | # 算法仓库图片地址 |
| | | VITE_APP_PICTURE_URL = https://wrj.shuixiongit.com/aiskyminio/cloud-bucket |
| | | |
| | | # 是否在打包时开启压缩,支持 gzip 和 brotli |
| | | VITE_BUILD_COMPRESS = gzip |
| | |
| | | <!DOCTYPE html> |
| | | <html lang="en"> |
| | | <html lang="zh-CN"> |
| | | |
| | | <head> |
| | | <meta charset="UTF-8" /> |
| | |
| | | "name": "saber", |
| | | "version": "4.1.0", |
| | | "scripts": { |
| | | "dev": "vite --mode development", |
| | | "dev": "vite --host", |
| | | "base": "vite --host --mode localhost", |
| | | "prod": "vite --mode production", |
| | | "build:test": "vite build --mode development", |
| | | "build:prod": "vite build --mode production", |
| | |
| | | "vite-plugin-compression": "^0.5.1", |
| | | "vite-plugin-vue-setup-extend": "^0.4.0" |
| | | } |
| | | } |
| | | } |
| | |
| | | src: url("SourceHanSansCN-Regular.otf") format("opentype"); |
| | | font-weight: 400; |
| | | font-style: normal; |
| | | font-display: swap; |
| | | } |
| | | |
| | | @font-face { |
| | |
| | | src: url("SourceHanSansCN-Medium.otf") format("opentype"); |
| | | font-weight: 500; |
| | | font-style: normal; |
| | | font-display: swap; |
| | | } |
| | | |
| | | @font-face { |
| | |
| | | src: url("SourceHanSansCN-Bold.otf") format("opentype") ; |
| | | font-weight: 700; |
| | | font-style: normal; |
| | | font-display: swap; |
| | | } |
| | | |
| | | |
| | |
| | | src: url("SourceHanSansCN-Regular.otf") format("opentype"); |
| | | font-weight: 400; |
| | | font-style: normal; |
| | | font-display: swap; |
| | | } |
| | | |
| | | @font-face { |
| | |
| | | src: url("SourceHanSansCN-Medium.otf") format("opentype"); |
| | | font-weight: 500; |
| | | font-style: normal; |
| | | font-display: swap; |
| | | } |
| | | |
| | | @font-face { |
| | |
| | | src: url("SourceHanSansCN-Bold.otf") format("opentype") ; |
| | | font-weight: 700; |
| | | font-style: normal; |
| | | font-display: swap; |
| | | } |
| | | |
| | | |
| | |
| | | src: url("YouSheBiaoTiHei.TTF") format("truetype"); |
| | | font-weight: normal; |
| | | font-style: normal; |
| | | font-display: swap; |
| | | } |
| New file |
| | |
| | | import request from '@/axios'; |
| | | // 列表接口 |
| | | export const getaiImagesPageAPI = (data,params) => { |
| | | return request({ |
| | | url: `/blade-resource//attach/attachmentsPage`, |
| | | method: 'post', |
| | | data,params |
| | | }) |
| | | }; |
| | | // 详情接口 |
| | | export const getAttachInfoAPI = (id) => { |
| | | return request({ |
| | | url: `/blade-resource/attach/getAttachInfo`, |
| | | method: 'get', |
| | | params: { |
| | | id, |
| | | }, |
| | | }) |
| | | }; |
| | | //删除 |
| | | export const deleteFileMultipleApi = (ids) => { |
| | | return request({ |
| | | url: `/blade-resource/attach/remove`, |
| | | method: 'post', |
| | | params: { |
| | | ids, |
| | | }, |
| | | }) |
| | | } |
| | | // 下载 |
| | | export const downloadApi = (data) => { |
| | | return request({ |
| | | url: `/blade-resource/attach/download`, |
| | | method: 'post', |
| | | data |
| | | }) |
| | | } |
| | | // 地图 |
| | | export const getMapInfoAPI = (jobId) => { |
| | | return request({ |
| | | url: `/blade-resource/attach/getAttachInfoByJobId`, |
| | | method: 'get', |
| | | params: { |
| | | jobId, |
| | | }, |
| | | }) |
| | | }; |
| | | |
| | | //编辑文件名 |
| | | export const updataTitleApi = (data) => { |
| | | return request({ |
| | | url: `/blade-resource/attach/updateFileName`, |
| | | method: 'post', |
| | | params:data |
| | | }) |
| | | } |
| | | // 正射文件 |
| | | export const getOrthoimageInfo = jobId => { |
| | | return request({ |
| | | url: '/drone-odm/odmTaskInfo/odmTaskInfo/getOrthoimageInfo', |
| | | method: 'get', |
| | | params: { |
| | | jobId, |
| | | }, |
| | | }) |
| | | } |
| New file |
| | |
| | | /* |
| | | * @Author : yuan |
| | | * @Date : 2025-06-05 09:49:42 |
| | | * @LastEditors : yuan |
| | | * @LastEditTime : 2025-06-07 18:16:57 |
| | | * @FilePath : \src\api\panorama\index.js |
| | | * @Description : |
| | | * Copyright 2025 OBKoro1, All Rights Reserved. |
| | | * 2025-06-05 09:49:42 |
| | | */ |
| | | import request from '@/axios' |
| | | |
| | | export const getPanoramaList = (data) => { |
| | | return request({ |
| | | url: `/blade-resource/attach/mapAttachs`, |
| | | method: 'post', |
| | | data |
| | | }) |
| | | } |
| | | |
| | | export const getPanoramaDetails = (id) => { |
| | | return request({ |
| | | url: `/blade-resource/attach/detail?id=${id}`, |
| | | method: 'get' |
| | | }) |
| | | } |
| New file |
| | |
| | | <template> |
| | | <el-dialog |
| | | modal-class="showFullScreenDlg" |
| | | v-model="panoramaParamsShow" |
| | | :close-on-click-modal="false" |
| | | :destroy-on-close="true" |
| | | fullscreen |
| | | > |
| | | <iframe :src="iframeSrc" frameborder="0"></iframe> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { getPanoramaDetails } from '@/api/panorama/index' |
| | | import { getShowImg } from '@/utils/util' |
| | | |
| | | import EventBus from '@/utils/eventBus' |
| | | |
| | | const panoramaParamsShow = defineModel('panoramaParamsShow', { |
| | | default: false, |
| | | }) |
| | | const panoramaParamsUrl = defineModel('panoramaParamsUrl', { |
| | | default: '', |
| | | }) |
| | | const iframeSrc = computed(() => { |
| | | if (!panoramaParamsUrl.value) return '' |
| | | return `https://wrj.shuixiongit.com/dronePanorama/html/simple-index.html?path=${getShowImg(panoramaParamsUrl.value)}` |
| | | }) |
| | | |
| | | const initPanorama = id => { |
| | | getPanoramaDetails(id).then(res => { |
| | | panoramaParamsUrl.value = res.data.data.link |
| | | panoramaParamsShow.value = true |
| | | }) |
| | | } |
| | | |
| | | onMounted(() => { |
| | | EventBus.on('initPanorama', initPanorama) |
| | | }) |
| | | |
| | | onBeforeUnmount(() => { |
| | | EventBus.off('initPanorama', initPanorama) |
| | | }) |
| | | </script> |
| | | |
| | | <style lang="scss" scoped></style> |
| | |
| | | * @param data 数据 数据格式 [[lng, lat], [lng, lat], [lng, lat]] |
| | | */ |
| | | async function addPolyline (data) { |
| | | await renderPreviewLine(data.url, data.type, data.cb) |
| | | await renderPreviewLine(data.url, data.type, data.cb, data.infos) |
| | | } |
| | | |
| | | onMounted(() => { |
| New file |
| | |
| | | <template> |
| | | <div class="mapPopUpBox"> |
| | | <div class="title"> |
| | | <span>{{ info.event_name }}</span> |
| | | <el-icon class="header-close" @click.stop="props.detailClick"><Warning /></el-icon> |
| | | <el-icon class="header-close" @click.stop="props.removeLabel"> |
| | | <Close /> |
| | | </el-icon> |
| | | </div> |
| | | <div class="content"> |
| | | <div class="medium"> |
| | | <img |
| | | class="quanjing" |
| | | @click="clickpanorama(infoList)" |
| | | v-if="infoList?.resultType === 5" |
| | | :src="infoList?.link" |
| | | alt="" |
| | | /> |
| | | <el-image |
| | | v-else |
| | | class="eventImage" |
| | | :src="getSmallImg(infoList?.link)" |
| | | :preview-src-list="[getSmallImg(infoList?.link)]" |
| | | fit="cover" |
| | | preview-teleported |
| | | ></el-image> |
| | | </div> |
| | | |
| | | <div class="details"> |
| | | <div class="label">时间:</div> |
| | | <div class="value point"> |
| | | {{ |
| | | infoList?.create_time?.slice(5, 16).replace('-', '/') || |
| | | infoList?.createTime?.slice(5, 16).replace('-', '/') |
| | | }} |
| | | </div> |
| | | <div class="label">地点:</div> |
| | | <div class="value"> |
| | | {{ _.round(infoList?.metadata?.shootPosition?.lng, 3) }},{{ |
| | | _.round(infoList?.metadata?.shootPosition?.lat, 3) |
| | | }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | <script setup> |
| | | import EventBus from '@/utils/eventBus'; |
| | | import { ElImage, ElIcon } from 'element-plus'; |
| | | import { Close, Warning } from '@element-plus/icons-vue'; |
| | | import _ from 'lodash'; |
| | | import { getShowImg, getSmallImg } from '@/utils/util'; |
| | | const props = defineProps(['data', 'removeLabel', 'detailClick']); |
| | | const loading = ref(true); |
| | | import PanoramaPopup from '@/components/PanoramaPopup/PanoramaPopup.vue'; //全景 |
| | | const emit = defineEmits(['update:panoramaParamsShow', 'update:panoramaParamsUrl']); |
| | | const info = ref({ |
| | | event_name: '', |
| | | status: 1, |
| | | url: '1', |
| | | longitude: '', |
| | | latitude: '', |
| | | create_time: '04/01 12:41', |
| | | }); |
| | | const infoList = props.data; |
| | | const clickpanorama = val => { |
| | | // 通过事件总线发送全景参数 |
| | | EventBus.emit('open-panorama', { |
| | | show: true, |
| | | url: val.link, |
| | | }); |
| | | // 保留原有事件触发,确保兼容性 |
| | | emit('update:panoramaParamsShow', true); |
| | | emit('update:panoramaParamsUrl', val.link); |
| | | }; |
| | | onMounted(async () => {}); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .mapPopUpBox { |
| | | width: 271px; |
| | | height: 153px; |
| | | // background: url('@/assets/images/dataCenter/datamap/popUpBox.png') no-repeat center / 100% 100%; |
| | | border-radius: 20px; |
| | | background-color: #fff; |
| | | |
| | | padding: 13px 13px 1px 13px; |
| | | pointer-events: all; |
| | | |
| | | .title { |
| | | font-family: YouSheBiaoTiHei, YouSheBiaoTiHei, serif; |
| | | font-weight: 400; |
| | | font-size: 18px; |
| | | line-height: 16px; |
| | | background: linear-gradient(180deg, #a8e5fb 0%, #e6f8ff 100%); |
| | | -webkit-background-clip: text; |
| | | -webkit-text-fill-color: transparent; |
| | | -moz-background-clip: text; |
| | | -moz-text-fill-color: transparent; |
| | | background-clip: text; |
| | | text-fill-color: transparent; |
| | | margin-bottom: 10px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: end; |
| | | |
| | | .header-close { |
| | | width: 20px; |
| | | height: 100%; |
| | | line-height: 100%; |
| | | display: flex; |
| | | align-items: center; |
| | | color: black; |
| | | pointer-events: all; |
| | | cursor: pointer; |
| | | } |
| | | } |
| | | |
| | | .content { |
| | | height: 102px; |
| | | width: 240px; |
| | | display: flex; |
| | | align-items: center; |
| | | |
| | | .medium { |
| | | height: 102px; |
| | | width: 120px; |
| | | margin-right: 10px; |
| | | |
| | | > img, |
| | | video, |
| | | div { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | } |
| | | |
| | | .details { |
| | | .label { |
| | | font-family: Source Han Sans CN, Source Han Sans CN; |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: black; |
| | | line-height: 16px; |
| | | margin-bottom: 2px; |
| | | } |
| | | |
| | | .value { |
| | | font-family: Source Han Sans CN, Source Han Sans CN; |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: black; |
| | | line-height: 16px; |
| | | margin-bottom: 2px; |
| | | } |
| | | |
| | | .point { |
| | | margin-bottom: 14px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | |
| | | execute_height_mode: '', |
| | | auto_flight_speed: '', |
| | | wayline_type: '', |
| | | infos: {} |
| | | }) |
| | | |
| | | const routeLineListClick = async data => { |
| | |
| | | await renderRouteLine(data.data) |
| | | } |
| | | |
| | | async function renderPreviewLine (kmzUrl, wayline_type, cb = () => { }) { |
| | | async function renderPreviewLine (kmzUrl, wayline_type, cb = () => { }, infos = {}) { |
| | | |
| | | |
| | | resetCurRouteLineData() |
| | | |
| | | const { pointPlacemark, polygonList, templateType, startPoint, execute_height_mode, auto_flight_speed } = |
| | |
| | | execute_height_mode, |
| | | auto_flight_speed, |
| | | wayline_type, |
| | | infos: infos |
| | | } |
| | | |
| | | cb(curRouteLineData.value.polygonList.map(item => [Number(item[0]), Number(item[1])])) |
| | |
| | | }) |
| | | } |
| | | |
| | | |
| | | |
| | | let pointList = handlePointListForKmz({ |
| | | placemark: data, |
| | | startPoint: curRouteLineData.value.startPoint, |
| | |
| | | |
| | | pointList.shift() |
| | | |
| | | if (JSON.stringify(curRouteLineData.value.infos) != '{}') { |
| | | pointList.unshift({ |
| | | longitude: curRouteLineData.value.infos[0].longitude, |
| | | latitude: curRouteLineData.value.infos[0].latitude, |
| | | height: curRouteLineData.value.infos[0].height || 0, |
| | | }) |
| | | } |
| | | |
| | | const routePositions = pointList.map(i => Cesium.Cartesian3.fromDegrees(Number(i.longitude), Number(i.latitude), Number(i.height))) |
| | | |
| | | previewDataSource.entities.add({ |
| | |
| | | .avue--detail .el-form-item { |
| | | background-color: #fafafa; |
| | | } |
| | | |
| | | .showFullScreenDlg { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | |
| | | .el-dialog { |
| | | position: relative; |
| | | padding: 0; |
| | | overflow: hidden; |
| | | |
| | | .el-dialog__body { |
| | | width: 100%; |
| | | height: 100%; |
| | | |
| | | iframe { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | } |
| | | |
| | | .el-dialog__header { |
| | | height: 0; |
| | | overflow: hidden; |
| | | padding: 0; |
| | | |
| | | .el-dialog__headerbtn { |
| | | position: absolute; |
| | | left: 0; |
| | | top: 14px; |
| | | |
| | | .el-dialog__close { |
| | | color: #fff; |
| | | font-size: 35px; |
| | | z-index: 99; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | baseLayer: false, |
| | | fullscreenButton: false, |
| | | } |
| | | |
| | | |
| | | this.viewer = new Viewer(options.dom, viewerOptions) |
| | | |
| | | this.viewer.imageryLayers.removeAll() |
| | |
| | | |
| | | if (terrain) { |
| | | try { |
| | | Cesium.CesiumTerrainProvider.fromUrl(`${import.meta.env.VITE_APP_TERRAIN_URL}/ztzf_terrain/all_terrain`, { |
| | | Cesium.CesiumTerrainProvider.fromUrl(`${import.meta.env.VITE_APP_TERRAIN_URL}`, { |
| | | requestVertexNormals: true, // 启用地形法线增强立体感 |
| | | requestWaterMask: true, // 启用水体遮罩效果 |
| | | }).then(terrainProvider => { |
| | |
| | | } |
| | | return a |
| | | } |
| | | |
| | | // 添加entity |
| | | addCustomImageryProviderDataSource (imageryLayers) { |
| | | console.log(imageryLayers, this.viewer, '---1-') |
| | | |
| | | return this.viewer?.imageryLayers.addImageryProvider(imageryLayers) |
| | | } |
| | | |
| | | showCustomImageryProviderDataSource (imageryLayers, show) { |
| | | if (imageryLayers) { |
| | | imageryLayers.show = show |
| | | } |
| | | } |
| | | |
| | | |
| | | removeAllCustomImageryProviderDataSource (imageryLayers) { |
| | | if (imageryLayers) { |
| | | this.viewer?.imageryLayers.remove(imageryLayers) |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | import mitt from 'mitt' |
| | | |
| | | const emitter = mitt() |
| | | |
| | | export default emitter |
| New file |
| | |
| | | /* |
| | | * @Author: shuishen 1109946754@qq.com |
| | | * @Date: 2025-04-17 20:17:12 |
| | | * @LastEditors: shuishen 1109946754@qq.com |
| | | * @LastEditTime: 2025-04-17 20:28:08 |
| | | * @FilePath: \command-center-dashboard\src\utils\stateToImageMap\drone.js |
| | | * @Description: |
| | | * |
| | | * Copyright (c) 2025 by shuishen, All Rights Reserved. |
| | | */ |
| | | import endingImg from '@/assets/images/aiNowFly/ending-offline.png' |
| | | import endingHighImg from '@/assets/images/aiNowFly/ending-high.png' |
| | | |
| | | const droneImage = { |
| | | 'OFFLINE': endingImg, |
| | | 'WORKING': endingHighImg, |
| | | 'LEISURE': endingHighImg |
| | | } |
| | | |
| | | /** |
| | | * 根据机巢状态获取图片 |
| | | * @param {string} status 状态 |
| | | * @returns |
| | | */ |
| | | export const getDroneStatusImage = (status) => droneImage[status] || endingHighImg |
| | | |
| | | /** |
| | | * 根据机巢状态获取图片 |
| | | * @param {boolean} isOnline 状态 |
| | | * @returns |
| | | */ |
| | | export const getDroneFlagImage = (isOnline) => isOnline ? endingHighImg : endingImg |
| New file |
| | |
| | | import eventPending1 from '@/assets/images/home/useEventOperate/eventPending1.png' // 待处理 0 |
| | | import eventPending from '@/assets/images/home/useEventOperate/eventPending.png' // 待处理 0 |
| | | import eventWaitAudit1 from '@/assets/images/home/useEventOperate/eventWaitAudit1.png' // 待审核 2 |
| | | import eventWaitAudit from '@/assets/images/home/useEventOperate/eventWaitAudit.png' // 待审核 2 |
| | | import eventProcessing1 from '@/assets/images/home/useEventOperate/eventProcessing1.png' // 处理中 3 |
| | | import eventProcessing from '@/assets/images/home/useEventOperate/eventProcessing.png' // 处理中 3 |
| | | import eventCompleted1 from '@/assets/images/home/useEventOperate/eventCompleted1.png' // 已完成 4 |
| | | import eventCompleted from '@/assets/images/home/useEventOperate/eventCompleted.png' // 已完成 4 |
| | | import eventClosed1 from '@/assets/images/home/useEventOperate/eventClosed1.png' // 已完结 5 |
| | | import eventClosed from '@/assets/images/home/useEventOperate/eventClosed.png' // 已完结 5 |
| | | |
| | | |
| | | const eventImage = { |
| | | 0: eventPending, |
| | | 2: eventWaitAudit, |
| | | 3: eventProcessing, |
| | | 4: eventCompleted, |
| | | 5: eventClosed |
| | | } |
| | | const eventActiveImage = { |
| | | 0: eventPending1, |
| | | 2: eventWaitAudit1, |
| | | 3: eventProcessing1, |
| | | 4: eventCompleted1, |
| | | 5: eventClosed1 |
| | | } |
| | | |
| | | /** |
| | | * 根据事件状态获取图片资源 |
| | | * @param {*} status 状态 |
| | | * @returns |
| | | */ |
| | | export const getEventImage = (status) => eventImage[status] || eventCompleted |
| | | export const getEventActiveImage = (status) => eventActiveImage[status] || eventCompleted |
| | |
| | | * @returns {string} |
| | | */ |
| | | export const getSmallImg = url => { |
| | | if (!url) return '' |
| | | const lastDotIndex = url.lastIndexOf('.') |
| | | return `${url.substring(0, lastDotIndex)}_small${url.substring(lastDotIndex)}` |
| | | if (!url) return '' |
| | | const lastDotIndex = url.lastIndexOf('.') |
| | | return `${url.substring(0, lastDotIndex)}_small${url.substring(lastDotIndex)}` |
| | | } |
| | | /** |
| | | * 获取正射路径 |
| | | * @param url |
| | | * @returns {string} |
| | | */ |
| | | export const getzsSmallImg = url => { |
| | | if (!url) return '' |
| | | const lastDotIndex = url.lastIndexOf('.') |
| | | return `${url.substring(0, lastDotIndex)}_small.jpeg` |
| | | } |
| | | /** |
| | | * 浏览器判断是否全屏 |
| | |
| | | // 设置默认范围 [上个月, 当前月] |
| | | return [new Date(lastMonthYear, lastMonth, 1), new Date(currentYear, currentMonth, 1)] |
| | | } |
| | | /** |
| | | * 获取中等缩略图路径 |
| | | * @param url |
| | | * @returns {string} |
| | | */ |
| | | export const getShowImg = url => { |
| | | if (!url) return '' |
| | | const lastDotIndex = url.lastIndexOf('.') |
| | | return `${url.substring(0, lastDotIndex)}_show${url.substring(lastDotIndex)}` |
| | | } |
| | | |
| | | // key驼峰转换为下划线 |
| | | export function camelToSnake (obj) { |
| | |
| | | }); |
| | | import { getDictionaryByCode } from '@/api/system/dictbiz'; |
| | | import { getalgorithmList, selectDeviceList } from '@/api/algorithm'; |
| | | import { getSmallImg } from '@/utils/util' |
| | | import { getSmallImg } from '@/utils/util'; |
| | | import { useRouter } from 'vue-router'; |
| | | const router = useRouter(); |
| | | const baseUrl = import.meta.env.VITE_APP_TERRAIN_URL; |
| | | const baseUrl = import.meta.env.VITE_APP_PICTURE_URL; |
| | | const showDetail = ref(false); |
| | | const taskData = ref(''); |
| | | const jcvalue = ref(''); |
| | |
| | | topCode: '00', |
| | | treeCode: '', |
| | | treeParentCode: '', |
| | | loading:null, |
| | | treeData: [], |
| | | treeOption: { |
| | | nodeKey: 'id', |
| | |
| | | }, |
| | | handleSubmit(form, done, loading) { |
| | | const parentCode = form.parentCode === this.topCode ? '' : form.parentCode; |
| | | form.code = parentCode + form.subCode; |
| | | // form.code = parentCode + form.subCode; |
| | | form.code = form.subCode; |
| | | submit(form).then( |
| | | () => { |
| | | this.$message({ |
| | |
| | | this.regionForm.subCode = ''; |
| | | this.$refs.form.resetForm(); |
| | | done(); |
| | | console.log('成功'); |
| | | |
| | | }, |
| | | error => { |
| | | loading(); |
| | | // loading(); |
| | | window.console.log(error); |
| | | } |
| | | ); |
| New file |
| | |
| | | <template> |
| | | <el-dialog modal-class="mapDialog" v-model="isShow" width="80%"> |
| | | <div class="mapBox"> |
| | | <div v-if="isShow" id="dataCenterMap" class="ztzf-cesium"></div> |
| | | </div> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import EventBus from '@/utils/eventBus'; |
| | | import PanoramaPopup from '@/components/PanoramaPopup/PanoramaPopup.vue'; |
| | | import { getMapInfoAPI } from '@/api/dataCenter/dataCenter'; |
| | | import { useStore } from 'vuex'; |
| | | import { PublicCesium } from '@/utils/cesium/publicCesium'; |
| | | import { Cartesian3 } from 'cesium'; |
| | | import * as Cesium from 'cesium'; |
| | | import EventPopUpBox from '@/hooks/components/EventPopUpBox.vue'; |
| | | import { render, nextTick, watch, onMounted, onBeforeUnmount, shallowRef, ref, h } from 'vue'; |
| | | import panoramaPoint from '@/assets/images/panorama/panorama-point.png'; //全景图标 |
| | | import defaultIcon from '@/assets/images/dataCenter/datamap/eventCompleted.png'; //默认图标 |
| | | import activeIcon from '@/assets/images/dataCenter/datamap/activeevent.png'; // 激活图标 |
| | | import { getEventActiveImage, getEventImage } from '@/utils/stateToImageMap/event'; //点 |
| | | const emit = defineEmits(['lookDetail']); |
| | | const isShow = defineModel('show'); |
| | | const viewerRef = shallowRef(null); |
| | | let viewer = null; |
| | | const store = useStore(); |
| | | const currentAreaPosition = ref({ height: 1987280, latitude: 27.636112, longitude: 115.732975 }); |
| | | let handler = null; |
| | | const props = defineProps(['jobId', 'dotData']); |
| | | let currentClickEntity = null; |
| | | // 存储地图实体引用 |
| | | const dataPointEntities = ref([]); |
| | | const isMapInitialized = ref(false); //地图加载 |
| | | const dataPointList = ref([]); |
| | | const activeEntity = ref(null); // 当前激活的点 |
| | | // 获取弹框box |
| | | const detailId = ref(''); |
| | | const createLabelDom = data => { |
| | | detailId.value = data; |
| | | const vNode = h(EventPopUpBox, { data, removeLabel, detailClick }); |
| | | const tooltipContainer = document.createElement('div'); |
| | | tooltipContainer.id = 'mapPopUpBox'; |
| | | tooltipContainer.style.position = 'absolute'; |
| | | tooltipContainer.style.transform = 'translate(-50%,-125%)'; |
| | | tooltipContainer.style.pointerEvents = 'none'; |
| | | document.querySelector('#dataCenterMap')?.append(tooltipContainer); |
| | | render(vNode, tooltipContainer); |
| | | return tooltipContainer; |
| | | }; |
| | | // 弹框位置刷新 |
| | | const labelBoxUpdate = () => { |
| | | if (!currentClickEntity) return; |
| | | const mapPopUpBox = document.querySelector('#mapPopUpBox'); |
| | | let dom = mapPopUpBox |
| | | ? mapPopUpBox |
| | | : createLabelDom(currentClickEntity.properties?.customData._value.data || currentClickEntity); |
| | | const screenPosition = viewer?.scene.cartesianToCanvasCoordinates( |
| | | currentClickEntity?.position?._value |
| | | ); |
| | | if (screenPosition) { |
| | | dom.style.left = `${screenPosition.x}px`; |
| | | dom.style.top = `${screenPosition.y}px`; |
| | | dom.style.display = 'block'; |
| | | } |
| | | }; |
| | | |
| | | const removeDom = () => { |
| | | const dom = document.querySelector('#mapPopUpBox'); |
| | | if (dom && dom.parentNode) { |
| | | dom.parentNode.removeChild(dom); |
| | | } |
| | | }; |
| | | // 移除弹框标签 |
| | | const removeLabel = () => { |
| | | viewer?.scene.postRender.removeEventListener(labelBoxUpdate); |
| | | removeDom(); |
| | | }; |
| | | // 点击去到详情页面 |
| | | const detailClick = () => { |
| | | removeLabel(); |
| | | // 给父组件传值 |
| | | // emit('update:show', false); //关闭地图弹框 |
| | | emit('lookDetail', detailId.value); |
| | | }; |
| | | |
| | | // 恢复所有点的默认图标 |
| | | const restoreAllIcons = () => { |
| | | dataPointEntities.value.forEach(entity => { |
| | | if (entity.billboard) { |
| | | const status = entity?.properties?._customData?._value?.data.status; |
| | | entity.billboard.image = |
| | | status === 1 || status === null ? defaultIcon : getEventImage(status); |
| | | } |
| | | }); |
| | | activeEntity.value = null; |
| | | }; |
| | | // 左键单机事件 |
| | | const singleMachineEvent = async click => { |
| | | let clickedEntities = click |
| | | ? viewer?.scene.drillPick(click.position).map(item => item.id) |
| | | : [currentClickEntity]; |
| | | if (!clickedEntities.length) { |
| | | // 点击空白处恢复所有图标并移除弹窗 |
| | | restoreAllIcons(); |
| | | removeLabel(); |
| | | return; |
| | | } |
| | | currentClickEntity = clickedEntities[0]; |
| | | // 恢复所有点的默认图标 |
| | | restoreAllIcons(); |
| | | if (currentClickEntity.billboard) { |
| | | const status = currentClickEntity?.properties?._customData?._value?.data.status; |
| | | console.log('stayus', currentClickEntity?.properties?._customData?._value?.data); |
| | | currentClickEntity.billboard.image = |
| | | status === 1 || status === null ? activeIcon : getEventActiveImage(status); |
| | | currentClickEntity.billboard.scale = 1; // 可选缩放效果 |
| | | activeEntity.value = currentClickEntity; |
| | | } |
| | | |
| | | removeLabel(); |
| | | viewer.scene.postRender.addEventListener(labelBoxUpdate); |
| | | }; |
| | | |
| | | // 事件初始化 |
| | | const handlerInit = () => { |
| | | if (handler) return; |
| | | handler = new Cesium.ScreenSpaceEventHandler(viewer?.scene.canvas); |
| | | handler.setInputAction(singleMachineEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK); |
| | | }; |
| | | |
| | | // 清除所有数据点实体 |
| | | const clearDataPoints = () => { |
| | | if (!viewer) return; |
| | | dataPointEntities.value.forEach(entity => { |
| | | viewer.entities.remove(entity); |
| | | }); |
| | | dataPointEntities.value = []; |
| | | activeEntity.value = null; |
| | | }; |
| | | |
| | | const removeHandler = () => { |
| | | handler?.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK); |
| | | handler?.destroy(); |
| | | handler = null; |
| | | }; |
| | | |
| | | const renderDataPoint = mapList => { |
| | | if (!viewer || !mapList?.length) return; |
| | | |
| | | // 清除旧实体 |
| | | clearDataPoints(); |
| | | |
| | | // 添加新实体 |
| | | mapList.forEach((item, index) => { |
| | | const entity = viewer?.entities.add({ |
| | | id: `dataCenter-point-${index}-${Date.now()}`, |
| | | position: Cesium.Cartesian3.fromDegrees( |
| | | Number(item.metadata.shootPosition.lng), |
| | | Number(item.metadata.shootPosition.lat) |
| | | ), |
| | | label: { |
| | | font: '12pt Source Han Sans CN', |
| | | fillColor: Cesium.Color.WHITE, |
| | | outlineColor: Cesium.Color.BLACK, |
| | | outlineWidth: 2, |
| | | style: Cesium.LabelStyle.FILL_AND_OUTLINE, |
| | | verticalOrigin: Cesium.VerticalOrigin.BOTTOM, |
| | | eyeOffset: new Cesium.Cartesian3(0, 0, -9), |
| | | pixelOffset: new Cesium.Cartesian2(0, 55), |
| | | }, |
| | | billboard: { |
| | | image: item.status == null || item.status === 1 ? defaultIcon : getEventImage(item.status), // 初始为默认图标 |
| | | width: 40, |
| | | height: 40, |
| | | pixelOffset: new Cesium.Cartesian2(0, -15), |
| | | }, |
| | | properties: { |
| | | customData: { |
| | | data: item, |
| | | }, |
| | | }, |
| | | }); |
| | | // 点击定位点位触发 |
| | | if (props.dotData.id === item.id) { |
| | | currentClickEntity = entity; |
| | | activeEntity.value = entity; |
| | | singleMachineEvent(); |
| | | } |
| | | |
| | | dataPointEntities.value.push(entity); |
| | | }); |
| | | }; |
| | | const initMap = () => { |
| | | if (viewer || isMapInitialized.value) return; |
| | | |
| | | const container = document.getElementById('dataCenterMap'); |
| | | if (!container) { |
| | | console.error('地图容器未找到'); |
| | | return; |
| | | } |
| | | try { |
| | | const publicCesiumInstance = new PublicCesium({ |
| | | dom: 'dataCenterMap', |
| | | flatMode: false, |
| | | terrain: false, |
| | | mapFilter: true, |
| | | }); |
| | | viewer = publicCesiumInstance.getViewer(); |
| | | viewerRef.value = viewer; |
| | | |
| | | isViewerReady.value = true; |
| | | |
| | | // 初始化事件处理器 |
| | | handlerInit(); |
| | | isMapInitialized.value = true; |
| | | console.log('地图初始化完成'); |
| | | // 初始化后立即渲染已有数据 |
| | | if (dataPointList.value.length > 0) { |
| | | renderDataPoint(dataPointList.value); |
| | | } |
| | | } catch (error) { |
| | | console.error('地图初始化失败:', error); |
| | | } |
| | | }; |
| | | const flyToEntity = position => { |
| | | const longitude = Number(position.lng); |
| | | const latitude = Number(position.lat); |
| | | viewer?.camera.flyTo({ |
| | | destination: Cesium.Cartesian3.fromDegrees(longitude, latitude, 1000), |
| | | duration: 1, |
| | | orientation: { |
| | | heading: Cesium.Math.toRadians(0.0), |
| | | pitch: Cesium.Math.toRadians(-90.0), |
| | | roll: 0.0, |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | // 地图接口 |
| | | const loading = ref(false); |
| | | const getMapInfoAPIFun = async ids => { |
| | | try { |
| | | const res = await getMapInfoAPI(ids); |
| | | dataPointList.value = res.data.data || []; |
| | | console.log('dataPointList.value', dataPointList.value); |
| | | |
| | | // 确保地图已初始化后再渲染 |
| | | if (isMapInitialized.value && viewer) { |
| | | renderDataPoint(dataPointList.value); |
| | | } |
| | | } catch (error) { |
| | | console.error('获取地图数据失败:', error); |
| | | } |
| | | }; |
| | | const isViewerReady = ref(false); |
| | | |
| | | /** |
| | | * 初始化标注添加 |
| | | * @param data 数据 |
| | | */ |
| | | const initEntityOrPopup = data => { |
| | | console.log('data', data); |
| | | |
| | | //地图点在范围内 |
| | | watch( |
| | | () => isMapInitialized.value, |
| | | ready => { |
| | | if (ready) { |
| | | flyToEntity(data.metadata.shootPosition); |
| | | labelBoxUpdate(); |
| | | } |
| | | }, |
| | | { deep: true, immediate: true } // 初始化时立即执行 |
| | | ); |
| | | }; |
| | | watch( |
| | | () => props.jobId, |
| | | newVal => { |
| | | if (newVal) { |
| | | getMapInfoAPIFun(newVal); |
| | | } |
| | | }, |
| | | { immediate: true } |
| | | ); |
| | | |
| | | // 监听对话框状态 |
| | | watch(isShow, newVal => { |
| | | if (newVal) { |
| | | nextTick(() => { |
| | | initMap(); |
| | | }); |
| | | } else { |
| | | // 清理资源 |
| | | if (viewer) { |
| | | viewer.destroy(); |
| | | viewer = null; |
| | | } |
| | | isMapInitialized.value = false; |
| | | removeHandler(); |
| | | clearDataPoints(); |
| | | } |
| | | }); |
| | | |
| | | onMounted(() => {}); |
| | | onBeforeUnmount(() => { |
| | | if (viewer) { |
| | | viewer.destroy(); |
| | | } |
| | | removeHandler(); |
| | | clearDataPoints(); |
| | | }); |
| | | |
| | | // 暴露方法给父组件 |
| | | defineExpose({ |
| | | initEntityOrPopup, |
| | | }); |
| | | </script> |
| | | |
| | | <style> |
| | | .mapDialog .el-dialog__body { |
| | | height: 700px !important; |
| | | padding: 0 !important; |
| | | } |
| | | </style> |
| | | |
| | | <style scoped lang="scss"> |
| | | .mapBox { |
| | | z-index: 2; |
| | | height: 650px; |
| | | width: 98%; |
| | | position: absolute; |
| | | left: 13px; |
| | | top: 50px; |
| | | |
| | | #dataCenterMap { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <div class="search-box-test"> |
| | | <el-form :model="searchForm" inline> |
| | | <div class="search-first"> |
| | | <el-form-item label="行政区划:"> |
| | | <el-tree-select |
| | | popper-class="custom-tree-select" |
| | | v-model="searchForm.areaCode" |
| | | :data="deptTreeData" |
| | | :default-expanded-keys="[searchForm.areaCode]" |
| | | check-strictly |
| | | node-key="id" |
| | | :props="treeProps" |
| | | @node-click="handleNodeClick" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="所属机巢:"> |
| | | <el-select |
| | | :teleported="false" |
| | | v-model="searchForm.deviceSn" |
| | | placeholder="请选择" |
| | | clearable |
| | | > |
| | | <el-option |
| | | v-for="item in machineData" |
| | | :key="item.device_sn" |
| | | :label="item.nickname" |
| | | :value="item.device_sn" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="任务名称:"> |
| | | <el-input v-model="searchForm.jobName" placeholder="请输入" clearable /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-date-picker |
| | | popper-class="custom-date-picker" |
| | | v-model="dateRange" |
| | | type="daterange" |
| | | :clearable="false" |
| | | range-separator="至" |
| | | start-placeholder="开始日期" |
| | | end-placeholder="结束日期" |
| | | :value-format="timeFormat" |
| | | @change="handleDateChange" |
| | | :popper-options="{ |
| | | modifiers: [ |
| | | { |
| | | name: 'flip', |
| | | options: { |
| | | fallbackPlacements: ['bottom-start'], // 优先向下弹出 |
| | | allowedAutoPlacements: ['bottom-start'], // 禁止其他方向 |
| | | }, |
| | | }, |
| | | ], |
| | | }" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="文件格式:"> |
| | | <el-select |
| | | :teleported="false" |
| | | v-model="searchForm.resultType" |
| | | placeholder="请选择" |
| | | clearable |
| | | > |
| | | <el-option |
| | | v-for="item in fileFormatOption" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="文件类别:"> |
| | | <el-select |
| | | :teleported="false" |
| | | v-model="searchForm.photoType" |
| | | placeholder="请选择" |
| | | clearable |
| | | > |
| | | <el-option |
| | | v-for="item in CategoryOption" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </div> |
| | | <div class="search-first"> |
| | | <el-form-item label="文件名称:"> |
| | | <el-input v-model="searchForm.name" placeholder="请输入" clearable /> |
| | | </el-form-item> |
| | | <div class="search-btn"> |
| | | <el-button type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button> |
| | | <el-button icon="el-icon-refresh" @click="handleReset">清空</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="search-first"> |
| | | <div class="search-btn"> |
| | | <el-button type="primary" icon="el-icon-download" @click="allDownloadFun">全部下载</el-button> |
| | | <el-button type="success" plain icon="el-icon-download" @click="downloadFun">下载</el-button> |
| | | </div> |
| | | </div> |
| | | </el-form> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { pxToRem } from '@/utils/rem'; |
| | | import { ElMessage } from 'element-plus'; |
| | | import dayjs from 'dayjs'; |
| | | import { useStore } from 'vuex'; |
| | | import { getRegionTreeAll, getDeviceRegion, deptsByAreaCode } from '@/api/job/task'; |
| | | const store = useStore(); |
| | | const userAreaCode = computed(() => store.getters.userInfo.detail.areaCode); |
| | | const selectedAreaCode = computed(() => store.state.user.selectedAreaCode); |
| | | const emit = defineEmits(['search','downFun','allDownFun']); |
| | | const treeProps = { |
| | | label: 'name', |
| | | value: 'id', |
| | | children: 'childrens', |
| | | }; |
| | | const fileFormatOption = [ |
| | | { |
| | | value: '', |
| | | label: '全部', |
| | | }, |
| | | { |
| | | value: '0', |
| | | label: '照片', |
| | | }, |
| | | { |
| | | value: '1', |
| | | label: '视频', |
| | | }, |
| | | { |
| | | value: '2', |
| | | label: 'AI识别', |
| | | }, |
| | | { |
| | | value: '5', |
| | | label: '全景', |
| | | }, |
| | | { |
| | | value: '4', |
| | | label: '正射', |
| | | }, |
| | | ]; |
| | | const CategoryOption = [ |
| | | { |
| | | value: 'visible', |
| | | label: '可见光', |
| | | }, |
| | | { |
| | | value: 'ir', |
| | | label: '红外', |
| | | }, |
| | | ]; |
| | | const startTime = dayjs().subtract(6, 'day').startOf('day') |
| | | const endTime = dayjs().endOf('day') |
| | | const timeRange = [startTime.format('YYYY-MM-DD HH:mm:ss'), endTime.format('YYYY-MM-DD HH:mm:ss')] |
| | | const dateRange = ref(timeRange); |
| | | const timeFormat = 'YYYY-MM-DD HH:mm:ss'; |
| | | const searchForm = reactive({ |
| | | jobName: '', //任务名称 |
| | | name: '', //文件名称 |
| | | areaCode: userAreaCode.value, // 区域code |
| | | endTime: endTime.format(timeFormat), // 结束时间 |
| | | startTime: startTime.format(timeFormat), // 开始时间 |
| | | deviceSn: '', // 所属机巢 |
| | | resultType: '', //文件格式 |
| | | photoType: '', //文件类别 |
| | | }); |
| | | |
| | | // 部门 |
| | | let deptTreeData = ref([]); |
| | | // 机巢 |
| | | let machineData = ref([]); |
| | | let deptData = ref([]); |
| | | // 从单机巢任务传参值 |
| | | const signDevice_sn = ref(''); |
| | | // 日期限制 |
| | | const handleDateChange = val => { |
| | | if (val && val.length === 2) { |
| | | const start = dayjs(val[0]); |
| | | const end = dayjs(val[1]); |
| | | const diff = end.diff(start, 'day'); |
| | | |
| | | if (diff > 31) { |
| | | ElMessage.warning('日期范围不能超过31天'); |
| | | dateRange.value = []; |
| | | // 重置为默认时间范围 |
| | | setTimeout(() => { |
| | | dateRange.value = timeRange; |
| | | }, 0); |
| | | return; |
| | | } |
| | | |
| | | // 更新搜索表单中的时间 |
| | | searchForm.startTime = start.format(timeFormat); |
| | | searchForm.endTime = end.format(timeFormat); |
| | | } |
| | | //handleSearch(); |
| | | }; |
| | | // 部门下得机巢 |
| | | const requestDockInfo = () => { |
| | | getRegionTreeAll({ parentCode: userAreaCode.value }).then(res => { |
| | | deptTreeData.value = res.data.data ? [res.data.data] : []; |
| | | |
| | | handleNodeClick({ id: userAreaCode.value }); |
| | | }); |
| | | }; |
| | | const handleNodeClick = async data => { |
| | | // 处理机巢数据 |
| | | searchForm.deviceSn = ''; |
| | | machineData.value = ''; |
| | | |
| | | const droneList = await getDeviceRegion({ areaCode: data.id }); |
| | | |
| | | machineData.value = droneList?.data?.data; |
| | | // 默认选中值 |
| | | if (signDevice_sn.value) { |
| | | searchForm.deviceSn = signDevice_sn.value; |
| | | } |
| | | // 所属部门重新请求值 |
| | | |
| | | deptData.value = []; |
| | | getDeptsByAreaCode(); |
| | | }; |
| | | // 所属部门信息 |
| | | const getDeptsByAreaCode = () => { |
| | | deptsByAreaCode(searchForm.areaCode).then(res => { |
| | | if (res.code !== 0) { |
| | | deptData.value = res.data.data; |
| | | } |
| | | }); |
| | | }; |
| | | // 搜索 |
| | | const handleSearch = () => { |
| | | if (!dateRange.value) { |
| | | dateRange.value = []; |
| | | } |
| | | let params = { |
| | | ...searchForm, |
| | | startTime: dateRange.value.length |
| | | ? dayjs(dateRange?.value[0]).startOf('day').format(timeFormat) |
| | | : null, |
| | | endTime: dateRange.value.length |
| | | ? dayjs(dateRange?.value[1]).endOf('day').format(timeFormat) |
| | | : null, |
| | | }; |
| | | // 调用父组件方法 |
| | | emit('search', params); |
| | | }; |
| | | // 清空 |
| | | const handleReset = () => { |
| | | dateRange.value = timeRange; |
| | | searchForm.jobName = '' |
| | | searchForm.name = '' |
| | | searchForm.areaCode = userAreaCode.value |
| | | searchForm.endTime = endTime.format(timeFormat) |
| | | searchForm.startTime = startTime.format(timeFormat) |
| | | searchForm.deviceSn = '' |
| | | searchForm.resultType = '' |
| | | searchForm.photoType = '' |
| | | handleNodeClick({ id: userAreaCode.value }); |
| | | handleSearch(); |
| | | |
| | | }; |
| | | // 下载 |
| | | const downloadFun =()=>{ |
| | | emit('downFun'); |
| | | } |
| | | // 全部下载 |
| | | const allDownloadFun =()=>{ |
| | | emit('allDownFun'); |
| | | } |
| | | onMounted(() => { |
| | | requestDockInfo(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .search-box-test { |
| | | transition: all 0.3s; |
| | | |
| | | .search-first { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | align-items: center; |
| | | gap: 20px 7px; // 设置行间距和列间距 |
| | | margin-bottom: 10px; |
| | | justify-content: space-between; |
| | | |
| | | .search-btn { |
| | | margin-right: 32px; |
| | | } |
| | | .time-card { |
| | | text-align: center; |
| | | background: #ffffff; |
| | | border-radius: 4px 0px 0px 4px; |
| | | border: 1px solid #e5e5e5; |
| | | font-family: Source Han Sans CN, Source Han Sans CN; |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | display: flex; |
| | | height: 32px; |
| | | |
| | | .card-item { |
| | | width: 60px; |
| | | height: 100%; |
| | | line-height: 32px; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .active { |
| | | background: #ffffff; |
| | | border-radius: 0px 0px 0px 0px; |
| | | border: 1px solid #1c5cff; |
| | | color: #1441ff; |
| | | } |
| | | } |
| | | } |
| | | |
| | | :deep(.el-form) { |
| | | :deep(.el-input__wrapper.is-disabled) { |
| | | box-shadow: 0 0 0 1px #026ad6; |
| | | } |
| | | |
| | | .el-form-item { |
| | | margin-bottom: 0; |
| | | width: 233px; |
| | | |
| | | .el-form-item__label { |
| | | color: #363636; |
| | | line-height: 32px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <div class="dataCenter-table"> |
| | | <searchData |
| | | @search="searchClick" |
| | | @downFun="downloadFile" |
| | | @allDownFun="aLLDownloadFile" |
| | | ></searchData> |
| | | <!-- 表格部分 --> |
| | | <div class="dataTable"> |
| | | <el-table |
| | | v-loading="loadings" |
| | | element-loading-text="加载中" |
| | | stripe |
| | | :data="tableData" |
| | | class="custom-header" |
| | | @selection-change="handleSelectionChange" |
| | | > |
| | | <el-table-column type="selection" width="55" /> |
| | | <el-table-column label="序号" type="index" width="60"> |
| | | <template #default="{ $index }"> |
| | | {{ |
| | | ($index + 1 + (jobListParams.current - 1) * jobListParams.size) |
| | | .toString() |
| | | .padStart(2, '0') |
| | | }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="regionName" label="所属区域" v-if="!isDistrictLevel"> |
| | | <template #default="scope"> |
| | | <span>{{processAddress(scope.row.regionName)}}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column property="nestName" label="所属机巢" /> |
| | | <el-table-column property="jobName" label="任务名称" show-overflow-tooltip /> |
| | | <el-table-column prop="nickName" label="文件名称" show-overflow-tooltip /> |
| | | <el-table-column property="link" label="缩图" width="120"> |
| | | <template #default="scope"> |
| | | <img |
| | | class="quanjing" |
| | | @click="clickpanorama(scope.row)" |
| | | v-if="scope.row?.resultType === 5" |
| | | :src="scope.row?.smallUrl" |
| | | alt="" |
| | | /> |
| | | <img |
| | | v-else-if="scope.row?.resultType === 1" |
| | | :src="convertVideoUrlToThumbnail(scope.row?.link)" |
| | | alt="" |
| | | class="imageBox" |
| | | @click="enterFullScreen(scope.row)" |
| | | /> |
| | | <!-- 正射 --> |
| | | <img |
| | | v-else-if="scope.row?.resultType === 4" |
| | | :src="getzsSmallImg(scope.row?.link)" |
| | | alt="" |
| | | class="imageBox" |
| | | /> |
| | | <el-image |
| | | v-else |
| | | :src="scope.row?.smallUrl" |
| | | :preview-src-list="[scope.row?.showUrl]" |
| | | fit="cover" |
| | | preview-teleported |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="jobTime" label="任务时间" /> |
| | | <el-table-column property="photoType" label="文件类别"> |
| | | <template #default="scope"> |
| | | <span>{{ photoTypeMap[scope.row.photoType] }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column property="resultType" label="文件格式"> |
| | | <template #default="{ row }"> |
| | | <span>{{ resultTypeMap[row?.resultType] }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" width="150" align="center"> |
| | | <template #default="scope"> |
| | | <span class="look" @click="lookDetail(scope.row)">查看</span> |
| | | <span class="delete" @click="deleteDetail(scope.row)" v-if="scope.row.resultType !== 2" |
| | | >删除</span |
| | | > |
| | | <!-- && !isTifFile(scope.row.nickName) |
| | | scope.row.resultType !== 1--> |
| | | <span |
| | | class="location" |
| | | @click="positionDetail(scope.row)" |
| | | v-if="shouldShowLocation(scope.row)" |
| | | >定位</span |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | <!-- 分页 --> |
| | | <div class="pagination"> |
| | | <el-pagination |
| | | v-model:current-page="jobListParams.current" |
| | | v-model:page-size="jobListParams.size" |
| | | :page-sizes="[10, 20, 30, 40]" |
| | | background |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :total="total" |
| | | @size-change="handleSizeChange" |
| | | @current-change="handleCurrentChange" |
| | | /> |
| | | </div> |
| | | <!-- 查看弹框 --> |
| | | <el-dialog v-model="dialogVisible" width="60%" append-to-body @close="dialogClose"> |
| | | <template #header="{ titleId, titleClass }"> |
| | | <div class="my-header"> |
| | | <h4 :id="titleId" :class="titleClass">{{ detailTitle }}</h4> |
| | | </div> |
| | | </template> |
| | | <div class="detailContainer"> |
| | | <div class="leftImg"> |
| | | <img |
| | | v-if="dialogDetailList?.resultType === 1" |
| | | :src="convertVideoUrlToThumbnail(dialogDetailList?.link)" |
| | | alt="" |
| | | class="imageBox" |
| | | /> |
| | | <!-- 地图 --> |
| | | <div |
| | | v-if="dialogDetailList?.resultType === 4" |
| | | id="detaildataCenterMap" |
| | | class="ztzf-cesium" |
| | | ></div> |
| | | <img v-else :src="getSmallImg(dialogDetailList?.link)" alt="" /> |
| | | </div> |
| | | <div class="rightDetail"> |
| | | <div class="title"> |
| | | <div class="inputEdit"> |
| | | 文件名称:<span class="fileTitle" v-if="!dialogDetailList?.checkedinput">{{ |
| | | dialogDetailList?.editName |
| | | }}</span> |
| | | <el-input |
| | | v-else |
| | | v-model="dialogDetailList.editName" |
| | | @keyup.enter="saveTitle()" |
| | | class="title-input" |
| | | clearable |
| | | /> |
| | | </div> |
| | | <div class="editname"> |
| | | <span v-if="!dialogDetailList?.checkedinput" @click="editTitle(dialogDetailList)" |
| | | ><el-icon><Edit /></el-icon |
| | | ></span> |
| | | <div v-else class="suffixBoxEdit"> |
| | | <div class="editText" @click="submitEditSuffix(dialogDetailList)">✔</div> |
| | | <div class="editText" @click="cancelEditSuffix(dialogDetailList)">✖</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div>任务名称:{{ dialogDetailList?.jobName }}</div> |
| | | <div>所属区域:{{ dialogDetailList?.regionName }}</div> |
| | | <div>拍摄机巢:{{ dialogDetailList?.nestName }}</div> |
| | | <div> |
| | | 照片位置:{{ _.round(dialogDetailList?.longitude, 3) }},{{ |
| | | _.round(dialogDetailList?.latitude, 3) |
| | | }} |
| | | </div> |
| | | <div>任务时间:{{ dialogDetailList?.jobTime }}</div> |
| | | <div>拍摄时间:{{ dialogDetailList?.createTime }}</div> |
| | | <div>文件类型:{{ photoTypeMap[dialogDetailList?.photoType] }}</div> |
| | | <div>文件格式:{{ resultTypeMap[dialogDetailList?.resultType] }}</div> |
| | | <div>照片文件大小:{{ bytesToMB(dialogDetailList?.attachSize) }}</div> |
| | | <div> |
| | | <el-button |
| | | type="success" |
| | | plain |
| | | icon="el-icon-download" |
| | | @click="detailDownLoad(dialogDetailList)" |
| | | >下载</el-button |
| | | > |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-dialog> |
| | | <!-- 全景预览 --> |
| | | <PanoramaPopup |
| | | v-model:panoramaParamsShow="panoramaParamsShow" |
| | | v-model:panoramaParamsUrl="panoramaParamsUrl" |
| | | ></PanoramaPopup> |
| | | <!-- 视频预览 --> |
| | | <el-dialog |
| | | :title="currentVideoTitle" |
| | | modal-class="videoDialog" |
| | | append-to-body |
| | | width="54%" |
| | | v-model="VideoShow" |
| | | :close-on-click-modal="false" |
| | | :destroy-on-close="true" |
| | | @close="currentVideoIndex = -1" |
| | | > |
| | | <div class="video-container"> |
| | | <video |
| | | style="width: 100%" |
| | | class="videoBox" |
| | | ref="videoRefs" |
| | | controls |
| | | autoplay |
| | | :src="currentVideoUrl" |
| | | ></video> |
| | | </div> |
| | | </el-dialog> |
| | | <!-- 地图弹框 --> |
| | | <dataCenterMap |
| | | ref="mapComponent" |
| | | v-model:show="dataCenterMapVisible" |
| | | :jobId="jobId" |
| | | @lookDetail="lookDetail" |
| | | :dotData="mapList" |
| | | ></dataCenterMap> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { useStore } from 'vuex'; |
| | | import { PublicCesium } from '@/utils/cesium/publicCesium'; |
| | | import { Cartesian3 } from 'cesium'; |
| | | import * as Cesium from 'cesium'; |
| | | import EventPopUpBox from '@/hooks/components/EventPopUpBox.vue'; |
| | | import { nextTick, render } from 'vue'; |
| | | import defaultIcon from '@/assets/images/dataCenter/datamap/eventCompleted.png'; |
| | | const userInfo = computed(() => store.getters.userInfo); |
| | | const isShow = defineModel('show'); |
| | | const viewerRef = shallowRef(null); |
| | | let viewer = null; |
| | | |
| | | const viewInstance = shallowRef(null); |
| | | const store = useStore(); |
| | | const currentAreaPosition = ref({ height: 1987280, latitude: 27.636112, longitude: 115.732975 }); |
| | | let handler = null; |
| | | |
| | | import EventBus from '@/utils/eventBus'; |
| | | import dataCenterMap from '@/views/dataCenter/components/dataCenterMap.vue'; |
| | | import PanoramaPopup from '@/components/PanoramaPopup/PanoramaPopup.vue'; //全景 |
| | | import { ElMessage, ElMessageBox, ElLoading } from 'element-plus'; |
| | | import searchData from '@/views/dataCenter/components/searchData.vue'; |
| | | import fy1 from '@/assets/images/dataCenter/1.jpeg'; |
| | | import _ from 'lodash'; |
| | | import { |
| | | getaiImagesPageAPI, |
| | | getAttachInfoAPI, |
| | | deleteFileMultipleApi, |
| | | downloadApi, |
| | | updataTitleApi, |
| | | getOrthoimageInfo, |
| | | } from '@/api/dataCenter/dataCenter'; |
| | | import { getShowImg, getSmallImg, getzsSmallImg } from '@/utils/util'; |
| | | import { onMounted, watch } from 'vue'; |
| | | import dayjs from 'dayjs'; |
| | | |
| | | |
| | | |
| | | function bytesToMB(bytes, decimalPlaces = 2) { |
| | | if (typeof bytes !== 'number' || bytes < 0) return '无效输入'; |
| | | return (bytes / 1048576).toFixed(decimalPlaces) + ' MB'; |
| | | } |
| | | // 视频一帧 |
| | | function convertVideoUrlToThumbnail(videoUrl) { |
| | | // 检查是否是有效的视频URL |
| | | if (!videoUrl || typeof videoUrl !== 'string') { |
| | | return videoUrl; |
| | | } |
| | | // 替换文件扩展名 |
| | | const thumbnailUrl = videoUrl.replace(/\.mp4$/, '_small.jpg'); |
| | | return thumbnailUrl; |
| | | } |
| | | const resultTypeMap = { |
| | | 0: '照片', |
| | | 1: '视频', |
| | | 2: 'AI识别', |
| | | 5: '全景', |
| | | 4: '正射', |
| | | }; |
| | | const photoTypeMap = { |
| | | visible: '可见光', |
| | | ir: '红外', |
| | | }; |
| | | // 判断省市区 |
| | | const areaCode = userInfo.value?.detail?.areaCode || ''; |
| | | |
| | | // 判断账号级别 |
| | | const isProvinceLevel = computed(() => areaCode.endsWith('00000000')); // 省级:后8位为0 |
| | | const isCityLevel = computed(() => !isProvinceLevel.value && areaCode.endsWith('000000')); // 市级:后6位为0 |
| | | const isDistrictLevel = computed(() => !isProvinceLevel.value && !isCityLevel.value); // 区县级 |
| | | |
| | | // 处理区域名称的函数 |
| | | function processAddress(str) { |
| | | if (!str) return ''; |
| | | |
| | | const parts = str.split(','); |
| | | |
| | | // 省级账号:显示市和区(去除省级) |
| | | if (isProvinceLevel.value) { |
| | | return parts.filter(part => !part.endsWith('省')).join(','); |
| | | } |
| | | |
| | | // 市级账号:只显示区(去除省和市) |
| | | if (isCityLevel.value) { |
| | | return parts.filter(part => !part.endsWith('省') && !part.endsWith('市')).join(','); |
| | | } |
| | | |
| | | // 区县级账号:不显示区域信息(在模板中已隐藏该列) |
| | | return ''; |
| | | } |
| | | const showRegionColumn = computed(() => { |
| | | // 检查表格数据中是否有任何行的 regionName 处理后不为空 |
| | | return tableData.value.some(row => { |
| | | const processed = processAddress(row.regionName || ''); |
| | | return processed !== ''; |
| | | }); |
| | | }); |
| | | const loadings = ref(true); |
| | | let loading; |
| | | const total = ref(0); |
| | | const startTime = dayjs().subtract(6, 'day').startOf('day'); |
| | | const endTime = dayjs().endOf('day'); |
| | | const timeRange = [startTime.format('YYYY-MM-DD HH:mm:ss'), endTime.format('YYYY-MM-DD HH:mm:ss')]; |
| | | // 全景预览 |
| | | const panoramaParamsShow = ref(false); |
| | | const panoramaParamsUrl = ref(null); |
| | | const clickpanorama = val => { |
| | | panoramaParamsShow.value = true; |
| | | panoramaParamsUrl.value = val.link; |
| | | }; |
| | | // 视频 |
| | | const currentVideoTitle = ref(''); |
| | | const VideoShow = ref(false); |
| | | const currentVideoUrl = ref(null); |
| | | const enterFullScreen = val => { |
| | | currentVideoTitle.value = val?.nickName; |
| | | currentVideoUrl.value = val?.link; |
| | | VideoShow.value = true; |
| | | }; |
| | | const jobListParams = reactive({ |
| | | current: 1, |
| | | size: 10, |
| | | orderByCreateTime: true, |
| | | searchParams: { startTime: timeRange[0], endTime: timeRange[1] }, |
| | | }); |
| | | const tableData = ref([]); |
| | | |
| | | // 获取列表数据 |
| | | const getaiImagesPage = () => { |
| | | loadings.value = true; |
| | | const params = { |
| | | orderByCreateTime: jobListParams.orderByCreateTime, |
| | | ...jobListParams.searchParams, |
| | | }; |
| | | getaiImagesPageAPI(params, { current: jobListParams.current, size: jobListParams.size }).then( |
| | | res => { |
| | | total.value = res.data.data.total; |
| | | tableData.value = res.data.data.records.map(i => ({ |
| | | ...i, |
| | | checked: false, |
| | | url: i?.link, |
| | | smallUrl: getSmallImg(i?.link), |
| | | showUrl: getShowImg(i?.link), |
| | | file_name: i.name.split('/').pop(), |
| | | })); |
| | | // console.log('res', tableData.value); |
| | | loadings.value = false; |
| | | } |
| | | ); |
| | | }; |
| | | // 查询 |
| | | const searchClick = params => { |
| | | jobListParams.current = 1; |
| | | jobListParams.size = 10; |
| | | jobListParams.searchParams = params; |
| | | getaiImagesPage(); |
| | | }; |
| | | const handleSizeChange = val => { |
| | | jobListParams.size = val; |
| | | getaiImagesPage(); |
| | | }; |
| | | const handleCurrentChange = val => { |
| | | jobListParams.current = val; |
| | | getaiImagesPage(); |
| | | }; |
| | | // 多选 |
| | | const selectedRows = ref([]); |
| | | const handleSelectionChange = val => { |
| | | // 更新选中状态 |
| | | tableData.value.forEach(item => { |
| | | item.checked = val.some(selected => selected.id === item.id); |
| | | }); |
| | | |
| | | selectedRows.value = val; |
| | | }; |
| | | // 删除 |
| | | const deleteDetail = val => { |
| | | ElMessageBox.confirm('您确定删除吗?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning', |
| | | }) |
| | | .then(() => { |
| | | deleteFileMultipleApi(val.id) |
| | | .then(res => { |
| | | ElMessage.success('删除成功'); |
| | | getaiImagesPage(); |
| | | }) |
| | | .catch(error => { |
| | | ElMessage.error('删除失败'); |
| | | }); |
| | | }) |
| | | .catch(() => {}); |
| | | }; |
| | | // url下载 |
| | | function aLinkDownload(url, name) { |
| | | const a = document.createElement('a'); |
| | | a.style.display = 'none'; |
| | | a.href = url; |
| | | a.download = name; |
| | | document.body.appendChild(a); |
| | | a.click(); |
| | | document.body.removeChild(a); |
| | | } |
| | | const fileDownload = () => { |
| | | const list = selectedRows.value.filter(i => i.checked); |
| | | if (!list?.length) return ElMessage.warning('请选择文件'); |
| | | if (list.length === 1) { |
| | | list.forEach((item, index) => { |
| | | setTimeout(() => { |
| | | aLinkDownload(item.url, item?.nickName); |
| | | }, index * 500); // 每个文件下载间隔50毫秒 |
| | | }); |
| | | } else { |
| | | loading = ElLoading.service({ background: 'rgba(0, 0, 0, 0.5)', text: '打包中,请稍等...' }); |
| | | const fileIds = list.map(i => i.id); |
| | | let aaa = { |
| | | areaCode: '', |
| | | attachIds: fileIds, |
| | | dockSn: '', |
| | | endTime: '', |
| | | fileName: '', |
| | | fileType: '', |
| | | jobName: '', |
| | | resultType: '', |
| | | startTime: '', |
| | | wayLineJobIds: [], |
| | | }; |
| | | |
| | | downloadApi(aaa).then(res => { |
| | | aLinkDownload(res.data.data, `sjzx-file-pack-${dayjs().format('YYYYMMDDHHmmss')}.zip`); |
| | | loading.close(); |
| | | }); |
| | | } |
| | | }; |
| | | // 下载 |
| | | const downloadFile = () => { |
| | | fileDownload(); |
| | | }; |
| | | const detailDownLoad = val => { |
| | | aLinkDownload(val.link, val?.nickName); |
| | | }; |
| | | // 全部下载 |
| | | const aLLDownloadFile = () => { |
| | | loading = ElLoading.service({ background: 'rgba(0, 0, 0, 0.5)', text: '打包中,请稍等...' }); |
| | | |
| | | const params = { |
| | | ...jobListParams.searchParams, |
| | | }; |
| | | params.areaCode = ''; |
| | | downloadApi(params).then(res => { |
| | | aLinkDownload(res.data.data, `sjzx-file-pack-${dayjs().format('YYYYMMDDHHmmss')}.zip`); |
| | | loading.close(); |
| | | }); |
| | | }; |
| | | // 地图弹框 |
| | | const mapComponent = ref(null); // 创建子组件引用 |
| | | const mapList = ref(null); |
| | | const dataCenterMapVisible = ref(false); |
| | | const jobId = ref(''); |
| | | const statusType = ref(null); |
| | | const positionDetail = val => { |
| | | jobId.value = val.wayLineJobId; |
| | | mapList.value = val; |
| | | dataCenterMapVisible.value = true; |
| | | // 确保地图组件加载完成 |
| | | nextTick(() => { |
| | | if (mapComponent.value) { |
| | | // 调用子组件方法并传递数据 |
| | | mapComponent.value.initEntityOrPopup(val); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const fileNameedit = ref(''); |
| | | // 编辑文件名 |
| | | const editTitle = val => { |
| | | val.checkedinput = true; |
| | | fileNameedit.value = val?.nickName; |
| | | }; |
| | | // 取消 |
| | | const cancelEditSuffix = item => { |
| | | item.nickName = fileNameedit.value; |
| | | item.checkedinput = false; |
| | | }; |
| | | const submitEditSuffix = item => { |
| | | saveTitle(item); |
| | | }; |
| | | // 通用空值检查函数 |
| | | const validateNickname = (name, fieldName) => { |
| | | if (!name || name.trim() === '') { |
| | | ElMessage.warning(`${fieldName}不能为空`); |
| | | return false; |
| | | } |
| | | if (name.length > 50) { |
| | | ElMessage.warning(`${fieldName}不能超过50个字符`); |
| | | return false; |
| | | } |
| | | return true; |
| | | }; |
| | | // 保存文件名 |
| | | const saveTitle = item => { |
| | | const updateparams = { |
| | | id: item.id, |
| | | nickName: `${item.editName.trim()}`, |
| | | }; |
| | | // 验证并提示 |
| | | if (!validateNickname(updateparams.nickName, '名称')) return; |
| | | item.checkedinput = false; |
| | | detailTitle.value = item.editName; |
| | | updataTitleApi(updateparams) |
| | | .then(res => { |
| | | if (res.status === 200) { |
| | | ElMessage.success('修改成功'); |
| | | getaiImagesPage() |
| | | } else { |
| | | ElMessage.error(res.data.message || '修改失败'); |
| | | } |
| | | }) |
| | | .catch(error => { |
| | | ElMessage.error('请求失败,请稍后重试'); |
| | | console.error('API error:', error); |
| | | }); |
| | | }; |
| | | // 正射地图 |
| | | const yxShowBox = ref(false); |
| | | |
| | | const removeHandler = () => { |
| | | handler?.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK); |
| | | handler?.destroy(); |
| | | handler = null; |
| | | }; |
| | | const isMapInitialized = ref(false); //地图加载 |
| | | const initMap = () => { |
| | | if (viewer || isMapInitialized.value) return; |
| | | |
| | | const container = document.getElementById('detaildataCenterMap'); |
| | | if (!container) { |
| | | console.error('地图容器未找到'); |
| | | return; |
| | | } |
| | | viewInstance.value = new PublicCesium({ |
| | | dom: 'detaildataCenterMap', |
| | | flatMode: false, |
| | | terrain: false, |
| | | mapFilter: true, |
| | | }); |
| | | viewer = viewInstance.value.getViewer(); |
| | | viewerRef.value = viewer; |
| | | |
| | | isMapInitialized.value = true; |
| | | |
| | | // console.log('地图初始化完成'); |
| | | }; |
| | | // 查看弹框 |
| | | function isTifFile(filename) { |
| | | const lastDot = filename.lastIndexOf('.'); |
| | | if (lastDot === -1) return false; // 无后缀 |
| | | return filename.slice(lastDot + 1).toLowerCase() === 'tif'; |
| | | } |
| | | const shouldShowLocation = row => { |
| | | return row.resultType !== 1 && row.resultType !== 4; |
| | | }; |
| | | const dialogVisible = ref(false); |
| | | const dialogDetailList = ref(null); |
| | | const detailTitle = ref(''); |
| | | let curCustomImageryProvider = null; |
| | | const orthoimageApi = ref(''); |
| | | const odmToken = ref(null); |
| | | const lookDetail = val => { |
| | | if (val.resultType === 4) { |
| | | // 正射 |
| | | getOrthoimageInfo(val.wayLineJobId).then(async res => { |
| | | dialogVisible.value = true; |
| | | detailTitle.value = val.nickName; |
| | | dialogDetailList.value = val; |
| | | await nextTick(); |
| | | initMap(); |
| | | orthoimageApi.value = res.data.data.orthoimageApi; |
| | | odmToken.value = res.data.data.odmToken; |
| | | if (res.data.data != null && isMapInitialized.value === true) { |
| | | curCustomImageryProvider = viewInstance.value.addCustomImageryProviderDataSource( |
| | | new Cesium.UrlTemplateImageryProvider({ |
| | | url: `${import.meta.env.VITE_APP_AREA_NAME}/webodm${res.data.data.orthoimageApi}?jwt=${ |
| | | res.data.data.odmToken.split(' ')[1] |
| | | }`, |
| | | // maximumLevel: 12, |
| | | }) |
| | | ); |
| | | yxShowBox.value = true; |
| | | } |
| | | }); |
| | | } else { |
| | | getAttachInfoAPI(val.id).then(res => { |
| | | detailTitle.value = res.data.data.nickName.split('.jpeg')[0]; |
| | | // dialogDetailList.value = res.data.data; |
| | | dialogDetailList.value = { |
| | | ...res.data.data, |
| | | checkedinput: false, |
| | | editName: res.data.data?.nickName?.split('.jpeg')[0], |
| | | }; |
| | | dialogVisible.value = true; |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | function dialogClose() { |
| | | // 对话框关闭时清理资源 |
| | | viewInstance.value?.viewerDestroy(); |
| | | viewer = null; |
| | | isMapInitialized.value = false; |
| | | } |
| | | onMounted(() => { |
| | | getaiImagesPage(); |
| | | // 监听打开全景事件 |
| | | EventBus.on('open-panorama', params => { |
| | | panoramaParamsShow.value = params.show; |
| | | panoramaParamsUrl.value = params.url; |
| | | }); |
| | | }); |
| | | onBeforeUnmount(() => { |
| | | // 组件卸载时移除事件监听,防止内存泄漏 |
| | | EventBus.off('open-panorama'); |
| | | viewInstance.value?.removeAllCustomImageryProviderDataSource(curCustomImageryProvider); |
| | | viewInstance.value?.viewerDestroy(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .dataCenter-table { |
| | | margin: 0 18px 16px 10px; |
| | | background-color: #ffffff; |
| | | padding: 14px 18px; |
| | | .dataTable { |
| | | height: 700px; |
| | | overflow: auto; |
| | | .look { |
| | | color: #1c5cff; |
| | | cursor: pointer; |
| | | margin-right: 10px; |
| | | } |
| | | .delete { |
| | | color: #ff241c; |
| | | margin-right: 10px; |
| | | cursor: pointer; |
| | | } |
| | | .location { |
| | | color: #19876d; |
| | | cursor: pointer; |
| | | } |
| | | } |
| | | .pagination { |
| | | display: flex; |
| | | justify-content: end; |
| | | margin-top: 20px; |
| | | } |
| | | .quanjing, |
| | | .el-image, |
| | | .imageBox { |
| | | cursor: pointer; |
| | | width: 76px; |
| | | height: 72px; |
| | | } |
| | | .videoDialog :deep(.el-dialog) { |
| | | height: 600px; |
| | | width: 54%; |
| | | } |
| | | .video-container { |
| | | width: 100%; |
| | | aspect-ratio: 16/9; /* 按视频比例设置(如16:9) */ |
| | | overflow: hidden; /* 隐藏溢出部分 */ |
| | | } |
| | | .videoBox { |
| | | width: 100%; |
| | | height: 100%; |
| | | object-fit: contain; /* 保持比例完整显示 */ |
| | | display: block; |
| | | } |
| | | } |
| | | :deep(.custom-header th.el-table__cell) { |
| | | color: rgba(0, 0, 0, 0.85); |
| | | } |
| | | // 弹框 |
| | | .detailContainer { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | .leftImg { |
| | | width: 70%; |
| | | height: 500px; |
| | | img { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | #detaildataCenterMap { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | } |
| | | .rightDetail { |
| | | width: 30%; |
| | | padding-left: 40px; |
| | | .title { |
| | | display: flex; |
| | | margin: 0 !important; |
| | | .editname { |
| | | cursor: pointer; |
| | | } |
| | | .inputEdit { |
| | | display: flex; |
| | | align-items: center; |
| | | width: 100%; |
| | | .fileTitle { |
| | | width: 70%; |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | } |
| | | } |
| | | } |
| | | div { |
| | | margin-bottom: 20px; |
| | | } |
| | | } |
| | | } |
| | | .my-header :deep(.el-dialog__title) { |
| | | margin: 0 !important; |
| | | height: 19px; |
| | | } |
| | | .my-header { |
| | | display: flex; |
| | | align-items: center; |
| | | .el-button { |
| | | margin-left: 20px; |
| | | } |
| | | } |
| | | .title-input { |
| | | margin-bottom: 0 !important; |
| | | width: 70%; |
| | | } |
| | | .editname { |
| | | margin-bottom: 0 !important; |
| | | } |
| | | .suffixBoxEdit { |
| | | display: flex; |
| | | // align-items: center; |
| | | justify-content: space-between; |
| | | margin-bottom: 0 !important; |
| | | font-size: 16px; |
| | | > .editText { |
| | | cursor: pointer; |
| | | margin-right: 6px; |
| | | &:hover { |
| | | transform: scale(1.2); |
| | | } |
| | | } |
| | | .editimg { |
| | | cursor: pointer; |
| | | width: 15px; |
| | | height: 15px; |
| | | &:hover { |
| | | transform: scale(1.2); |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <basic-container> |
| | | <avue-crud |
| | | :option="option" |
| | | :table-loading="loading" |
| | | :data="data" |
| | | v-model:page="page" |
| | | ref="crud" |
| | | @row-del="rowDel" |
| | | v-model="form" |
| | | :permission="permissionList" |
| | | @row-update="rowUpdate" |
| | | @row-save="rowSave" |
| | | :before-open="beforeOpen" |
| | | @search-change="searchChange" |
| | | @search-reset="searchReset" |
| | | @selection-change="selectionChange" |
| | | @current-change="currentChange" |
| | | @size-change="sizeChange" |
| | | @refresh-change="refreshChange" |
| | | @on-load="onLoad" |
| | | @tree-load="treeLoad" |
| | | > |
| | | <avue-crud :option="option" :table-loading="loading" :data="data" v-model:page="page" ref="crud" @row-del="rowDel" |
| | | v-model="form" :permission="permissionList" @row-update="rowUpdate" @row-save="rowSave" :before-open="beforeOpen" |
| | | @search-change="searchChange" @search-reset="searchReset" @selection-change="selectionChange" |
| | | @current-change="currentChange" @size-change="sizeChange" @refresh-change="refreshChange" @on-load="onLoad" |
| | | @tree-load="treeLoad"> |
| | | <template #menu-left> |
| | | <el-button |
| | | type="danger" |
| | | icon="el-icon-delete" |
| | | plain |
| | | v-if="permission.device_delete" |
| | | @click="handleDelete" |
| | | >删 除 |
| | | <el-button type="danger" icon="el-icon-delete" plain v-if="permission.device_delete" @click="handleDelete">删 除 |
| | | </el-button> |
| | | </template> |
| | | <template #dept_name="{ row }"> |
| | |
| | | <template #firmware_status="{ row }"> |
| | | <div v-if="row.firmware_status == 1" class="onlineStatus1"> |
| | | <el-tag type="info">无需升级</el-tag> |
| | | <el-tag type="primary" effect="dark" class="active-element" @click="rollFirmware(row)" |
| | | >版本管理</el-tag |
| | | > |
| | | <el-tag type="primary" effect="dark" class="active-element" @click="rollFirmware(row)">版本管理</el-tag> |
| | | <!-- <div style="background-color: #f1f1f1; border-radius: 4px">无需升级</div> --> |
| | | </div> |
| | | <div v-if="row.firmware_status == 2" class="onlineStatus1"> |
| | | <!-- <el-tag type="success" effect="dark" class="active-element" @click="updateFirmware(row)" |
| | | >升级固件</el-tag |
| | | > --> |
| | | <el-tag type="primary" effect="dark" class="active-element" @click="rollFirmware(row)" |
| | | >版本管理</el-tag |
| | | > |
| | | <el-tag type="primary" effect="dark" class="active-element" @click="rollFirmware(row)">版本管理</el-tag> |
| | | <!-- <div @click="updateFirmware(row)" class="firmware_status">升级固件</div> --> |
| | | </div> |
| | | <div v-if="row.firmware_status == 3" class="onlineStatus"> |
| | | <el-tag type="success" effect="dark" class="active-element" @click="updateFirmware(row)" |
| | | >一致性升级</el-tag |
| | | > |
| | | <el-tag type="success" effect="dark" class="active-element" @click="updateFirmware(row)">一致性升级</el-tag> |
| | | <!-- <div @click="updateFirmware(row)" class="firmware_status">一致性升级</div> --> |
| | | </div> |
| | | <div v-if="row.firmware_status == 4" class="onlineStatus"> |
| | | <el-tag effect="dark" class="active-element" @click="updateFirmware(row)">升级中</el-tag> |
| | | <el-progress |
| | | v-if="row.firmware_progress > 0 && row.firmware_progress < 100" |
| | | :percentage="row.firmware_progress" |
| | | ></el-progress> |
| | | <el-progress v-if="row.firmware_progress > 0 && row.firmware_progress < 100" |
| | | :percentage="row.firmware_progress"></el-progress> |
| | | </div> |
| | | </template> |
| | | <template #mode_code="{ row }"> |
| | | <span |
| | | class="text" |
| | | v-if="row.domain == 3" |
| | | :style="row.mode_code != '-1' ? 'color: #00ee8b' : 'color: #de5e5e'" |
| | | > |
| | | <span class="text" v-if="row.domain == 3" :style="row.mode_code != '-1' ? 'color: #00ee8b' : 'color: #de5e5e'"> |
| | | {{ getDockModeText(row.mode_code) }} |
| | | </span> |
| | | </template> |
| | | <template #menu="scope"> |
| | | <el-button |
| | | type="primary" |
| | | text |
| | | icon="el-icon-paperclip" |
| | | v-if="permission.oss_set" |
| | | @click.stop="handleOpenOssSet(scope.row, scope.index)" |
| | | >存储配置 |
| | | <el-button type="primary" text icon="el-icon-paperclip" v-if="permission.oss_set" |
| | | @click.stop="handleOpenOssSet(scope.row, scope.index)">存储配置 |
| | | </el-button> |
| | | <el-button |
| | | type="primary" |
| | | text |
| | | icon="el-icon-share" |
| | | v-if="permission.per_share && scope.row.domain == 3" |
| | | @click.stop="handleOpenDevicePerShare(scope.row, scope.index)" |
| | | >机场授权 |
| | | <el-button type="primary" text icon="el-icon-share" v-if="permission.per_share && scope.row.domain == 3" |
| | | @click.stop="handleOpenDevicePerShare(scope.row, scope.index)">机场授权 |
| | | </el-button> |
| | | |
| | | <el-button |
| | | type="primary" |
| | | text |
| | | icon="el-icon-position" |
| | | :disabled="!scope.row.status" |
| | | <el-button type="primary" text icon="el-icon-position" :disabled="!scope.row.status" |
| | | v-if="permission.rang_con && scope.row.domain == 3" |
| | | @click.stop="handleOpenRemoteDebugging(scope.row, scope.index)" |
| | | >远程调试 |
| | | @click.stop="handleOpenRemoteDebugging(scope.row, scope.index)">远程调试 |
| | | </el-button> |
| | | </template> |
| | | <!-- 添加行政区划显示模板 --> |
| | |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <el-dialog |
| | | title="存储配置" |
| | | append-to-body |
| | | v-model="ossSetBox" |
| | | @close="handleOssSetClose" |
| | | width="600px" |
| | | > |
| | | <el-dialog title="存储配置" append-to-body v-model="ossSetBox" @close="handleOssSetClose" width="600px"> |
| | | <div class="onlineStatus"> |
| | | 存储对象配置: |
| | | <el-select v-model="ossEnd" clearable placeholder="请选择存储对象" style="width: 450px"> |
| | | <el-option |
| | | v-for="item in ossList" |
| | | :key="item.id" |
| | | :label="item.name + ': ' + item.endpoint" |
| | | :value="item.id" |
| | | > |
| | | <el-option v-for="item in ossList" :key="item.id" :label="item.name + ': ' + item.endpoint" :value="item.id"> |
| | | <span style="float: left">{{ item.name + ': ' + item.endpoint }}</span> |
| | | <span v-if="item.status == 1" class="status1">{{ item.categoryName }}</span> |
| | | <span v-if="item.status == 2" class="status2">{{ item.categoryName }}</span> |
| | |
| | | <el-dialog title="固件版本管理" append-to-body v-model="rollFirmwareBox" width="455px"> |
| | | <div class="onlineStatus"> |
| | | 固件版本升级/回退: |
| | | <el-select |
| | | v-model="rollFirmVersion" |
| | | clearable |
| | | placeholder="请选择固件版本" |
| | | style="width: 240px" |
| | | > |
| | | <el-option |
| | | v-for="item in firmList" |
| | | :key="item.firmware_version" |
| | | :label="item.firmware_version" |
| | | :value="item.firmware_version" |
| | | /> |
| | | <el-select v-model="rollFirmVersion" clearable placeholder="请选择固件版本" style="width: 240px"> |
| | | <el-option v-for="item in firmList" :key="item.firmware_version" :label="item.firmware_version" |
| | | :value="item.firmware_version" /> |
| | | </el-select> |
| | | </div> |
| | | <template #footer> |
| | |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <el-drawer |
| | | title="机场授权管理" |
| | | size="60%" |
| | | append-to-body |
| | | v-model="devicePerShareVisible" |
| | | :direction="'rtl'" |
| | | > |
| | | <el-drawer title="机场授权管理" size="60%" append-to-body v-model="devicePerShareVisible" :direction="'rtl'"> |
| | | <DevicePerShare ref="devicePerShare" /> |
| | | </el-drawer> |
| | | |
| | | <el-drawer :title="operateTitle" append-to-body v-model="remoteDebuggingShow" size="40%"> |
| | | <DockControlPanel |
| | | v-if="remoteDebuggingShow" |
| | | :sn="curDeviceInfo.device_sn" |
| | | :deviceInfo="curDeviceInfo" |
| | | > |
| | | <DockControlPanel v-if="remoteDebuggingShow" :sn="curDeviceInfo.device_sn" :deviceInfo="curDeviceInfo"> |
| | | </DockControlPanel> |
| | | </el-drawer> |
| | | </basic-container> |
| | |
| | | ota, |
| | | getDeviceFirmwareList, |
| | | getDevices, |
| | | } from '@/api/device/device'; |
| | | import { deleteByOssId, addOrUpdate as addOrUpdateOssBind } from '@/api/device/ossBind'; |
| | | import { getListPage as getOssList } from '@/api/resource/oss'; |
| | | import { getLazyTree } from '@/api/base/region'; |
| | | import { EDockModeText, EDockModeCode } from '@/types/device'; |
| | | import { mapGetters } from 'vuex'; |
| | | import FirmwareManage from './components/firmwareManage.vue'; |
| | | import DevicePerShare from './components/devicePerShare.vue'; |
| | | import DockControlPanel from './components/DockControlPanel.vue'; |
| | | import { getWebsocketUrl } from '@/websocket/util/config'; |
| | | import ConnectWebSocket from '@/websocket'; |
| | | } from '@/api/device/device' |
| | | import { deleteByOssId, addOrUpdate as addOrUpdateOssBind } from '@/api/device/ossBind' |
| | | import { getListPage as getOssList } from '@/api/resource/oss' |
| | | import { getLazyTree } from '@/api/base/region' |
| | | import { EDockModeText, EDockModeCode } from '@/types/device' |
| | | import { mapGetters } from 'vuex' |
| | | import FirmwareManage from './components/firmwareManage.vue' |
| | | import DevicePerShare from './components/devicePerShare.vue' |
| | | import DockControlPanel from './components/DockControlPanel.vue' |
| | | import { getWebsocketUrl } from '@/websocket/util/config' |
| | | import ConnectWebSocket from '@/websocket' |
| | | export default { |
| | | components: { |
| | | FirmwareManage, |
| | | DevicePerShare, |
| | | DockControlPanel, |
| | | }, |
| | | data() { |
| | | data () { |
| | | return { |
| | | bindOssId: null, |
| | | deviceSn: '', |
| | |
| | | ], |
| | | change: ({ value }) => { |
| | | if (!value) { |
| | | this.form.area_code = null; |
| | | this.form.area_code = null |
| | | } else { |
| | | if (typeof value === 'string' && value.includes(',')) { |
| | | const codes = value.split(','); |
| | | this.form.area_code = codes[codes.length - 1]; |
| | | const codes = value.split(',') |
| | | this.form.area_code = codes[codes.length - 1] |
| | | } else { |
| | | this.form.area_code = value; |
| | | this.form.area_code = value |
| | | } |
| | | } |
| | | }, |
| | | lazy: true, |
| | | lazyLoad(node, resolve) { |
| | | let level = node.level; |
| | | let list = []; |
| | | lazyLoad (node, resolve) { |
| | | let level = node.level |
| | | let list = [] |
| | | let callback = () => { |
| | | resolve( |
| | | (list || []).map(ele => ({ |
| | |
| | | value: ele.value, |
| | | leaf: level >= 2, |
| | | })) |
| | | ); |
| | | }; |
| | | ) |
| | | } |
| | | |
| | | if (level === 0) { |
| | | getLazyTree('000000000000').then(res => { |
| | | list = res.data.data; |
| | | callback(); |
| | | }); |
| | | list = res.data.data |
| | | callback() |
| | | }) |
| | | } else if (level === 1) { |
| | | getLazyTree(node.value).then(res => { |
| | | list = res.data.data; |
| | | callback(); |
| | | }); |
| | | list = res.data.data |
| | | callback() |
| | | }) |
| | | } else if (level === 2) { |
| | | getLazyTree(node.value).then(res => { |
| | | list = res.data.data; |
| | | callback(); |
| | | }); |
| | | list = res.data.data |
| | | callback() |
| | | }) |
| | | } else { |
| | | callback(); |
| | | callback() |
| | | } |
| | | }, |
| | | }, |
| | | |
| | | { |
| | | hide: true, |
| | | label: '保险有效期', |
| | | prop: 'duration_of_insurance', |
| | | labelWidth: 130, |
| | | type: "daterange", |
| | | format: 'YYYY-MM-DD', |
| | | valueFormat: 'YYYY-MM-DD HH:mm:ss', |
| | | startPlaceholder: '保险开始日期', |
| | | endPlaceholder: '保险结束日期', |
| | | rules: [ |
| | | { |
| | | required: true, |
| | | message: '请选择保险有效期', |
| | | trigger: 'blur', |
| | | }, |
| | | ], |
| | | }, |
| | | |
| | | { |
| | | label: '在线时间', |
| | | prop: 'login_time', |
| | |
| | | ConsistencyUpgrade: 3, // 一致性升级 |
| | | DuringUpgrade: 4, // 升级中 |
| | | }, |
| | | }; |
| | | } |
| | | }, |
| | | computed: { |
| | | ...mapGetters(['permission']), |
| | | permissionList() { |
| | | permissionList () { |
| | | return { |
| | | addBtn: this.validData(this.permission.airport_add, true), |
| | | viewBtn: this.validData(this.permission.airport_view, true), |
| | | delBtn: this.validData(this.permission.airport_delete, true), |
| | | editBtn: this.validData(this.permission.airport_edit, true), |
| | | }; |
| | | } |
| | | }, |
| | | ids() { |
| | | let ids = []; |
| | | ids () { |
| | | let ids = [] |
| | | this.selectionList.forEach(ele => { |
| | | ids.push(ele.id); |
| | | }); |
| | | return ids.join(','); |
| | | ids.push(ele.id) |
| | | }) |
| | | return ids.join(',') |
| | | }, |
| | | }, |
| | | destroyed() { |
| | | destroyed () { |
| | | this.websocketMap.forEach((k, v) => { |
| | | if (null != v) { |
| | | v.close(); |
| | | this.websocketMap.delete(k); |
| | | this.webSocketIdSet.delete(k); |
| | | v.close() |
| | | this.websocketMap.delete(k) |
| | | this.webSocketIdSet.delete(k) |
| | | } |
| | | }); |
| | | }) |
| | | }, |
| | | methods: { |
| | | getDockModeText(value) { |
| | | return EDockModeText[value] || ''; |
| | | getDockModeText (value) { |
| | | return EDockModeText[value] || '' |
| | | }, |
| | | // 关闭所有的webscoket |
| | | closeAllWebsoket() { |
| | | closeAllWebsoket () { |
| | | this.websocketMap.forEach((k, v) => { |
| | | if (null != v) { |
| | | v.close(); |
| | | this.websocketMap.delete(k); |
| | | this.webSocketIdSet.delete(k); |
| | | v.close() |
| | | this.websocketMap.delete(k) |
| | | this.webSocketIdSet.delete(k) |
| | | } |
| | | }); |
| | | }) |
| | | }, |
| | | // 连接webSocket |
| | | connectWebSocket(data) { |
| | | const webSocketId = data.workspace_id; |
| | | connectWebSocket (data) { |
| | | const webSocketId = data.workspace_id |
| | | if (!this.webSocketIdSet.has(webSocketId)) { |
| | | // 防止重复连接 |
| | | this.webSocketIdSet.add(webSocketId); |
| | | const webSorketUrl = getWebsocketUrl() + '&workspace-id=' + data.workspace_id; |
| | | this.webSocketIdSet.add(webSocketId) |
| | | const webSorketUrl = getWebsocketUrl() + '&workspace-id=' + data.workspace_id |
| | | // 监听ws 消息 |
| | | this.useConnectWebSocket(webSocketId, webSorketUrl); |
| | | this.useConnectWebSocket(webSocketId, webSorketUrl) |
| | | } |
| | | }, |
| | | useConnectWebSocket(webSocketId, url) { |
| | | const websocket = new ConnectWebSocket(url); |
| | | useConnectWebSocket (webSocketId, url) { |
| | | const websocket = new ConnectWebSocket(url) |
| | | // 加入 webscoket map |
| | | this.websocketMap.set(webSocketId, websocket); |
| | | websocket?.registerMessageHandler(this.messageHandler); |
| | | websocket?.initSocket(); |
| | | this.websocketMap.set(webSocketId, websocket) |
| | | websocket?.registerMessageHandler(this.messageHandler) |
| | | websocket?.initSocket() |
| | | }, |
| | | async messageHandler(payload) { |
| | | async messageHandler (payload) { |
| | | if (!payload) { |
| | | return; |
| | | return |
| | | } |
| | | if (payload.biz_code != 'ota_progress') { |
| | | return; |
| | | return |
| | | } |
| | | var data = payload.data; |
| | | var data = payload.data |
| | | this.data.forEach(e => { |
| | | if (e.device_sn == data.sn && data.output.status == 'in_progress') { |
| | | e.firmware_progress = data.output.progress.percent; |
| | | e.firmware_progress = data.output.progress.percent |
| | | } |
| | | |
| | | if (e.device_sn == data.sn && data.output.status == 'ok') { |
| | | // 升级完成修改状态 |
| | | e.firmware_status = 1; |
| | | const webscoket = this.websocketMap.get(e.workspace_id); |
| | | e.firmware_status = 1 |
| | | const webscoket = this.websocketMap.get(e.workspace_id) |
| | | // 关闭 |
| | | webscoket?.close(); |
| | | webscoket?.close() |
| | | //删除对应的webscoket |
| | | this.websocketMap.delete(e.workspace_id); |
| | | this.webSocketIdSet.delete(e.workspace_id); |
| | | this.websocketMap.delete(e.workspace_id) |
| | | this.webSocketIdSet.delete(e.workspace_id) |
| | | } |
| | | }); |
| | | }) |
| | | }, |
| | | init() { |
| | | init () { |
| | | (this.page = { |
| | | pageSize: 10, |
| | | currentPage: 1, |
| | | total: 0, |
| | | }), |
| | | this.onLoad(this.page); |
| | | this.onLoad(this.page) |
| | | }, |
| | | // 打开权限分享页面 |
| | | handleOpenDevicePerShare(row) { |
| | | var that = this; |
| | | this.devicePerShareVisible = true; |
| | | handleOpenDevicePerShare (row) { |
| | | var that = this |
| | | this.devicePerShareVisible = true |
| | | this.$nextTick(() => { |
| | | that.$refs.devicePerShare.init(row); |
| | | }); |
| | | that.$refs.devicePerShare.init(row) |
| | | }) |
| | | }, |
| | | // 打开远程调试 |
| | | handleOpenRemoteDebugging(row) { |
| | | this.curDeviceInfo = row; |
| | | this.remoteDebuggingShow = true; |
| | | this.operateTitle = row.nickname + ' - ' + row.device_sn; |
| | | handleOpenRemoteDebugging (row) { |
| | | this.curDeviceInfo = row |
| | | this.remoteDebuggingShow = true |
| | | this.operateTitle = row.nickname + ' - ' + row.device_sn |
| | | }, |
| | | // 打开存储对象配置页面 |
| | | handleOpenOssSet(row) { |
| | | var that = this; |
| | | this.deviceSn = row.device_sn; |
| | | this.ossSetBox = true; |
| | | this.bindOssId = row.oss_id; |
| | | this.ossEnd = row.oss_id; |
| | | handleOpenOssSet (row) { |
| | | var that = this |
| | | this.deviceSn = row.device_sn |
| | | this.ossSetBox = true |
| | | this.bindOssId = row.oss_id |
| | | this.ossEnd = row.oss_id |
| | | // 查询下拉列表 |
| | | const param = { |
| | | categoryKeys: '1,3,6', |
| | | }; |
| | | } |
| | | getOssList(1, 50, param).then(res => { |
| | | const data = res.data.data; |
| | | that.ossList = data.records; |
| | | }); |
| | | const data = res.data.data |
| | | that.ossList = data.records |
| | | }) |
| | | }, |
| | | // 打开固件管理页面 |
| | | handleOpenFirmwarm(row) { |
| | | var that = this; |
| | | this.firmwareManageVisible = true; |
| | | handleOpenFirmwarm (row) { |
| | | var that = this |
| | | this.firmwareManageVisible = true |
| | | this.$nextTick(() => { |
| | | that.$refs.firmwareManage.init(row); |
| | | }); |
| | | that.$refs.firmwareManage.init(row) |
| | | }) |
| | | }, |
| | | // 版本回退点击事件 |
| | | rollFirmware(row) { |
| | | rollFirmware (row) { |
| | | if (row.status == 0) { |
| | | this.$message({ |
| | | type: 'warning', |
| | | message: '设备不在线!', |
| | | }); |
| | | return; |
| | | }) |
| | | return |
| | | } |
| | | // 如果是机场,查询飞机对应的信息 |
| | | if (row.domain === 3) { |
| | | // 查询子设备名称 |
| | | getDevices(row.workspace_id, row.child_sn).then(res => { |
| | | const data = res.data.data; |
| | | that.firmwareInfo['child_device_name'] = data.device_name; |
| | | that.firmwareInfo['child_version'] = data.firmware_version; |
| | | that.firmwareInfo['child_status'] = data.status; |
| | | }); |
| | | const data = res.data.data |
| | | that.firmwareInfo['child_device_name'] = data.device_name |
| | | that.firmwareInfo['child_version'] = data.firmware_version |
| | | that.firmwareInfo['child_status'] = data.status |
| | | }) |
| | | } |
| | | var that = this; |
| | | this.rollFirmwareBox = true; |
| | | var that = this |
| | | this.rollFirmwareBox = true |
| | | const param = { |
| | | device_name: row.device_name, |
| | | }; |
| | | } |
| | | // 获取固件最新版本信息 |
| | | getDeviceFirmwareList(1, 20, param).then(res => { |
| | | that.firmList = []; |
| | | const data = res.data.data.records; |
| | | that.firmList = [] |
| | | const data = res.data.data.records |
| | | if (data.length === 0) { |
| | | return; |
| | | return |
| | | } |
| | | data.forEach(e => { |
| | | if (e.firmware_version != row.firmware_version) { |
| | | that.firmList.push(e); |
| | | that.firmList.push(e) |
| | | } |
| | | }); |
| | | that.firmwareInfo['device_name'] = row.device_name; |
| | | that.firmwareInfo['sn'] = row.device_sn; |
| | | }) |
| | | that.firmwareInfo['device_name'] = row.device_name |
| | | that.firmwareInfo['sn'] = row.device_sn |
| | | // 3-普通升级,2-一致性升级; |
| | | that.firmwareInfo['firmware_upgrade_type'] = 3; |
| | | that.firmwareInfo['workspaceId'] = row.workspace_id; |
| | | that.firmwareInfo['firmware_version'] = row.firmware_version; |
| | | that.firmwareInfo['status'] = row.status; |
| | | that.firmwareInfo['firmware_upgrade_type'] = 3 |
| | | that.firmwareInfo['workspaceId'] = row.workspace_id |
| | | that.firmwareInfo['firmware_version'] = row.firmware_version |
| | | that.firmwareInfo['status'] = row.status |
| | | // domain 0:飞机 3:机场 |
| | | that.firmwareInfo['domain'] = row.domain; |
| | | that.firmwareInfo['child_sn'] = row.child_sn; |
| | | that.firmwareInfo['now_version'] = row.firmware_version; |
| | | }); |
| | | that.firmwareInfo['domain'] = row.domain |
| | | that.firmwareInfo['child_sn'] = row.child_sn |
| | | that.firmwareInfo['now_version'] = row.firmware_version |
| | | }) |
| | | }, |
| | | // 比较版本大小 |
| | | compare(version1, version2) { |
| | | let arr1 = version1.split('.'); |
| | | let arr2 = version2.split('.'); |
| | | let length = Math.max(arr1.length, arr2.length); |
| | | compare (version1, version2) { |
| | | let arr1 = version1.split('.') |
| | | let arr2 = version2.split('.') |
| | | let length = Math.max(arr1.length, arr2.length) |
| | | for (let i = 0; i < length; i++) { |
| | | const n1 = Number(arr1[i] || 0); |
| | | const n2 = Number(arr2[i] || 0); |
| | | const n1 = Number(arr1[i] || 0) |
| | | const n2 = Number(arr2[i] || 0) |
| | | // version1 > version2 返回1,如果 version1 < version2 返回-1,不然返回0 |
| | | if (n1 > n2) return 1; |
| | | if (n1 < n2) return -1; |
| | | if (n1 > n2) return 1 |
| | | if (n1 < n2) return -1 |
| | | } |
| | | return 0; |
| | | return 0 |
| | | }, |
| | | // 版本回退 |
| | | rollFirmwareConfirm() { |
| | | rollFirmwareConfirm () { |
| | | // 无人机升级/回滚 |
| | | if (this.rollFirmVersion == '') { |
| | | this.$message({ |
| | | type: 'warning', |
| | | message: '请先选择回退的固件版本!', |
| | | }); |
| | | return; |
| | | }) |
| | | return |
| | | } |
| | | var that = this; |
| | | var arr = []; |
| | | that.firmwareInfo['product_version'] = this.rollFirmVersion; |
| | | var that = this |
| | | var arr = [] |
| | | that.firmwareInfo['product_version'] = this.rollFirmVersion |
| | | // 机场信息设置 |
| | | arr.push(that.firmwareInfo); |
| | | arr.push(that.firmwareInfo) |
| | | // 判断是机场升级还是飞机升级,如果是机场升级则把无人机信息一起带过去 |
| | | if (that.firmwareInfo.domain === 3) { |
| | | // 判断当前机场和无人机固件版本是否一致 |
| | | var dockFlag = this.compare( |
| | | this.firmwareInfo.firmware_version, |
| | | this.firmwareInfo.child_version |
| | | ); |
| | | ) |
| | | // 机场和无人机版本一致情况 |
| | | if (dockFlag == 0) { |
| | | if (!this.firmwareInfo.status || !this.firmwareInfo.child_status) { |
| | | this.$message({ |
| | | type: 'warning', |
| | | message: '请先更新无人机固件版本后再进行机场固件版本更新!', |
| | | }); |
| | | return; |
| | | }) |
| | | return |
| | | } |
| | | // 只有同时在线并且是升级才能操作 |
| | | // 判断升级还是降级,不能降级 |
| | | var upDownFlag = this.compare(this.rollFirmVersion, this.firmwareInfo.firmware_version); |
| | | var upDownFlag = this.compare(this.rollFirmVersion, this.firmwareInfo.firmware_version) |
| | | if (upDownFlag == -1) { |
| | | this.$message({ |
| | | type: 'warning', |
| | | message: '请先回滚无人机固件版本后再进行机场固件版本更新!', |
| | | }); |
| | | return; |
| | | }) |
| | | return |
| | | } |
| | | // 同时升级操作 |
| | | // 无人机信息设置 |
| | |
| | | sn: that.firmwareInfo.child_sn, |
| | | product_version: this.rollFirmVersion, |
| | | firmware_upgrade_type: 3, |
| | | }); |
| | | }) |
| | | } else { |
| | | // 版本不一致情况 |
| | | // 比对需要改变的版本号和无人机版本号是否一致,不一致提示不能操作 |
| | | var verFlag = this.compare(this.rollFirmVersion, this.firmwareInfo.child_version); |
| | | var verFlag = this.compare(this.rollFirmVersion, this.firmwareInfo.child_version) |
| | | if (verFlag == -1 || verFlag == 1) { |
| | | this.$message({ |
| | | type: 'warning', |
| | | message: '更新版本与当前无人机固件版本不一致,请更新和无人机一致的版本!', |
| | | }); |
| | | return; |
| | | }) |
| | | return |
| | | } |
| | | // 需要改变的版本号和无人机版本号如果是一致,进行单独的机场升级/回滚操作 |
| | | } |
| | | } |
| | | ota(that.firmwareInfo.workspaceId, arr).then(res => { |
| | | that.firmwareVersion = ''; |
| | | that.firmwareInfo = {}; |
| | | that.rollFirmwareBox = false; |
| | | that.firmwareVersion = '' |
| | | that.firmwareInfo = {} |
| | | that.rollFirmwareBox = false |
| | | this.$message({ |
| | | type: 'success', |
| | | message: '操作成功!', |
| | | }); |
| | | this.onLoad(this.page); |
| | | done(); |
| | | }); |
| | | }) |
| | | this.onLoad(this.page) |
| | | done() |
| | | }) |
| | | }, |
| | | // 升级固件按钮事件 |
| | | updateFirmware(row) { |
| | | updateFirmware (row) { |
| | | if (row.status == 0) { |
| | | this.$message({ |
| | | type: 'warning', |
| | | message: '设备不在线!', |
| | | }); |
| | | return; |
| | | }) |
| | | return |
| | | } |
| | | var that = this; |
| | | this.firmwareBox = true; |
| | | var that = this |
| | | this.firmwareBox = true |
| | | const param = { |
| | | device_name: row.device_name, |
| | | }; |
| | | } |
| | | // 获取固件最新版本信息 |
| | | getDeviceUpgradeInfo(param).then(res => { |
| | | const data = res.data.data; |
| | | const data = res.data.data |
| | | if (data.length === 0) { |
| | | return; |
| | | return |
| | | } |
| | | that.firmwareVersion = data[0].product_version; |
| | | that.firmwareInfo['device_name'] = row.device_name; |
| | | that.firmwareInfo['sn'] = row.device_sn; |
| | | that.firmwareInfo['product_version'] = data[0].product_version; |
| | | that.firmwareVersion = data[0].product_version |
| | | that.firmwareInfo['device_name'] = row.device_name |
| | | that.firmwareInfo['sn'] = row.device_sn |
| | | that.firmwareInfo['product_version'] = data[0].product_version |
| | | // 3-普通升级,2-一致性升级; |
| | | that.firmwareInfo['firmware_upgrade_type'] = |
| | | row.firmware_status === that.DeviceFirmwareStatusEnum.ToUpgraded |
| | | ? that.DeviceFirmwareTypeEnum.ToUpgraded |
| | | : that.DeviceFirmwareTypeEnum.ConsistencyUpgrade; |
| | | that.firmwareInfo['workspaceId'] = row.workspace_id; |
| | | }); |
| | | : that.DeviceFirmwareTypeEnum.ConsistencyUpgrade |
| | | that.firmwareInfo['workspaceId'] = row.workspace_id |
| | | }) |
| | | }, |
| | | // 确定升级固件版本 |
| | | updateFirmwareConfirm() { |
| | | var that = this; |
| | | var arr = []; |
| | | arr.push(that.firmwareInfo); |
| | | updateFirmwareConfirm () { |
| | | var that = this |
| | | var arr = [] |
| | | arr.push(that.firmwareInfo) |
| | | ota(that.firmwareInfo.workspaceId, arr).then(res => { |
| | | that.firmwareVersion = ''; |
| | | that.firmwareInfo = {}; |
| | | that.firmwareBox = false; |
| | | that.firmwareVersion = '' |
| | | that.firmwareInfo = {} |
| | | that.firmwareBox = false |
| | | this.$message({ |
| | | type: 'success', |
| | | message: '操作成功!', |
| | | }); |
| | | this.onLoad(this.page); |
| | | done(); |
| | | }); |
| | | }) |
| | | this.onLoad(this.page) |
| | | done() |
| | | }) |
| | | }, |
| | | handleOssSetClose() { |
| | | this.ossEnd = ''; |
| | | this.deviceSn = ''; |
| | | this.bindOssId = null; |
| | | handleOssSetClose () { |
| | | this.ossEnd = '' |
| | | this.deviceSn = '' |
| | | this.bindOssId = null |
| | | }, |
| | | // 存储对象配置确定 |
| | | ossSetConfirm() { |
| | | var that = this; |
| | | ossSetConfirm () { |
| | | var that = this |
| | | // 判断是否已选择 |
| | | if (!this.ossEnd) { |
| | | this.$message({ |
| | | type: 'warning', |
| | | message: '请选择对于的存储对象!', |
| | | }); |
| | | return; |
| | | }) |
| | | return |
| | | } |
| | | // 新增或者修改操作 |
| | | const data = { |
| | | oss_id: this.ossEnd, |
| | | sn: this.deviceSn, |
| | | }; |
| | | } |
| | | // 新增 |
| | | addOrUpdateOssBind(data).then(res => { |
| | | this.$message({ |
| | | type: 'success', |
| | | message: '操作成功!', |
| | | }); |
| | | that.ossSetBox = false; |
| | | this.onLoad(this.page); |
| | | done(); |
| | | }); |
| | | }) |
| | | that.ossSetBox = false |
| | | this.onLoad(this.page) |
| | | done() |
| | | }) |
| | | }, |
| | | // 取消配置 |
| | | cancelOssSetConfirm() { |
| | | var that = this; |
| | | cancelOssSetConfirm () { |
| | | var that = this |
| | | // 判断是否已有配置 |
| | | if (!this.bindOssId) { |
| | | this.$message({ |
| | | type: 'warning', |
| | | message: '当前机场尚未配置存储对象信息!', |
| | | }); |
| | | return; |
| | | }) |
| | | return |
| | | } |
| | | deleteByOssId(this.bindOssId).then(() => { |
| | | that.ossSetBox = false; |
| | | this.onLoad(this.page); |
| | | that.ossSetBox = false |
| | | this.onLoad(this.page) |
| | | this.$message({ |
| | | type: 'success', |
| | | message: '操作成功!', |
| | | }); |
| | | }); |
| | | }) |
| | | }) |
| | | }, |
| | | // 取消按钮 oss |
| | | cancelOssSet() { |
| | | this.ossEnd = ''; |
| | | this.deviceSn = ''; |
| | | this.bindOssId = null; |
| | | this.ossSetBox = false; |
| | | cancelOssSet () { |
| | | this.ossEnd = '' |
| | | this.deviceSn = '' |
| | | this.bindOssId = null |
| | | this.ossSetBox = false |
| | | }, |
| | | rowSave(row, done, loading) { |
| | | let areaCode = row.area_code; |
| | | rowSave (row, done, loading) { |
| | | let areaCode = row.area_code |
| | | if (Array.isArray(areaCode)) { |
| | | areaCode = areaCode[areaCode.length - 1]; |
| | | areaCode = areaCode[areaCode.length - 1] |
| | | } else if (typeof areaCode === 'string' && areaCode.includes(',')) { |
| | | const codes = areaCode.split(','); |
| | | areaCode = codes[codes.length - 1]; |
| | | const codes = areaCode.split(',') |
| | | areaCode = codes[codes.length - 1] |
| | | } |
| | | row.area_code = areaCode || null; |
| | | row.area_code = areaCode || null |
| | | |
| | | add(row).then( |
| | | () => { |
| | | this.onLoad(this.page); |
| | | this.onLoad(this.page) |
| | | this.$message({ |
| | | type: 'success', |
| | | message: '操作成功!', |
| | | }); |
| | | done(); |
| | | }) |
| | | done() |
| | | }, |
| | | error => { |
| | | window.console.log(error); |
| | | loading(); |
| | | window.console.log(error) |
| | | loading() |
| | | } |
| | | ); |
| | | ) |
| | | }, |
| | | rowUpdate(row, index, done, loading) { |
| | | rowUpdate (row, index, done, loading) { |
| | | const submitData = { |
| | | ...row, |
| | | area_code: row.area_code.split(',').pop(), |
| | | }; |
| | | insure_start_time: row.duration_of_insurance[0], |
| | | insure_expired_time: row.duration_of_insurance[1], |
| | | } |
| | | |
| | | delete submitData.duration_of_insurance |
| | | |
| | | update(submitData).then( |
| | | () => { |
| | | this.onLoad(this.page); |
| | | this.onLoad(this.page) |
| | | this.$message({ |
| | | type: 'success', |
| | | message: '操作成功!', |
| | | }); |
| | | done(); |
| | | }) |
| | | done() |
| | | }, |
| | | error => { |
| | | window.console.log(error); |
| | | loading(); |
| | | window.console.log(error) |
| | | loading() |
| | | } |
| | | ); |
| | | ) |
| | | }, |
| | | rowDel(row) { |
| | | rowDel (row) { |
| | | this.$confirm('确定将选择数据删除?', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning', |
| | | }) |
| | | .then(() => { |
| | | return remove(row.id); |
| | | return remove(row.id) |
| | | }) |
| | | .then(() => { |
| | | this.onLoad(this.page); |
| | | this.onLoad(this.page) |
| | | this.$message({ |
| | | type: 'success', |
| | | message: '操作成功!', |
| | | }); |
| | | }); |
| | | }) |
| | | }) |
| | | }, |
| | | searchReset() { |
| | | this.query = {}; |
| | | this.onLoad(this.page); |
| | | searchReset () { |
| | | this.query = {} |
| | | this.onLoad(this.page) |
| | | }, |
| | | searchChange(params, done) { |
| | | this.query = params; |
| | | this.page.currentPage = 1; |
| | | this.onLoad(this.page, params); |
| | | done(); |
| | | searchChange (params, done) { |
| | | this.query = params |
| | | this.page.currentPage = 1 |
| | | this.onLoad(this.page, params) |
| | | done() |
| | | }, |
| | | selectionChange(list) { |
| | | this.selectionList = list; |
| | | selectionChange (list) { |
| | | this.selectionList = list |
| | | }, |
| | | selectionClear() { |
| | | this.selectionList = []; |
| | | this.$refs.crud.toggleSelection(); |
| | | selectionClear () { |
| | | this.selectionList = [] |
| | | this.$refs.crud.toggleSelection() |
| | | }, |
| | | handleDelete() { |
| | | handleDelete () { |
| | | if (this.selectionList.length === 0) { |
| | | this.$message.warning('请选择至少一条数据'); |
| | | return; |
| | | this.$message.warning('请选择至少一条数据') |
| | | return |
| | | } |
| | | this.$confirm('确定将选择数据删除?', { |
| | | confirmButtonText: '确定', |
| | |
| | | type: 'warning', |
| | | }) |
| | | .then(() => { |
| | | return remove(this.ids); |
| | | return remove(this.ids) |
| | | }) |
| | | .then(() => { |
| | | this.onLoad(this.page); |
| | | this.onLoad(this.page) |
| | | this.$message({ |
| | | type: 'success', |
| | | message: '操作成功!', |
| | | }); |
| | | this.$refs.crud.toggleSelection(); |
| | | }); |
| | | }) |
| | | this.$refs.crud.toggleSelection() |
| | | }) |
| | | }, |
| | | getFullAreaCode(areaCode) { |
| | | if (!areaCode) return ''; |
| | | getFullAreaCode (areaCode) { |
| | | if (!areaCode) return '' |
| | | |
| | | const code = areaCode.toString(); |
| | | const code = areaCode.toString() |
| | | |
| | | if (code.includes(',')) return code; |
| | | if (code.includes(',')) return code |
| | | |
| | | if (code.endsWith('0000000000')) { |
| | | return code; |
| | | return code |
| | | } else if (code.endsWith('00000000') && !code.endsWith('0000000000')) { |
| | | const provinceCode = code.substring(0, 2) + '0000000000'; |
| | | return `${provinceCode},${code}`; |
| | | const provinceCode = code.substring(0, 2) + '0000000000' |
| | | return `${provinceCode},${code}` |
| | | } else { |
| | | const provinceCode = code.substring(0, 2) + '0000000000'; |
| | | const cityCode = code.substring(0, 4) + '00000000'; |
| | | return `${provinceCode},${cityCode},${code}`; |
| | | const provinceCode = code.substring(0, 2) + '0000000000' |
| | | const cityCode = code.substring(0, 4) + '00000000' |
| | | return `${provinceCode},${cityCode},${code}` |
| | | } |
| | | }, |
| | | beforeOpen(done, type) { |
| | | beforeOpen (done, type) { |
| | | if (['edit', 'view'].includes(type)) { |
| | | getDetail(this.form.id).then(res => { |
| | | const data = res.data.data; |
| | | const data = res.data.data |
| | | |
| | | this.form = { |
| | | ...data, |
| | | area_code: this.getFullAreaCode(data.area_code), |
| | | }; |
| | | }); |
| | | duration_of_insurance: [data?.insure_start_time || '', data?.insure_expired_time || ''] |
| | | } |
| | | |
| | | done() |
| | | }) |
| | | } |
| | | done(); |
| | | }, |
| | | currentChange(currentPage) { |
| | | this.page.currentPage = currentPage; |
| | | currentChange (currentPage) { |
| | | this.page.currentPage = currentPage |
| | | }, |
| | | sizeChange(pageSize) { |
| | | this.page.pageSize = pageSize; |
| | | sizeChange (pageSize) { |
| | | this.page.pageSize = pageSize |
| | | }, |
| | | refreshChange() { |
| | | this.onLoad(this.page, this.query); |
| | | refreshChange () { |
| | | this.onLoad(this.page, this.query) |
| | | }, |
| | | onLoad(page, params = {}) { |
| | | onLoad (page, params = {}) { |
| | | // this.closeAllWebsoket(); |
| | | const { releaseTimeRange } = this.query; |
| | | const { releaseTimeRange } = this.query |
| | | let values = { |
| | | ...params, |
| | | ...this.query, |
| | | }; |
| | | } |
| | | if (releaseTimeRange) { |
| | | values = { |
| | | ...values, |
| | | releaseTime_datege: releaseTimeRange[0], |
| | | releaseTime_datelt: releaseTimeRange[1], |
| | | }; |
| | | values.releaseTimeRange = null; |
| | | } |
| | | values.releaseTimeRange = null |
| | | } |
| | | values['type'] = 1; |
| | | this.loading = true; |
| | | values['type'] = 1 |
| | | this.loading = true |
| | | getList(page.currentPage, page.pageSize, values).then(res => { |
| | | const data = res.data.data; |
| | | this.page.total = data.total; |
| | | const data = res.data.data |
| | | this.page.total = data.total |
| | | data.records.forEach(e => { |
| | | e['hasChildren'] = e.has_children; |
| | | e['hasChildren'] = e.has_children |
| | | if (e.firmware_status == 4) { |
| | | this.connectWebSocket(e); |
| | | this.connectWebSocket(e) |
| | | } |
| | | }); |
| | | this.data = data.records; |
| | | this.loading = false; |
| | | this.selectionClear(); |
| | | }); |
| | | }) |
| | | this.data = data.records |
| | | this.loading = false |
| | | this.selectionClear() |
| | | }) |
| | | }, |
| | | treeLoad(tree, treeNode, resolve) { |
| | | treeLoad (tree, treeNode, resolve) { |
| | | var params = { |
| | | childSn: tree.child_sn, |
| | | }; |
| | | } |
| | | getList(1, 10, params).then(res => { |
| | | const data = res.data.data.records; |
| | | resolve(data); |
| | | }); |
| | | const data = res.data.data.records |
| | | resolve(data) |
| | | }) |
| | | }, |
| | | }, |
| | | }; |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | |
| | | color: #40cb8b; |
| | | font-size: 13px; |
| | | } |
| | | |
| | | .status1 { |
| | | float: right; |
| | | color: #8492a6; |
| | | font-size: 13px; |
| | | } |
| | | |
| | | .online { |
| | | margin-left: 5px; |
| | | } |
| | | |
| | | .onlineStatus1 { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | } |
| | | |
| | | .onlineStatus { |
| | | display: flex; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | } |
| | | |
| | | .onlineStatus-dot { |
| | | width: 15px; |
| | | height: 15px; |
| | | background-color: #40cb8b; |
| | | border-radius: 50%; |
| | | } |
| | | |
| | | .onlineStatus-dot1 { |
| | | width: 15px; |
| | | height: 15px; |
| | | background-color: #de5e5e; |
| | | border-radius: 50%; |
| | | } |
| | | |
| | | .firmware_status { |
| | | background-color: aqua; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .firmware_status:hover { |
| | | cursor: pointer; |
| | | /* color: aqua; */ |
| | |
| | | |
| | | .active-element { |
| | | cursor: pointer; |
| | | |
| | | &:hover { |
| | | background-color: #409eff; |
| | | border-color: #409eff; |
| | |
| | | <div class="task-intermediate-content"> |
| | | <SearchBox @search="searchClick" @addTask="handleAddTask"></SearchBox> |
| | | <div class="task-table"> |
| | | <el-table |
| | | stripe |
| | | :data="jobListData" |
| | | :row-class-name="tableRowClassName" |
| | | :row-style="{ height: '54px', fontSize: '14px', 'text-align': 'center' }" |
| | | :header-cell-style="{ 'text-align': 'center', height: '36px', fontSize: '14px' }" |
| | | > |
| | | <el-table border :data="jobListData" class="custom-header"> |
| | | <el-table-column label="序号" type="index" width="60"> |
| | | <template #default="{ $index }"> |
| | | {{ ($index + 1 + (jobListParams.current - 1) * jobListParams.size).toString().padStart(2, '0') }} |
| | |
| | | <span>{{ scope.row.event_number ? scope.row.event_number : '/' }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="cycle_time_value" label="任务时间" /> |
| | | <el-table-column prop="creator_name" label="创建人" align="center" /> |
| | | <el-table-column prop="cycle_time_value" label="任务时间" show-overflow-tooltip /> |
| | | <el-table-column prop="creator_name" label="创建人" align="center" show-overflow-tooltip /> |
| | | <el-table-column label="操作" width="200" align="center"> |
| | | <template #default="scope"> |
| | | <div |
| | |
| | | color: #3efe96; |
| | | } |
| | | } |
| | | :deep(.el-table) { |
| | | --el-table-tr-bg-color: #ffffff; |
| | | --el-table-striped-bg-color: #EFEFEF; |
| | | // :deep(.el-table) { |
| | | // --el-table-tr-bg-color: #ffffff; |
| | | // --el-table-striped-bg-color: #EFEFEF; |
| | | |
| | | .el-table__body tr.el-table__row--striped td { |
| | | background-color: var(--el-table-striped-bg-color); |
| | | } |
| | | } |
| | | // .el-table__body tr.el-table__row--striped td { |
| | | // background-color: var(--el-table-striped-bg-color); |
| | | // } |
| | | // } |
| | | // 分页 |
| | | :deep(.custom-header th.el-table__cell) { |
| | | color: rgba(0, 0, 0, 0.85); |
| | | } |
| | | :deep(.el-table td.el-table__cell) { |
| | | color: #606266; |
| | | } |
| | | :deep(.el-pagination) { |
| | | display: flex; |
| | | justify-content: center; |
| | | justify-content: flex-end; |
| | | } |
| | | :deep(.el-pagination button) { |
| | | background: center center no-repeat none !important; |
| | |
| | | { |
| | | name: '机巢事件统计', |
| | | type: 'pie', |
| | | roseType: 'radius', |
| | | radius: ['20%', '70%'], |
| | | center: ['50%', '45%'], |
| | | // roseType: 'radius', |
| | | radius: ['40%', '70%'], |
| | | // center: ['50%', '45%'], |
| | | // radius: '70%', // 设置饼图的半径 |
| | | center: ['50%', '50%'], // 调整饼图位置 |
| | | data: [], |
| | | // [ |
| | | // { name: '国土类', value: 0, itemStyle: { color: '#3D7FFF' } }, |
| | | // { name: '城管类', value: 0, itemStyle: { color: '#8277E9' } }, |
| | | // { name: '消防类', value: 0, itemStyle: { color: '#FFB77E' } }, |
| | | // { name: '林业类', value: 0, itemStyle: { color: '#44D7B6' } }, |
| | | // { name: '公安类', value: 0, itemStyle: { color: '#62F4FF' } } |
| | | // ], |
| | | label: { |
| | | show: true, |
| | | position: 'outside', |
| | |
| | | <!-- 密钥上传 --> |
| | | <div class="upload-file" v-else-if="checked === 2"> |
| | | <img v-if="isSuccess === 'success'" :src="uploadSuccess" alt="" /> |
| | | <div v-if="isSuccess === 'success'">上传成功</div> |
| | | <img v-if="isSuccess === 'error'" :src="uploadError" alt="" /> |
| | | <div v-if="isSuccess === 'error'">上传失败</div> |
| | | <el-upload |
| | | action="/upload" |
| | | accept=".lic" |
| | |
| | | .upload-btn { |
| | | width: 93px; |
| | | height: 38px; |
| | | line-height: 38px; |
| | | background: #1b3e6c; |
| | | background: #DDEBFF; |
| | | border-radius: 8px 8px 8px 8px; |
| | | border: 1px solid #ffffff; |
| | | color: #fff; |
| | | border: 1px solid #409EFF; |
| | | text-align: center; |
| | | font-family: Source Han Sans CN, Source Han Sans CN; |
| | | font-weight: bold; |
| | | font-size: 14px; |
| | | color: #228CFA; |
| | | line-height: 38px; |
| | | text-align: center; |
| | | margin-top: 10px; |
| | | } |
| | | .password-time { |
| | | margin-top: 50px; |
| | |
| | | this.initMapLine() |
| | | }, |
| | | |
| | | initMapLine (cb = () => { }) { |
| | | initMapLine (infos = {}, cb = () => { }) { |
| | | let currentLine = this.wayLineList.find(item => item.wayline_id == this.form.file_id) |
| | | |
| | | if (!currentLine) return |
| | |
| | | this.$refs.MapContainer.initAddEntity('polyline', { |
| | | url: `${import.meta.env.VITE_APP_AIRLINE_URL + currentLine.object_key}?_t=${new Date().getTime()}`, |
| | | type: currentLine.wayline_type, |
| | | cb |
| | | cb, |
| | | infos |
| | | }) |
| | | } |
| | | }) |
| | |
| | | |
| | | that.device_sns = [] |
| | | |
| | | this.initMapLine(async (polygon) => { |
| | | this.initMapLine({}, async (polygon) => { |
| | | const currentLine = that.wayLineList.find(item => item.wayline_id === waylineId) |
| | | |
| | | //按照航线来 |
| New file |
| | |
| | | import eventPending1 from '@/assets/images/home/useEventOperate/eventPending1.png' // 待处理 0 |
| | | import eventPending from '@/assets/images/home/useEventOperate/eventPending.png' // 待处理 0 |
| | | import eventWaitAudit1 from '@/assets/images/home/useEventOperate/eventWaitAudit1.png' // 待审核 2 |
| | | import eventWaitAudit from '@/assets/images/home/useEventOperate/eventWaitAudit.png' // 待审核 2 |
| | | import eventProcessing1 from '@/assets/images/home/useEventOperate/eventProcessing1.png' // 处理中 3 |
| | | import eventProcessing from '@/assets/images/home/useEventOperate/eventProcessing.png' // 处理中 3 |
| | | import eventCompleted1 from '@/assets/images/home/useEventOperate/eventCompleted1.png' // 已完成 4 |
| | | import eventCompleted from '@/assets/images/home/useEventOperate/eventCompleted.png' // 已完成 4 |
| | | import eventClosed1 from '@/assets/images/home/useEventOperate/eventClosed1.png' // 已完结 5 |
| | | import eventClosed from '@/assets/images/home/useEventOperate/eventClosed.png' // 已完结 5 |
| | | |
| | | |
| | | const eventImage = { |
| | | 0: eventPending, |
| | | 2: eventWaitAudit, |
| | | 3: eventProcessing, |
| | | 4: eventCompleted, |
| | | 5: eventClosed |
| | | } |
| | | const eventActiveImage = { |
| | | 0: eventPending1, |
| | | 2: eventWaitAudit1, |
| | | 3: eventProcessing1, |
| | | 4: eventCompleted1, |
| | | 5: eventClosed1 |
| | | } |
| | | |
| | | /** |
| | | * 根据事件状态获取图片资源 |
| | | * @param {*} status 状态 |
| | | * @returns |
| | | */ |
| | | export const getEventImage = (status) => eventImage[status] || eventCompleted |
| | | export const getEventActiveImage = (status) => eventActiveImage[status] || eventCompleted |
| | |
| | | > |
| | | <div> |
| | | {{ item.name }} |
| | | <div class="dots" :class="item.number >= 100 ? 'dots1':''">{{ item.number >= 100 ? '99+' : item.number }}</div> |
| | | <div class="dots" >{{ item.number >= 100 ? '99+' : item.number }}</div> |
| | | <!-- <div class="dots">99+</div> --> |
| | | </div> |
| | | </div> |
| | |
| | | }; |
| | | const monthRange = getCurrentMonthRange(); |
| | | params.value = monthRange; |
| | | console.log('params.value', params.value); |
| | | |
| | | const getJobEventBar = () => { |
| | | getCalen(params.value).then(res => { |
| | |
| | | </script> |
| | | <style lang="scss"> |
| | | .calenBox { |
| | | height: 630px; |
| | | .el-calendar { |
| | | height: 100%; // 日历填充容器 |
| | | // 标题样式 |
| | | &__title { |
| | | font-weight: bold; |
| | | font-size: 14px; |
| | | color: #363636; |
| | | } |
| | | |
| | | .el-button-group>.el-button:not(:first-child):not(:last-child) { |
| | | border-radius: 0; |
| | | // 日历主体 |
| | | &__body { |
| | | height: 98%; // 关键:继承父高度 |
| | | |
| | | .el-calendar-table { |
| | | height: 90% !important; // 百分比生效 |
| | | } |
| | | } |
| | | |
| | | // 选中日期样式 |
| | | .el-calendar-table td.is-selected { |
| | | background-color: #f0f7ff; |
| | | border: 2px solid #409eff; |
| | | border-radius: 4px; |
| | | |
| | | .date-number { |
| | | font-weight: bold; |
| | | color: #409eff; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 隐藏按钮组中间按钮 |
| | | .el-button-group > .el-button:not(:first-child):not(:last-child) { |
| | | display: none; |
| | | } |
| | | .el-calendar__title { |
| | | font-weight: bold; |
| | | font-size: 14px; |
| | | color: #363636; |
| | | } |
| | | |
| | | .el-calendar-table td.is-selected { |
| | | background-color: #f0f7ff; |
| | | border: 2px solid #409eff; |
| | | border-radius: 4px; |
| | | } |
| | | .el-calendar-table td.is-selected .date-number { |
| | | font-weight: bold; |
| | | color: #409eff; |
| | | } |
| | | |
| | | .el-calendar-table td.is-selected .events { |
| | | // padding: 2px; |
| | | } |
| | | } |
| | | </style> |
| | | <style lang="scss" scoped> |
| | | .calenBox { |
| | | margin-top: 17px; |
| | | height: 567px; |
| | | height: 630px; |
| | | overflow: hidden; |
| | | .event-item { |
| | | font-size: 12px; |
| | |
| | | padding: 0px 20px 0 10px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | height: calc(100% - 5rem); |
| | | } |
| | | .workleft { |
| | | width: 68%; |
| | |
| | | background: #ffffff; |
| | | border-radius: 8px 8px 8px 8px; |
| | | .comprehensive { |
| | | padding: 14px 14px 0 21px; |
| | | padding: 17px 14px 0 21px; |
| | | .title { |
| | | display: flex; |
| | | justify-content: space-between; |
| | |
| | | width: 60%; |
| | | border-right: 1px solid #dfdfdf; |
| | | .workOrder { |
| | | margin-top: 21px; |
| | | margin-top: 31px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | border-bottom: 1px solid #dfdfdf; |
| | |
| | | width: 40%; |
| | | .card-title { |
| | | display: flex; |
| | | margin-bottom: 10px; |
| | | margin-bottom: 17px; |
| | | align-items: center; |
| | | img { |
| | | width: 36px; |
| | |
| | | grid-template-columns: repeat(2, 1fr); |
| | | row-gap: 14px; |
| | | gap: 10px; |
| | | margin-bottom: 27px; |
| | | .status-item { |
| | | display: flex; |
| | | text-align: center; |
| | |
| | | max-width: 158px; |
| | | background: #f6f8fe; |
| | | border-radius: 8px 8px 8px 8px; |
| | | margin-bottom: 9px; |
| | | img { |
| | | width: 26px; |
| | | height: 26px; |
| | |
| | | font-weight: bold; |
| | | font-size: 30px; |
| | | color: #363636; |
| | | margin: 5px 0; |
| | | // margin: 5px 0; |
| | | // font-style: italic; |
| | | display: inline-block; |
| | | transform: skewX(-5deg); |
| | |
| | | |
| | | // 飞行统计 |
| | | .flyOrder { |
| | | margin-top: 5px; |
| | | margin-top: 10px; |
| | | .fytitle { |
| | | display: flex; |
| | | align-items: center; |
| | |
| | | } |
| | | } |
| | | .flycenter { |
| | | margin-top: 13px; |
| | | |
| | | margin-top: 22px; |
| | | .centerBox { |
| | | display: flex; |
| | | justify-content: space-between; |
| | |
| | | import { defineConfig, loadEnv } from 'vite'; |
| | | import { resolve } from 'path'; |
| | | import createVitePlugins from './vite/plugins'; |
| | | import { defineConfig, loadEnv } from 'vite' |
| | | import { resolve } from 'path' |
| | | import createVitePlugins from './vite/plugins' |
| | | import postCssPxToRem from 'postcss-pxtorem' |
| | | // https://vitejs.dev/config/ |
| | | export default ({ mode, command }) => { |
| | | const env = loadEnv(mode, process.cwd()); |
| | | const { VITE_APP_ENV, VITE_APP_BASE, VITE_APP_URL } = env; |
| | | const env = loadEnv(mode, process.cwd()) |
| | | const { VITE_APP_ENV, VITE_APP_BASE, VITE_APP_URL } = env |
| | | // 判断是打生产环境包 |
| | | const isProd = VITE_APP_ENV === 'production'; |
| | | const isProd = VITE_APP_ENV === 'production' |
| | | |
| | | // 根据是否生产环境,动态设置压缩配置 |
| | | const buildConfig = { |
| | | outDir: 'manage', |
| | | target: 'esnext', |
| | | minify: isProd ? 'terser' : 'esbuild', // 根据环境选择压缩工具 |
| | | }; |
| | | } |
| | | |
| | | // 如果是生产环境,添加Terser的配置 |
| | | if (isProd) { |
| | |
| | | format: { |
| | | comments: false, // 删除所有注释 |
| | | }, |
| | | }; |
| | | } |
| | | } |
| | | return defineConfig({ |
| | | base: VITE_APP_BASE, |
| | |
| | | __INTLIFY_PROD_DEVTOOLS__: false, |
| | | }, |
| | | server: { |
| | | // port: 2888, |
| | | port: 5174, |
| | | // host: '192.168.1.178', |
| | | proxy: { |
| | | '/api': { |
| | | target: VITE_APP_URL, |
| | | changeOrigin: true, |
| | | rewrite: path => path.replace(/^\/api/, ''), |
| | | }, |
| | | } |
| | | }, |
| | | }, |
| | | resolve: { |
| | |
| | | target: 'esnext', |
| | | }, |
| | | }, |
| | | }); |
| | | }; |
| | | }) |
| | | } |