.env.production
@@ -1,3 +1,13 @@ ### # @Author : yuan # @Date : 2025-06-26 15:18:36 # @LastEditors : yuan # @LastEditTime : 2025-07-01 16:34:42 # @FilePath : \.env.production # @Description : # Copyright 2025 OBKoro1, All Rights Reserved. # 2025-06-26 15:18:36 ### NODE_ENV = 'production' #生产环境配置 @@ -34,3 +44,9 @@ # 是否在打包时开启压缩,支持 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 src/assets/images/task/qxzx.pngsrc/assets/images/task/zxz.pngsrc/axios.js
@@ -34,7 +34,7 @@ baseUrl } from '@/config/env'; import crypto from '@/utils/crypto'; const adminUrl = import.meta.env.VITE_APP_DASHBOARD_URL // 全局未授权错误提示状态,只提示一次 let isErrorShown = false; @@ -170,9 +170,10 @@ removeToken(); removeRefreshToken(); // 重定向到登录页 store.dispatch('FedLogOut').then(() => router.push({ path: '/login' })); // store.dispatch('FedLogOut').then(() => router.push({ // path: '/login' // })); store.dispatch('FedLogOut').then(() => window.location.replace(`${adminUrl}#/login`)); return Promise.reject(new Error(message)); }); } @@ -190,9 +191,10 @@ removeToken(); removeRefreshToken(); // 重定向到登录页 store.dispatch('FedLogOut').then(() => router.push({ path: '/login' })); // store.dispatch('FedLogOut').then(() => router.push({ // path: '/login' // })); store.dispatch('FedLogOut').then(() => window.location.replace(`${adminUrl}#/login`)); return Promise.reject(new Error(message)); } // 如果请求为oauth2错误码则首次报错时提示 src/option/job/jobinfo.js
@@ -145,6 +145,7 @@ }, search: true, display: false, showOverflowTooltip: true, }, { label: '任务ID', @@ -153,6 +154,7 @@ search: true, width: 80, display: false, showOverflowTooltip: true, }, { label: '任务名称', @@ -161,6 +163,7 @@ search: true, width: 200, display: false, showOverflowTooltip: true, }, { label: '定时信息', @@ -177,6 +180,7 @@ ], width: 120, display: false, showOverflowTooltip: true, }, { label: '执行类型', @@ -193,6 +197,7 @@ ], width: 110, display: false, showOverflowTooltip: true, }, { label: '处理器类型', @@ -209,6 +214,7 @@ ], width: 180, display: false, showOverflowTooltip: true, }, { label: '任务状态', @@ -219,6 +225,7 @@ slot: true, width: 100, display: false, showOverflowTooltip: true, }, ], group: [ src/page/index/top/index.vue
@@ -102,7 +102,9 @@ type: 'warning', }).then(() => { this.$store.dispatch('LogOut').then(() => { this.$router.push({ path: '/login' }) // this.$router.push({ path: '/login' }) const adminUrl = import.meta.env.VITE_APP_DASHBOARD_URL window.location.replace(`${adminUrl}#/login`) }) }) }, src/page/index/top/top-qna.vue
@@ -1,7 +1,10 @@ <template> <div class="top-qna"> <el-dropdown @command="dropdownClick"> <img alt="" src="@/assets/images/helpCenter/helpIcon.png" /> <!-- <img class="help-icon" alt="" src="@/assets/images/helpCenter/helpIcon.png" /> --> <el-icon class="help-icon" color="#707070" size="17"> <QuestionFilled /> </el-icon> <template #dropdown> <el-dropdown-menu> <el-dropdown-item command="guide">入门指南</el-dropdown-item> @@ -19,7 +22,7 @@ const router = useRouter(); const systemupdateFilePath = ref(null); const rumenPath = ref(null); const fileData = ref([]); // 存储所有文件数据 const fileData = ref([]); const loading = ref(false); const getlistDataAPI = async () => { loading.value = true; @@ -27,21 +30,19 @@ const res = await listDataAPI(); if (res.data.code === 200 && res.data.data) { fileData.value = res.data.data; // 使用 find 方法查找特定条目 const systemUpdateItem = res.data.data.find(item => item.id === 4); const rumenItem = res.data.data.find(item => item.id === 2); const systemUpdateItem = res.data.data.find(item => item.id === 4); //系统更新 const rumenItem = res.data.data.find(item => item.id === 2); //入门指南 if (systemUpdateItem) { systemupdateFilePath.value = systemUpdateItem.filePath; } else { console.warn('未找到系统更新条目'); console.warn('暂无系统更新'); } if (rumenItem) { rumenPath.value = rumenItem.filePath; } else { console.warn('未找到入门指南条目'); console.warn('暂无入门指南'); } } else { ElMessage.error('获取帮助数据失败'); @@ -54,23 +55,24 @@ } }; const dropdownClick = val => { // 确保数据已加载 if (loading.value) { ElMessage.warning('数据正在加载,请稍后'); return; } if (val === 'guide') { if (!rumenPath.value) { ElMessage.warning('入门指南文件未找到'); ElMessage.warning('暂无入门指南文件'); return; } window.open(rumenPath.value, '_blank'); window.open(rumenPath.value, '_blank'); } else if (val === 'systemupdate') { window.open(systemupdateFilePath.value, '_blank'); if (!systemupdateFilePath.value) { ElMessage.warning('暂无系统更新文件'); return; } window.open(systemupdateFilePath.value, '_blank'); } }; @@ -86,5 +88,12 @@ font-size: 18px; justify-content: center; cursor: pointer; .help-icon { width: 25px; height: 25px; } .theme-white .el-dropdown { color: none !important; } } </style> src/permission.js
@@ -18,7 +18,7 @@ NProgress.configure({ showSpinner: false }) const lockPage = '/lock' //锁屏页 const urlParams = getUrlParams(window.location.href) const adminUrl = import.meta.env.VITE_APP_DASHBOARD_URL function findRouteByPath (routes, targetPath) { // 遍历数组中的每个路由对象 for (const route of routes) { @@ -93,9 +93,10 @@ } else { const systemToken = store.getters.token || urlParams?.token if (systemToken === 0) { store.dispatch('FedLogOut').then(() => { next({ path: '/login' }) }) // store.dispatch('FedLogOut').then(() => { // next({ path: '/login' }) // }) store.dispatch('FedLogOut').then(() => window.location.replace(`${adminUrl}#/login`)); } else { const meta = to.meta || {} const query = to.query || {} @@ -120,7 +121,8 @@ if (meta.isAuth === false) { next() } else { next('/login') // next('/login') window.location.replace(`${adminUrl}#/login`) } } }) src/styles/element-ui.scss
@@ -31,12 +31,20 @@ padding: 0 !important; } .el-dropdown-menu__item--divided:before, .el-menu, .el-menu--horizontal > .el-menu-item:not(.is-disabled):focus, .el-menu--horizontal > .el-menu-item:not(.is-disabled):hover, .el-menu--horizontal > .el-sub-menu .el-sub-menu__title:hover { .el-dropdown-menu__item--divided:before, .el-menu, .el-menu--horizontal>.el-menu-item:not(.is-disabled):focus, .el-menu--horizontal>.el-menu-item:not(.is-disabled):hover, .el-menu--horizontal>.el-sub-menu .el-sub-menu__title:hover { background-color: transparent; } .el-dropdown-menu__item--divided:before, .el-menu, .el-menu--horizontal > .el-menu-item:not(.is-disabled):focus, .el-menu--horizontal > .el-menu-item:not(.is-disabled):hover, .el-menu--horizontal > .el-sub-menu .el-sub-menu__title:hover { .el-dropdown-menu__item--divided:before, .el-menu, .el-menu--horizontal>.el-menu-item:not(.is-disabled):focus, .el-menu--horizontal>.el-menu-item:not(.is-disabled):hover, .el-menu--horizontal>.el-sub-menu .el-sub-menu__title:hover { background-color: transparent !important; } @@ -49,11 +57,13 @@ background-color: transparent; } .el-button.is-text:not(.is-disabled):focus, .el-button.is-text:not(.is-disabled):hover { .el-button.is-text:not(.is-disabled):focus, .el-button.is-text:not(.is-disabled):hover { background-color: transparent; } .avue-icon i, .avue-icon svg { .avue-icon i, .avue-icon svg { line-height: 20px; } @@ -66,36 +76,43 @@ justify-content: space-between; .el-dialog { position: relative; padding: 0; 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__body { width: 100%; height: 100%; .el-dialog__headerbtn { position: absolute; left: 0; top: 14px; iframe { width: 100%; height: 100%; } .el-dialog__close { color: #fff; font-size: 35px; z-index: 99; } } } } } .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; } } } .ztzf-drawer-body-basic-container { .el-drawer__body { display: flex; flex-direction: column; } } src/views/algorithmRepository/algorithmRepository.vue
@@ -42,7 +42,10 @@ v-model="taskData" placeholder="请选择日期" @change="changeselect" clearable /> <!-- @clear="handleDateClear" --> </div> <div class="item"> <div class="itemchild">机巢查询:</div> @@ -70,7 +73,7 @@ ref="imageRef" :src="getSmallImg(item.url)" show-progress :preview-src-list="detailData.map(i => i.url)" :preview-src-list="[getShowImg(item.url)]" fit="cover" /> @@ -107,7 +110,7 @@ import { getDictionaryByCode } from '@/api/system/dictbiz'; import { getalgorithmList, selectDeviceList } from '@/api/algorithm'; import { getSmallImg } from '@/utils/util'; import { getSmallImg,getShowImg } from '@/utils/util'; import { useRouter } from 'vue-router'; const router = useRouter(); const baseUrl = import.meta.env.VITE_APP_PICTURE_URL; @@ -181,19 +184,31 @@ params.value.start_date = taskData.value.length ? `${taskData.value[0]} 00:00:00` : null; params.value.end_date = taskData.value.length ? `${taskData.value[1]} 23:59:59` : null; }; const handleSearch = () => { // 处理日期清空 const handleDateClear = () => { taskData.value = ''; // 清空绑定的日期数据 params.value.start_date = null; // 重置开始日期参数 params.value.end_date = null; // 重置结束日期参数 getList(); }; const handleSearch = () => { getList(); }; const handleReset = () => { taskData.value ='' params.value.start_date = null; params.value.end_date = null; params.value.device_name = ''; params.value.name = ''; getList(); }; const goback = () => { showDetail.value = false; activeItem.value = null; params.value.current =1 params.value.size=15 }; // 分页大小改变 const handleSizeChange = val => { @@ -244,6 +259,7 @@ &:hover { background: url('/src/assets/images/ht-sfbg-hover.png') no-repeat center; background-size: 100% 100%; cursor: pointer; } &.active-bg { background: url('/src/assets/images/ht-sfbg-click.png') no-repeat center; src/views/dataCenter/components/dataCenterMap.vue
@@ -111,13 +111,11 @@ 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); }; @@ -128,7 +126,6 @@ handler = new Cesium.ScreenSpaceEventHandler(viewer?.scene.canvas); handler.setInputAction(singleMachineEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK); }; // 清除所有数据点实体 const clearDataPoints = () => { if (!viewer) return; @@ -138,7 +135,6 @@ dataPointEntities.value = []; activeEntity.value = null; }; const removeHandler = () => { handler?.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK); handler?.destroy(); @@ -147,11 +143,7 @@ const renderDataPoint = mapList => { if (!viewer || !mapList?.length) return; // 清除旧实体 clearDataPoints(); // 添加新实体 mapList.forEach((item, index) => { const entity = viewer?.entities.add({ id: `dataCenter-point-${index}-${Date.now()}`, @@ -210,12 +202,9 @@ viewerRef.value = viewer; isViewerReady.value = true; // 初始化事件处理器 handlerInit(); isMapInitialized.value = true; console.log('地图初始化完成'); // 初始化后立即渲染已有数据 if (dataPointList.value.length > 0) { renderDataPoint(dataPointList.value); } @@ -243,9 +232,7 @@ try { const res = await getMapInfoAPI(ids); dataPointList.value = res.data.data || []; console.log('dataPointList.value', dataPointList.value); // 确保地图已初始化后再渲染 if (isMapInitialized.value && viewer) { renderDataPoint(dataPointList.value); } @@ -255,13 +242,7 @@ }; const isViewerReady = ref(false); /** * 初始化标注添加 * @param data 数据 */ const initEntityOrPopup = data => { console.log('data', data); //地图点在范围内 watch( () => isMapInitialized.value, src/views/dataCenter/dataCenter.vue
@@ -1,12 +1,23 @@ <template> <div class="manage-m-all-10 manage-m-t-0 manage-p-all-20 manage-h-0 manage-flex-1 manage-flex manage-f-d-c manage-b-r-5 manage-b-c-w"> class="manage-m-all-10 manage-m-t-0 manage-p-all-20 manage-h-0 manage-flex-1 manage-flex manage-f-d-c manage-b-r-5 manage-b-c-w" > <div class="dataCenter-table"> <searchData @search="searchClick" @downFun="downloadFile" @allDownFun="aLLDownloadFile"></searchData> <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 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 }"> @@ -25,25 +36,45 @@ <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"> <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 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-if="scope.row?.resultType === 4" :src="getzsSmallImg(scope.row?.link)" :preview-src-list="[getzsSmallImg(scope.row?.link)]" fit="cover" preview-teleported /> <el-image v-else :src="scope.row?.smallUrl" :preview-src-list="[scope.row?.showUrl]" fit="cover" preview-teleported /> <el-image v-else-if="scope.row?.resultType === 4" :src="getzsSmallImg(scope.row?.link)" :preview-src-list="[getzsSmallImg(scope.row?.link)]" fit="cover" preview-teleported /> <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] ? photoTypeMap[scope.row.photoType]:'tif' }}</span> <template #default="scope"> <span>{{ photoTypeMap[scope.row.photoType] ? photoTypeMap[scope.row.photoType] : '--' }}</span> </template> </el-table-column> <el-table-column property="resultType" label="文件格式"> @@ -54,21 +85,44 @@ <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> <span class="delete" @click="deleteDetail(scope.row)" v-if="scope.row.resultType !== 2" >删除</span > <span class="location" @click="positionDetail(scope.row)" v-if="shouldShowLocation(scope.row)">定位</span> <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" /> <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" align-center> <el-dialog v-model="dialogVisible" width="60%" append-to-body @close="dialogClose" align-center > <template #header="{ titleId, titleClass }"> <div class="my-header"> <!-- <h4 :id="titleId" :class="titleClass">{{ detailTitle }}</h4> --> @@ -81,7 +135,7 @@ <video v-if="dialogDetailList?.resultType === 1" style="width: 100%" class="videoItem" class="videoItem" ref="videoRefs" controls autoplay @@ -109,12 +163,10 @@ class="title-input" clearable /> <span class="editname" @click="editTitle(dialogDetailList)" ><el-icon> <Edit /> </el-icon <span class="editname" @click="editTitle(dialogDetailList)" ><el-icon> <Edit /> </el-icon ></span> </div> </div> <div>任务名称:{{ dialogDetailList?.jobName ? dialogDetailList?.jobName : '--' }}</div> <div> @@ -123,7 +175,7 @@ <div> 拍摄机巢:{{ dialogDetailList?.nestName ? dialogDetailList?.nestName : '--' }} </div> <div> <div v-if="dialogDetailList?.resultType != 4"> 照片位置:{{ _.round(dialogDetailList?.longitude, 3) }},{{ _.round(dialogDetailList?.latitude, 3) }} @@ -136,7 +188,7 @@ 文件类型:{{ photoTypeMap[dialogDetailList?.photoType] ? photoTypeMap[dialogDetailList?.photoType] : 'tif' : '--' }} </div> <div> @@ -147,7 +199,7 @@ }} </div> <div> 照片文件大小:{{ 文件大小:{{ bytesToMB(dialogDetailList?.attachSize) ? bytesToMB(dialogDetailList?.attachSize) : '--' @@ -166,47 +218,68 @@ </div> </el-dialog> <!-- 全景预览 --> <PanoramaPopup v-model:panoramaParamsShow="panoramaParamsShow" v-model:panoramaParamsUrl="panoramaParamsUrl"> <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"> <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> <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> <dataCenterMap ref="mapComponent" v-model:show="dataCenterMapVisible" :jobId="jobId" @lookDetail="lookDetail" :dotData="mapList" ></dataCenterMap> </div> </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 { 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, @@ -214,24 +287,24 @@ downloadApi, updataTitleApi, getOrthoimageInfo, } from '@/api/dataCenter/dataCenter' import { getShowImg, getSmallImg, getzsSmallImg } from '@/utils/util' import { onMounted, watch } from 'vue' import dayjs from 'dayjs' } 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 '0' return (bytes / 1048576).toFixed(decimalPlaces) + ' MB' function bytesToMB(bytes, decimalPlaces = 2) { if (typeof bytes !== 'number' || bytes < 0) return '0'; return (bytes / 1048576).toFixed(decimalPlaces) + ' MB'; } // 视频一帧 function convertVideoUrlToThumbnail (videoUrl) { function convertVideoUrlToThumbnail(videoUrl) { // 检查是否是有效的视频URL if (!videoUrl || typeof videoUrl !== 'string') { return videoUrl return videoUrl; } // 替换文件扩展名 const thumbnailUrl = videoUrl.replace(/\.mp4$/, '_small.jpg') return thumbnailUrl const thumbnailUrl = videoUrl.replace(/\.mp4$/, '_small.jpg'); return thumbnailUrl; } const resultTypeMap = { 0: '照片', @@ -239,84 +312,84 @@ 2: 'AI识别', 5: '全景', 4: '正射', } }; const photoTypeMap = { visible: '可见光', ir: '红外', } }; // 判断省市区 const areaCode = userInfo.value?.detail?.areaCode || '' 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) // 区县级 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(',') function processAddress(str) { if (!str) return ''; const parts = str.split(','); // 省级账号:显示市和区(去除省级) if (isProvinceLevel.value) { return parts.filter(part => !part.endsWith('省')).join(',') return parts.filter(part => !part.endsWith('省')).join(','); } // 市级账号:只显示区(去除省和市) if (isCityLevel.value) { return parts.filter(part => !part.endsWith('省') && !part.endsWith('市')).join(',') return parts.filter(part => !part.endsWith('省') && !part.endsWith('市')).join(','); } // 区县级账号:不显示区域信息(在模板中已隐藏该列) return '' return ''; } const showRegionColumn = computed(() => { // 是否显示列 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 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 panoramaParamsShow = ref(false); const panoramaParamsUrl = ref(null); const clickpanorama = val => { panoramaParamsShow.value = true panoramaParamsUrl.value = val.link } panoramaParamsShow.value = true; panoramaParamsUrl.value = val.link; }; // 视频 const currentVideoTitle = ref('') const VideoShow = ref(false) const currentVideoUrl = ref(null) 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 } 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 tableData = ref([]); // 获取列表数据 const getaiImagesPage = () => { loadings.value = true loadings.value = true; const params = { orderByCreateTime: jobListParams.orderByCreateTime, ...jobListParams.searchParams, } }; getaiImagesPageAPI(params, { current: jobListParams.current, size: jobListParams.size size: jobListParams.size, }) .then(res => { total.value = res.data.data.total total.value = res.data.data.total; tableData.value = res.data.data.records.map(i => ({ ...i, checked: false, @@ -324,41 +397,41 @@ smallUrl: getSmallImg(i?.link), showUrl: getShowImg(i?.link), file_name: i.name.split('/').pop(), })) })); }) .catch(error => { // 可选:这里可以添加错误处理逻辑 console.error("获取数据失败:", error) console.error('获取数据失败:', error); }) .finally(() => { loadings.value = false // 无论成功失败都会执行 }) } loadings.value = false; // 无论成功失败都会执行 }); }; // 查询 const searchClick = params => { jobListParams.current = 1 jobListParams.size = 10 jobListParams.searchParams = params getaiImagesPage() } jobListParams.current = 1; jobListParams.size = 10; jobListParams.searchParams = params; getaiImagesPage(); }; const handleSizeChange = val => { jobListParams.size = val getaiImagesPage() } jobListParams.size = val; getaiImagesPage(); }; const handleCurrentChange = val => { jobListParams.current = val getaiImagesPage() } jobListParams.current = val; getaiImagesPage(); }; // 多选 const selectedRows = ref([]) const selectedRows = ref([]); const handleSelectionChange = val => { // 更新选中状态 tableData.value.forEach(item => { item.checked = val.some(selected => selected.id === item.id) }) item.checked = val.some(selected => selected.id === item.id); }); selectedRows.value = val } selectedRows.value = val; }; // 删除 const deleteDetail = val => { ElMessageBox.confirm('您确定删除吗?', '提示', { @@ -369,36 +442,36 @@ .then(() => { deleteFileMultipleApi(val.id) .then(res => { ElMessage.success('删除成功') getaiImagesPage() ElMessage.success('删除成功'); getaiImagesPage(); }) .catch(error => { ElMessage.error('删除失败') }) ElMessage.error('删除失败'); }); }) .catch(() => { }) } .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) 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('请选择文件') const list = selectedRows.value.filter(i => i.checked); if (!list?.length) return ElMessage.warning('请选择文件'); if (list.length === 1) { list.forEach((item, index) => { const suffix = item.url.split('.').pop() aLinkDownload(item.url, item.nickName + '.' + suffix) }) const suffix = item.url.split('.').pop(); aLinkDownload(item.url, item.nickName + '.' + suffix); }); } else { loading = ElLoading.service({ background: 'rgba(0, 0, 0, 0.5)', text: '打包中,请稍等...' }) const fileIds = list.map(i => i.id) loading = ElLoading.service({ background: 'rgba(0, 0, 0, 0.5)', text: '打包中,请稍等...' }); const fileIds = list.map(i => i.id); let aaa = { areaCode: '', attachIds: fileIds, @@ -410,236 +483,234 @@ resultType: '', startTime: '', wayLineJobIds: [], } }; downloadApi(aaa).then(res => { aLinkDownload(res.data.data, `sjzx-file-pack-${dayjs().format('YYYYMMDDHHmmss')}.zip`) loading.close() }) aLinkDownload(res.data.data, `sjzx-file-pack-${dayjs().format('YYYYMMDDHHmmss')}.zip`); loading.close(); }); } } }; // 下载 const downloadFile = () => { fileDownload() } fileDownload(); }; const detailDownLoad = val => { const suffix = val.link?.split('.').pop() aLinkDownload(val.link, val?.nickName + '.' + suffix) } const suffix = val.link?.split('.').pop(); aLinkDownload(val.link, val?.nickName + '.' + suffix); }; // 全部下载 const aLLDownloadFile = () => { loading = ElLoading.service({ background: 'rgba(0, 0, 0, 0.5)', text: '打包中,请稍等...' }) loading = ElLoading.service({ background: 'rgba(0, 0, 0, 0.5)', text: '打包中,请稍等...' }); const params = { ...jobListParams.searchParams, } params.areaCode = '' }; params.areaCode = ''; downloadApi(params).then(res => { aLinkDownload(res.data.data, `sjzx-file-pack-${dayjs().format('YYYYMMDDHHmmss')}.zip`) loading.close() }) } 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 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 jobId.value = val.wayLineJobId; mapList.value = val; dataCenterMapVisible.value = true; // 确保地图组件加载完成 nextTick(() => { if (mapComponent.value) { // 调用子组件方法并传递数据 mapComponent.value.initEntityOrPopup(val) mapComponent.value.initEntityOrPopup(val); } }) } }); }; const fileNameedit = ref('') const fileNameedit = ref(''); // 编辑文件名 const editTitle = val => { val.checkedinput = true fileNameedit.value = val?.nickName } val.checkedinput = true; fileNameedit.value = val?.nickName; }; // 通用空值检查函数 const validateNickname = (name, fieldName) => { if (!name || name.trim() === '') { ElMessage.warning(`${fieldName}不能为空`) return false ElMessage.warning(`${fieldName}不能为空`); return false; } if (name.length > 50) { ElMessage.warning(`${fieldName}不能超过50个字符`) return false ElMessage.warning(`${fieldName}不能超过50个字符`); return false; } return true } return true; }; // 保存文件名 const saveTitle = item => { const updateparams = { id: item.id, nickName: `${item.nickName.trim()}`, } }; // 验证并提示 if (!validateNickname(updateparams.nickName, '名称')) return item.checkedinput = false detailTitle.value = item.nickName if (!validateNickname(updateparams.nickName, '名称')) return; item.checkedinput = false; detailTitle.value = item.nickName; updataTitleApi(updateparams) .then(res => { if (res.status === 200) { ElMessage.success('修改成功') getaiImagesPage() ElMessage.success('修改成功'); getaiImagesPage(); } else { ElMessage.error(res.data.message || '修改失败') ElMessage.error(res.data.message || '修改失败'); } }) .catch(error => { ElMessage.error('请求失败,请稍后重试') console.error('API error:', error) }) } ElMessage.error('请求失败,请稍后重试'); console.error('API error:', error); }); }; // 正射地图 const yxShowBox = ref(false) const yxShowBox = ref(false); const removeHandler = () => { handler?.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK) handler?.destroy() handler = null } const isMapInitialized = ref(false) //地图加载 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 (viewer || isMapInitialized.value) return; const container = document.getElementById('detaildataCenterMap'); if (!container) { console.error('地图容器未找到') return 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('地图初始化完成'); } }); viewer = viewInstance.value.getViewer(); viewerRef.value = viewer; isMapInitialized.value = true; }; // 判断是否是正射 function isTifFile (filename) { const lastDot = filename.lastIndexOf('.') if (lastDot === -1) return false // 无后缀 return filename.slice(lastDot + 1).toLowerCase() === 'tif' 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 } 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 positiongeom = ref('') const odmToken = ref(null) const dialogVisible = ref(false); const dialogDetailList = ref(null); const detailTitle = ref(''); let curCustomImageryProvider = null; const orthoimageApi = ref(''); const positiongeom = 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 console.log('3333', detailTitle.value, dialogDetailList.value) await nextTick() initMap() positiongeom.value = res.data.data.geom orthoimageApi.value = res.data.data.orthoimageApi odmToken.value = res.data.data.odmToken dialogVisible.value = true; detailTitle.value = val.nickName; dialogDetailList.value = val; await nextTick(); initMap(); positiongeom.value = res.data.data.geom; 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, url: `${import.meta.env.VITE_APP_AREA_NAME}/webodm${res.data.data.orthoimageApi}?jwt=${ res.data.data.odmToken.split(' ')[1] }`, }) ) yxShowBox.value = true ); yxShowBox.value = true; // 添加定位 if (positiongeom.value) { // 解析WKT格式的多边形坐标 const wktString = positiongeom.value const coordinates = parseWKTCoordinates(wktString) const wktString = positiongeom.value; const coordinates = parseWKTCoordinates(wktString); if (coordinates.length > 0) { // 创建多边形边界 const positions = coordinates.map(coord => Cesium.Cartesian3.fromDegrees(coord[0], coord[1]) ) ); // 计算多边形的包围球 const boundingSphere = Cesium.BoundingSphere.fromPoints(positions) const viewer = viewInstance.value.getViewer() const boundingSphere = Cesium.BoundingSphere.fromPoints(positions); const viewer = viewInstance.value.getViewer(); if (viewer && viewer.camera) { viewer.camera.flyToBoundingSphere(boundingSphere, { duration: 0, // 飞行动画持续时间(秒) offset: new Cesium.HeadingPitchRange(0, -0.5, boundingSphere.radius * 1.5), }) duration: 0, // 增加动画时间使过渡更平滑 offset: new Cesium.HeadingPitchRange( Cesium.Math.toRadians(-45), // 航向角:-45度(东北方向) Cesium.Math.toRadians(-90), // 俯仰角:-30度(向下倾斜30度) boundingSphere.radius * 2.5 // 距离:2.5倍半径 ), }); } else { console.error('无法获取有效的 Viewer 或 camera 对象') console.error('无法获取有效的 Viewer 或 camera 对象'); } } } } }) }); } else { getAttachInfoAPI(val.id).then(res => { detailTitle.value = res.data.data.nickName.split('.jpeg')[0] detailTitle.value = res.data.data.nickName.split('.jpeg')[0]; dialogDetailList.value = { ...res.data.data, checkedinput: false, editName: res.data.data?.nickName?.split('.jpeg')[0], } dialogVisible.value = true }) }; dialogVisible.value = true; }); } } }; // WKT坐标解析函数 function parseWKTCoordinates (wktString) { function parseWKTCoordinates(wktString) { // 解析POLYGON格式的WKT字符串 const regex = /POLYGON\s*\(\((.*?)\)\)/i const match = wktString.match(regex) if (!match || !match[1]) return [] const regex = /POLYGON\s*\(\((.*?)\)\)/i; const match = wktString.match(regex); if (!match || !match[1]) return []; // 分割坐标对 const coordinatePairs = match[1].split(/,\s*/) const coordinatePairs = match[1].split(/,\s*/); return coordinatePairs.map(pair => { const [lon, lat] = pair.trim().split(/\s+/).map(Number) return [lon, lat] }) const [lon, lat] = pair.trim().split(/\s+/).map(Number); return [lon, lat]; }); } function dialogClose () { function dialogClose() { // 对话框关闭时清理资源 viewInstance.value?.viewerDestroy() viewer = null isMapInitialized.value = false viewInstance.value?.viewerDestroy(); viewer = null; isMapInitialized.value = false; } onMounted(() => { getaiImagesPage() getaiImagesPage(); // 监听打开全景事件 EventBus.on('open-panorama', params => { panoramaParamsShow.value = params.show panoramaParamsUrl.value = params.url }) }) panoramaParamsShow.value = params.show; panoramaParamsUrl.value = params.url; }); }); onBeforeUnmount(() => { // 组件卸载时移除事件监听,防止内存泄漏 EventBus.off('open-panorama') viewInstance.value?.removeAllCustomImageryProviderDataSource(curCustomImageryProvider) viewInstance.value?.viewerDestroy() }) EventBus.off('open-panorama'); viewInstance.value?.removeAllCustomImageryProviderDataSource(curCustomImageryProvider); viewInstance.value?.viewerDestroy(); }); </script> <style scoped lang="scss"> @@ -716,20 +787,20 @@ .detailContainer { display: flex; justify-content: space-between; align-items: center; align-items: center; .leftImg { width: 70%; height: 500px; overflow: hidden; overflow: hidden; img { width: 100%; height: 100%; } .videoItem { width: 100%; .videoItem { width: 100%; height: 100%; object-fit: cover; } } #detaildataCenterMap { width: 100%; height: 100%; @@ -798,7 +869,7 @@ margin-bottom: 0 !important; font-size: 16px; >.editText { > .editText { cursor: pointer; margin-right: 6px; @@ -817,4 +888,4 @@ } } } </style> </style> src/views/device/airport.vue
@@ -115,7 +115,8 @@ </template> </el-dialog> <el-drawer title="机场授权管理" size="60%" append-to-body v-model="devicePerShareVisible" :direction="'rtl'"> <el-drawer class="ztzf-drawer-body-basic-container" title="机场授权管理" size="60%" append-to-body v-model="devicePerShareVisible" :direction="'rtl'"> <DevicePerShare ref="devicePerShare" /> </el-drawer> @@ -352,10 +353,20 @@ }, { label: '行政区划', prop: 'area_name', hide: true, editDisplay: false, //编辑显示 viewDisplay: true, //查看显示 labelWidth: 130, }, { label: '行政区划', prop: 'area_code', type: 'cascader', labelWidth: 130, hide: true, editDisplay: true, viewDisplay: false, props: { label: 'title', value: 'value', @@ -1010,12 +1021,13 @@ if (['edit', 'view'].includes(type)) { getDetail(this.form.id).then(res => { const data = res.data.data this.form = { ...data, area_code: this.getFullAreaCode(data.area_code), area_name: this.form.area_name, duration_of_insurance: [data?.insure_start_time || '', data?.insure_expired_time || ''] } console.log('this.form', this.form) done() }) src/views/device/components/devicePerShare.vue
@@ -1,35 +1,20 @@ <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-save="rowSave" @row-update="rowUpdate" :before-open="beforeOpen" @search-change="searchChange" @search-reset="searchReset" @selection-change="selectionChange" @current-change="currentChange" @size-change="sizeChange" @refresh-change="refreshChange" > <avue-crud :option="option" :table-loading="loading" :data="data" v-model:page="page" ref="crud" @row-del="rowDel" v-model="form" :permission="permissionList" @row-save="rowSave" @row-update="rowUpdate" :before-open="beforeOpen" @search-change="searchChange" @search-reset="searchReset" @selection-change="selectionChange" @current-change="currentChange" @size-change="sizeChange" @refresh-change="refreshChange"> </avue-crud> </basic-container> </template> <script> import { getList, remove, add, update } from '@/api/device/devicePerShare'; import { getDeptTreeByNotItself } from '@/api/system/dept'; import { mapGetters } from 'vuex'; import { getList, remove, add, update } from '@/api/device/devicePerShare' import { getDeptTreeByNotItself } from '@/api/system/dept' import { mapGetters } from 'vuex' export default { data() { data () { return { deptId: '', deviceSn: '', @@ -58,6 +43,10 @@ excelBtn: false, dialogClickModal: false, grid: false, height: 'auto', calcHeight: 20, column: [ { label: '授权单位', @@ -136,118 +125,118 @@ ], }, data: [], }; } }, 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(',') }, }, methods: { init(data) { this.deptId = data.dept_id; this.deviceSn = data.device_sn; this.getDeptTreeInfo(); this.getLoadPage(this.page); init (data) { this.deptId = data.dept_id this.deviceSn = data.device_sn this.getDeptTreeInfo() this.getLoadPage(this.page) }, getDeptTreeInfo() { getDeptTreeInfo () { getDeptTreeByNotItself(this.deptId, this.deviceSn).then(res => { const column = this.findObject(this.option.column, 'deptId'); this.deptTreeData = res.data.data; column.dicData = res.data.data; }); const column = this.findObject(this.option.column, 'deptId') this.deptTreeData = res.data.data column.dicData = res.data.data }) }, rowSave(row, done, loading) { rowSave (row, done, loading) { // 添加毫秒级时间戳 row['createTime'] = Date.now(); row['deviceSn'] = this.deviceSn; row['createTime'] = Date.now() row['deviceSn'] = this.deviceSn add(row).then( () => { this.initFlag = false; this.getLoadPage(this.page); this.getDeptTreeInfo(); this.initFlag = false this.getLoadPage(this.page) this.getDeptTreeInfo() this.$message({ type: 'success', message: '操作成功!', }); done(); }) done() }, error => { window.console.log(error); loading(); window.console.log(error) loading() } ); ) }, rowUpdate(row, index, done, loading) { row['device_sn'] = this.deviceSn; rowUpdate (row, index, done, loading) { row['device_sn'] = this.deviceSn update(row).then( () => { this.getLoadPage(this.page); this.getLoadPage(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.getDeptTreeInfo(); this.getLoadPage(this.page); this.getDeptTreeInfo() this.getLoadPage(this.page) this.$message({ type: 'success', message: '操作成功!', }); }); }) }) }, searchReset() { this.query = {}; this.getLoadPage(this.page); searchReset () { this.query = {} this.getLoadPage(this.page) }, searchChange(params, done) { this.query = params; this.page.currentPage = 1; this.getLoadPage(this.page, params); done(); searchChange (params, done) { this.query = params this.page.currentPage = 1 this.getLoadPage(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: '确定', @@ -255,48 +244,48 @@ type: 'warning', }) .then(() => { return remove(this.ids); return remove(this.ids) }) .then(() => { this.getLoadPage(this.page); this.getLoadPage(this.page) this.$message({ type: 'success', message: '操作成功!', }); this.$refs.crud.toggleSelection(); }); }) this.$refs.crud.toggleSelection() }) }, beforeOpen(done, type) { done(); beforeOpen (done, type) { done() }, currentChange(currentPage) { this.page.currentPage = currentPage; this.getLoadPage(this.page, this.query); // 添加数据刷新 currentChange (currentPage) { this.page.currentPage = currentPage this.getLoadPage(this.page, this.query) // 添加数据刷新 }, sizeChange(pageSize) { this.page.pageSize = pageSize; this.page.currentPage = 1; // 每页条数改变时,重置到第一页 this.getLoadPage(this.page, this.query); // 添加数据刷新 sizeChange (pageSize) { this.page.pageSize = pageSize this.page.currentPage = 1 // 每页条数改变时,重置到第一页 this.getLoadPage(this.page, this.query) // 添加数据刷新 }, refreshChange() { this.getLoadPage(this.page, this.query); refreshChange () { this.getLoadPage(this.page, this.query) }, getLoadPage(page, params = {}) { params['deviceSn'] = this.deviceSn; this.loading = true; getLoadPage (page, params = {}) { params['deviceSn'] = this.deviceSn this.loading = true getList(page.currentPage, page.pageSize, params).then(res => { const data = res.data.data; this.page.total = data.total; this.data = data.records; this.loading = false; this.selectionClear(); const data = res.data.data this.page.total = data.total this.data = data.records this.loading = false this.selectionClear() }).catch(err => { console.error('加载数据失败:', err); this.loading = false; }); console.error('加载数据失败:', err) this.loading = false }) }, }, }; } </script> <style> @@ -304,6 +293,7 @@ background-color: aqua; border-radius: 4px; } .firmware_status:hover { cursor: pointer; } src/views/job/components/SearchBox.vue
@@ -59,7 +59,7 @@ </el-form-item> <el-form-item label="任务算法:" v-if="isExpand" class="taskAlgorithm"> <TaskAlgorithmBusiness :setWidth="162" :setWidth="222" :showAlgorithm="true" @algorithmChange="algorithmChange" /> @@ -451,6 +451,7 @@ .el-form-item { margin-bottom: 0; width: 233px; margin-right: 20px; .el-form-item__label { color: #363636; src/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue
@@ -247,8 +247,8 @@ } &.cancelTask { border: 1px solid #3efe96; color: #3efe96; border: 1px solid #00AA2D; color: #00AA2D; } } src/views/job/components/TaskTop/TaskEvent.vue
@@ -81,7 +81,7 @@ { name: '任务', type: 'bar', barWidth: 8, barWidth: 6, itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#FF8902' }, @@ -92,7 +92,7 @@ { name: '事件', type: 'bar', barWidth: 8, barWidth: 6, itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#4363FF' }, src/views/job/components/TaskTop/TaskTotal.vue
@@ -40,10 +40,10 @@ const list = ref([ { name: '计划执行', value: '0', color: '#1C5CFF', img: jhzxpng }, { name: '已执行', value: '0', color: '#00AA2D', img: yzxpng }, { name: '执行中', value: '0', color: '#FFA768', img: zxzpng}, { name: '执行中', value: '0', color: '#FF720F', img: zxzpng}, { name: '执行失败', value: '0', color: '#FF4848', img: zxsbpng }, { name: '待执行', value: '0', color: '#00CDC2', img: dzxpng }, { name: '取消执行', value: '0', color: '#A6A6A6', img: qxzxpng }, { name: '取消执行', value: '0', color: '#373333', img: qxzxpng }, ]) // 获取任务统计总数 src/views/tickets/orderLog.vue
@@ -573,19 +573,55 @@ calcHeight: 196, column: [ { label: '工单编号', prop: 'job_info_num', width: 100, ellipsis: true, overHidden: true }, { label: '工单名称', prop: 'name', width: 100, ellipsis: true, overHidden: true }, { label: '工单状态', prop: 'status', width: 88 }, { label: '所属单位', prop: 'dept_name', width: 100, ellipsis: true }, { label: '发起时间', prop: 'create_time', width: 144, ellipsis: true }, { label: '已执行次数', prop: 'job_num', width: 96, ellipsis: true }, { label: '工单内容', prop: 'content', ellipsis: true, overHidden: true }, { label: '关联航线', prop: 'wayline_name', width: 100, ellipsis: true, overHidden: true }, { label: '关联算法', prop: 'ai_type_str', width: 100, ellipsis: true, overHidden: true }, { label: '关联机巢', prop: 'device_names', width: 100, ellipsis: true, overHidden: true }, { label: '工单编号', prop: 'job_info_num', width: 100, ellipsis: true, overHidden: true, showOverflowTooltip: true, }, { label: '工单名称', prop: 'name', width: 100, ellipsis: true, overHidden: true, showOverflowTooltip: true, }, { label: '工单状态', prop: 'status', width: 88, showOverflowTooltip: true, }, { label: '所属单位', prop: 'dept_name', width: 100, ellipsis: true, showOverflowTooltip: true, }, { label: '发起时间', prop: 'create_time', width: 144, ellipsis: true, showOverflowTooltip: true, }, { label: '已执行次数', prop: 'job_num', width: 96, ellipsis: true, showOverflowTooltip: true, }, { label: '工单内容', prop: 'content', ellipsis: true, overHidden: true, showOverflowTooltip: true, }, { label: '关联航线', prop: 'wayline_name', width: 100, ellipsis: true, overHidden: true, showOverflowTooltip: true, }, { label: '关联算法', prop: 'ai_type_str', width: 100, ellipsis: true, overHidden: true, showOverflowTooltip: true, }, { label: '关联机巢', prop: 'device_names', width: 100, ellipsis: true, overHidden: true, showOverflowTooltip: true, }, { label: '创建人', prop: 'creator_name', width: 96, ellipsis: true, overHidden: true }, { label: '关联机巢', prop: 'device_names', width: 112, ellipsis: true, overHidden: true }, { label: '创建人', prop: 'creator_name', width: 96, ellipsis: true, overHidden: true, showOverflowTooltip: true, }, { label: '关联机巢', prop: 'device_names', width: 112, ellipsis: true, overHidden: true, showOverflowTooltip: true, }, { label: '工单周期频次', prop: '', @@ -593,6 +629,7 @@ formatter: row => this.formatCycleTime(row), html: true, ellipsis: true, showOverflowTooltip: true, // overHidden: true }, ], @@ -1089,7 +1126,7 @@ //按照航线来 const params = { type: currentLine.wayline_type, type: [2, 4, 5].includes(currentLine.wayline_type) ? 2 : 0, wayline_id: waylineId, polygon } @@ -1336,12 +1373,9 @@ &> ::v-deep(.el-button) { flex: 1; max-width: 44px; margin-left: 0; margin-right: 10px; &:nth-child(4n) { margin-left: 0; } } } } src/views/tickets/ticket.vue
@@ -152,7 +152,10 @@ :on-change="handleFileChange" :on-remove="handleUploadRemove" :before-upload="beforeUpload" :file-list="form.photos" :limit="1" accept="image/*" class="create-upload"> <template v-if="form.photos.length < 1"> <i class="el-icon-plus">+</i> <!-- <i class="el-icon-plus">+</i> --> <div class="el-icon-plus"> <span>+</span> </div> </template> </el-upload> <div class="upload-tip">需上传含有地址信息的照片(jpg、jpeg、png),且不超过5M</div> @@ -173,7 +176,8 @@ <!-- 工单详情对话框 --> <el-dialog v-model="detailVisible" title="工单详情" width="80%" append-to-body> <div class="detail-container"> <div class="event-title-center">{{ currentDetail.orderName || '事件名称' }}</div> <div class="detail-top-title"> <div class="event-title-center event-orderNumber">{{ currentDetail.orderNumber || '工单编号' }}</div> <div class="event-title-center">{{ currentDetail.orderName || '事件名称' }}</div></div> <div v-if="totalTime" class="event-total-time"> 总耗时:{{ totalTime }} </div> @@ -305,7 +309,10 @@ :before-upload="beforeUpload" :file-list="currentDetail.photos || []" :limit="1" accept="image/*" class="detail-upload"> <template v-if="!currentDetail.photos || currentDetail.photos.length < 1"> <i class="el-icon-plus">+</i> <!-- <i class="el-icon-plus">+</i> --> <div class="el-icon-plus"> <span>+</span> </div> </template> </el-upload> <div class="el-upload__tip" v-if="hasProcessedAndOverBtnPermission()"> @@ -493,7 +500,7 @@ @close="reCheckDialog = false"> <div class="dialog-footer"> <el-button type="primary" @click="reCheckConfirm(1)">人工复核</el-button> <el-button type="primary" @click="reCheckConfirm(2)">AI复核</el-button> <el-button type="primary" @click="reCheckConfirm(2)">无人机复核</el-button> </div> </el-dialog> </basic-container> @@ -1351,6 +1358,17 @@ if (response.data.code === 0) { this.$message.success('草稿保存成功') this.dialogVisible = false this.form={ name: '', type: '', department: '', handler: '', algorithm: [], // 关联算法改为数组 location: [], // 将存储为[经度, 纬度, 地址]格式 address: '', photos: [], content: '', // 新增字段,用于存储后端返回的 content }, this.fetchTableData() } else { throw new Error(response.data.msg || '保存失败') @@ -1559,6 +1577,8 @@ }) } this.currentDetail.status = row.status } catch (error) { if (this.activeTab === 'myTickets') { const statusArr = this.workType === 1 ? ['3', '4'] : this.fixedStatuses @@ -1573,6 +1593,7 @@ } this.currentDetail = detailData console.log('this.currentDetail',this.currentDetail); this.detailVisible = true this.$nextTick(() => { if (this.$refs.MapContainer && this.$refs.MapContainer.initAddEntity) { @@ -2758,6 +2779,13 @@ .detail-container { padding: 20px; .detail-top-title { display: flex; justify-content: center; align-items: center; .event-orderNumber{ margin-right: 10px;} } } .status-flow { src/views/wel/components/statistics.vue
@@ -198,7 +198,7 @@ // 样式配置对象 const statusStyles = { 机巢保险: { 0: { class: 'expired', color: 'rgba(186, 186, 186, 1)', background: 'rgba(186, 186, 186, 1)' }, 0: { class: 'expired', color: '#FFA600', background: '#FFA600' }, 1: { class: 'normal', color: 'rgba(27, 148, 255, 1)', background: 'rgba(27, 148, 255, 1)' }, }, src/views/wel/index.vue
@@ -378,7 +378,7 @@ yAxis: [ { type: 'log', name: '单位:万套', name: '单位:万', nameTextStyle: { color: '#383838', fontSize: '1.2rem' @@ -436,7 +436,7 @@ { name: '飞行时长', type: 'bar', barWidth: '12px', barWidth: '8px', itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#29acff' }, @@ -449,7 +449,7 @@ { name: '飞行里程', type: 'bar', barWidth: '12px', barWidth: '8px', itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#01c871' },