From d60b1396df45de76c6bae6aa75c15602dd4c2570 Mon Sep 17 00:00:00 2001
From: chenyao <1219716595@qq.com>
Date: Sat, 31 Jan 2026 14:22:27 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'
---
applications/drone-command/src/page/index/top/index.vue | 4
applications/drone-command/src/views/areaManage/areaStatistics/index.vue | 10
applications/drone-command/src/views/areaManage/sceneConfig/index.vue | 8
applications/task-work-order/src/views/orderView/organizational/agenciesManagement/agenciesApi.js | 3
applications/drone-command/src/views/areaManage/sceneManage/sceneManageApi.js | 9
applications/drone-command/src/components/map-container/common-cesium-map.vue | 29
applications/task-work-order/src/views/orderView/organizational/zoningManagement/FormDiaLog.vue | 5
pnpm-lock.yaml | 60
applications/task-work-order/src/views/orderView/organizational/zoningManagement/index.vue | 80 +
applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue | 89 ++
applications/drone-command/src/components/map-container/device-map-container.vue | 17
applications/task-work-order/src/views/flow/deploy.vue | 160 ++++
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/ViewDiaLog.vue | 2
applications/task-work-order/src/views/orderView/organizational/agenciesManagement/FormDiaLog.vue | 5
applications/task-work-order/src/views/orderView/organizational/agenciesManagement/index.vue | 90 +
packages/constants/package.json | 8
applications/task-work-order/src/views/orderView/orderManage/orderManage/index.vue | 46
applications/task-work-order/src/views/flow/model.vue | 548 +++++++++++++
applications/task-work-order/src/views/flow/manager.vue | 369 +++++++++
applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue | 57
applications/task-work-order/src/views/orderView/orderManage/orderManage/RefuseOrderDialog.vue | 6
applications/task-work-order/src/views/orderView/organizational/zoningManagement/zoningApi.js | 3
applications/task-work-order/src/views/flow/follow.vue | 199 ++++
/dev/null | 14
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/FormDiaLog.vue | 120 ++
applications/drone-command/src/views/areaManage/sceneManage/FormDiaLog.vue | 155 +++
applications/drone-command/src/views/api.txt | 77 -
applications/drone-command/src/components/map-container/components/DevicePopup.vue | 4
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/index.vue | 30
applications/drone-command/src/views/areaManage/precinctInfo/FormDiaLog.vue | 43 +
applications/drone-command/src/views/areaManage/sceneConfig/FormDiaLog.vue | 122 ++
31 files changed, 2,103 insertions(+), 269 deletions(-)
diff --git a/applications/drone-command/src/components/map-container/common-cesium-map.vue b/applications/drone-command/src/components/map-container/common-cesium-map.vue
index e728ff2..5a93db0 100644
--- a/applications/drone-command/src/components/map-container/common-cesium-map.vue
+++ b/applications/drone-command/src/components/map-container/common-cesium-map.vue
@@ -3,6 +3,8 @@
</template>
<script setup>
+import * as Cesium from 'cesium'
+
import { onBeforeUnmount, onMounted, watch } from 'vue'
import { PublicCesium } from '@/utils/cesium/publicCesium'
import { loadJaAdminBoundary, removeJaAdminBoundary } from '@/utils/cesium/adminBoundary'
@@ -172,11 +174,36 @@
})
}
+const zoomToAdminBoundary = async () => {
+ if (!viewer) return
+ const resolveSources = () => {
+ if (adminBoundarySources) return adminBoundarySources
+ const boundarySource = viewer.dataSources.getByName(ADMIN_BOUNDARY_NAMES.boundary)?.[0] ?? null
+ const lineSource = viewer.dataSources.getByName(ADMIN_BOUNDARY_NAMES.line)?.[0] ?? null
+ const labelSource = viewer.dataSources.getByName(ADMIN_BOUNDARY_NAMES.label)?.[0] ?? null
+ if (boundarySource || lineSource || labelSource) {
+ adminBoundarySources = { boundarySource, lineSource, labelSource }
+ }
+ return adminBoundarySources
+ }
+ let sources = resolveSources()
+ if (!sources?.boundarySource) {
+ sources = await loadJaAdminBoundary(viewer, { zoomTo: false })
+ adminBoundarySources = sources
+ }
+ const target = sources?.boundarySource
+ if (!target) return
+ await viewer.flyTo(target, {
+ duration: 0,
+ offset: new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-75), 0),
+ })
+}
+
const getMap = () => ({ viewer, publicCesium: viewInstance })
const getViewer = () => viewer
const getPublicCesium = () => viewInstance
-defineExpose({ getMap, getViewer, getPublicCesium, setAdminBoundaryVisible })
+defineExpose({ getMap, getViewer, getPublicCesium, setAdminBoundaryVisible, zoomToAdminBoundary })
</script>
<style scoped lang="scss">
diff --git a/applications/drone-command/src/components/map-container/components/DevicePopup.vue b/applications/drone-command/src/components/map-container/components/DevicePopup.vue
index e1f24f5..2f5c8a8 100644
--- a/applications/drone-command/src/components/map-container/components/DevicePopup.vue
+++ b/applications/drone-command/src/components/map-container/components/DevicePopup.vue
@@ -151,9 +151,9 @@
return `${val}台`
})
const popupRangeText = computed(() => {
- const val = props.device?.effectiveRangeKm
+ const val = Number(props.device?.effectiveRangeKm).toFixed(0) || 0
if (val == null) return '-'
- return `${val}KM`
+ return `${val}m`
})
</script>
diff --git a/applications/drone-command/src/components/map-container/device-map-container.vue b/applications/drone-command/src/components/map-container/device-map-container.vue
index 659f8ca..0e84459 100644
--- a/applications/drone-command/src/components/map-container/device-map-container.vue
+++ b/applications/drone-command/src/components/map-container/device-map-container.vue
@@ -50,7 +50,9 @@
import { buildEllipsePositions } from '@/utils/cesium/shapeTools'
import { AREA_TYPE_STYLE_MAP, BUFFER_LEVEL_STYLES, DEFAULT_AREA_STYLE } from '@ztzf/constants'
import { fwAreaDivideListApi } from '@/views/areaManage/partition/partitionApi'
-import { fwDefenseSceneListApi } from '@/views/areaManage/sceneConfig/sceneConfigApi'
+
+import { fwDefenseSceneManageListApi } from '@/views/areaManage/sceneManage/sceneManageApi'
+
import { cockpitAggregationApi } from '@/api/dataCockpit'
import layerControlIcon from '@/assets/images/dataCockpit/layerControl.png'
import equipmentIcon from '@/assets/images/dataCockpit/map/equipment.png'
@@ -60,6 +62,7 @@
import jaGeojsonRaw from '@/assets/geojson/ja.geojson?raw'
import DevicePopup from './components/DevicePopup.vue'
import DronePopup from './components/DronePopup.vue'
+import dayjs from 'dayjs'
import {
createDroneTrackMaterial,
createRadialGradientMaterial,
@@ -117,6 +120,7 @@
let droneTrackTickHandler = null
let droneTrackStartTime = null
let droneTrackRuntime = []
+let mapReadyHandled = false
let favoritePulseRafId = null
let favoritePulseStartAt = 0
let favoritePulseElapsed = 0
@@ -968,7 +972,10 @@
const loadPartitions = async () => {
if (!viewer) return
try {
- const res = await fwAreaDivideListApi()
+ const res = await fwAreaDivideListApi({
+ isSetSceneManage: 1,
+ flyTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
+ })
await renderPartitions(res?.data?.data ?? [])
} catch (error) {
await renderPartitions([])
@@ -1129,7 +1136,9 @@
const loadCommandPosts = async () => {
try {
- const res = await fwDefenseSceneListApi()
+ const res = await fwDefenseSceneManageListApi({
+ time: dayjs().format('YYYY-MM-DD HH:mm:ss')
+ })
renderCommandPosts(res?.data?.data ?? [])
} catch (error) {
renderCommandPosts([])
@@ -1201,6 +1210,8 @@
}
const handleMapReady = ({ viewer: mapViewer, publicCesium: mapPublic }) => {
+ if (mapReadyHandled) return
+ mapReadyHandled = true
viewer = mapViewer
publicCesium = mapPublic
ensureCockpitPrimitiveLayer()
diff --git a/applications/drone-command/src/page/index/top/index.vue b/applications/drone-command/src/page/index/top/index.vue
index 814e02e..22d4a43 100644
--- a/applications/drone-command/src/page/index/top/index.vue
+++ b/applications/drone-command/src/page/index/top/index.vue
@@ -100,8 +100,8 @@
})
},
jumpMH () {
- const adminUrl = import.meta.env.VITE_APP_DASHBOARD_URL
- window.location.href = `${adminUrl}#/gatewayPage`
+ // const adminUrl = import.meta.env.VITE_APP_DASHBOARD_URL
+ // window.location.href = `${adminUrl}#/gatewayPage`
},
},
}
diff --git a/applications/drone-command/src/utils/cesium/createRouteLine.js b/applications/drone-command/src/utils/cesium/createRouteLine.js
deleted file mode 100644
index 6ec1c1f..0000000
--- a/applications/drone-command/src/utils/cesium/createRouteLine.js
+++ /dev/null
@@ -1,1361 +0,0 @@
-import * as Cesium from 'cesium'
-import { Decimal } from 'decimal.js'
-import _, { cloneDeep, throttle } from 'lodash'
-import { v4 as uuidv4 } from 'uuid'
-import { render } from 'vue'
-import { analysisPointLineKmz, handlePointListForKmz } from '@/views/newRoutePlan/Waypoint/pointWayLineUtils'
-import HistoryDronePopup from '@/components/DeviceJobDetails/HistoryDronePopup.vue'
-
-import {
- getNewPolygonData,
-} from '@/views/newRoutePlan/routeUtils'
-
-import { analyzeKmzFile, removeTextKey, XMLToJSON } from '@/utils/cesium/kmz'
-import { ArrowLineMaterialProperty, LineTrailMaterial, PolylineGlowMaterial } from '@/utils/cesium/Material'
-import CreateFrustum from '@/utils/cesium/frustum/CreateFrustum'
-import { ElMessage } from 'element-plus'
-
-import rwqfdImg from '@/assets/images/signMachineNest/rwqfd.png'
-import endingOnlineImg from '@/assets/images/aiNowFly/ending-online.png'
-import newEndPointImg from '@/assets/images/newEndPointicon.png'
-import endPointImg from '@/assets/images/EndPointicon.png'
-import aircraftGltf from '@/assets/gltf/aircraft.gltf'
-import newNumPoint from '@/assets/images/newStartPoint.png'
-
-import { getPolyLine } from '@/views/newRoutePlan/Waypoint/pointWayLineUtils'
-import store from '@/store/index'
-import newStartPoint from '@/assets/images/newStartPoint.png'
-import newStartPointMore from '@/assets/images/newStartPointMore.png'
-import {
- arrowLineMaterialProperty, arrowLineMaterialPropertyGray, arrowLineMaterialPropertyGrayT,
- arrowLineMaterialPropertyGreen,
- arrowLineMaterialPropertyOrange,
-} from '@/hooks/useMorePointLine/useMorePointLine'
-
-
-let runningTextColor = Cesium.Color.fromCssColorString('#1FFF69')
-let runningTextOffset = new Cesium.Cartesian2(0, -32)
-let pendingTextColor = Cesium.Color.fromCssColorString('#FFB81D')
-let pendingTextOffset = new Cesium.Cartesian2(0, 32)
-let MapPopUpBox = HistoryDronePopup
-
-function getZoomFactor (camerasInfo, type) {
- let zoom_factor
- const irLevel = camerasInfo?.ir_zoom_factor
- const zoomLevel = camerasInfo?.zoom_factor
- if (type === 'wide') {
- zoom_factor = 1
- } else if (type === 'ir') {
- zoom_factor = irLevel
- } else {
- zoom_factor = zoomLevel
- }
- zoom_factor = _.round(zoom_factor, 1)
-
- return zoom_factor
-}
-// 记录index
-let indexLog = 0
-// 颜色
-function getPendingExecutionLineColor (lineNumber) {
- // 颜色顺序: 蓝(灰), 更灰
- const colors = [arrowLineMaterialPropertyGray, arrowLineMaterialPropertyGrayT]
- // 计算颜色索引 (从0开始)
- const colorIndex = (lineNumber - 1) % 2
- // 返回对应颜色
- return colors[colorIndex]
-}
-
-function getLineColor (lineNumber) {
- // 颜色顺序: 蓝(0), 黄(1), 绿(2)
- const colors = [arrowLineMaterialProperty, arrowLineMaterialPropertyOrange, arrowLineMaterialPropertyGreen]
- // 计算颜色索引 (从0开始)
- const colorIndex = (lineNumber - 1) % 3
- // 返回对应颜色
- return colors[colorIndex]
-}
-
-function getBase64Image (imgUrl) {
- const img = new Image()
- const canvas = document.createElement('canvas')
- const ctx = canvas.getContext('2d')
-
- return new Promise(resolve => {
- img.onload = function () {
- canvas.width = img.width
- canvas.height = img.height
- ctx.drawImage(img, 0, 0)
- const base64 = canvas.toDataURL('image/png')
- resolve(base64)
- }
- img.src = imgUrl
- })
-}
-async function createCombinedSVG (index, billboardImageUrl) {
- const base64Image = await getBase64Image(billboardImageUrl)
- const svg = `
- <svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
- <image href="${base64Image}" x="10" y="10" width="80" height="80"/>
- <text x="50" y="54" font-size="26" fill="white" text-anchor="middle" dominant-baseline="middle">
- ${index + 1}
- </text>
- </svg>
- `
- return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`
-}
-
-function findHeightByCoordinates (data, targetLng, targetLat) {
- // 展平二维数组,然后查找第一个匹配的点
- const allPoints = data.flat()
- const point = allPoints.find(point =>
- point.longitude === targetLng && point.latitude === targetLat
- )
-
- return point ? point.height : null
-}
-
-function renderingMachineNest (options) {
- const { viewer, dockTransformPosition, dockPosition, data } = options
- viewer.entities.add({
- name: 'work-drone-route-point-dock',
- position: dockTransformPosition,
- billboard: {
- image: endingOnlineImg,
- width: 50,
- height: 50,
- disableDepthTestDistance: Number.POSITIVE_INFINITY,
- },
- customData: {
- data,
- },
- })
- viewer.entities.add({
- name: 'work-drone-route--point-polyline',
- position: dockTransformPosition,
- polyline: getPolyLine(viewer, { value: [dockPosition] }, 0),
- })
-}
-
-function renderingStartingPoint (options) {
- const { viewer, startPosition, data } = options
- viewer.entities.add({
- name: 'work-drone-route-point-start',
- position: startPosition,
- billboard: {
- image: rwqfdImg,
- width: 50,
- height: 50,
- disableDepthTestDistance: Number.POSITIVE_INFINITY,
- },
- customData: {
- data,
- },
- })
-}
-
-export default class CreateRouteLine {
- /**
- *
- * @param {*} options 参数
- */
- constructor(options) {
- this.type = options?.type || 'single'
- this.isShowVideoPlan = options?.isShowVideoPlan || false
-
- this.viewer = null
- this.currentWaypointIndex = null
- this.droneSpeedArr = []
-
- this.multiNestData = []
-
- this.detailsMapPopupData = []
-
- this.labelBoxRenderHandler = this.labelBoxRender.bind(this)
- this.removeLabelHandler = this.removeLabel.bind(this)
- }
-
- /**
- * 初始化viewer
- * @param {*} viewer
- */
- initCreateRoute (viewer) {
- this.viewer = viewer
- }
-
- /**
- * 设置当前所在点位
- * @param {*} data flighttaskProgressInfo 信息
- */
- setCurrentWaypointIndex (data) {
- const output = data?.output
- const sn = data?.sn
-
- if (output?.ext) {
- const currentWaypointIndex = output?.ext['current_waypoint_index']
-
- this.updataMultiNestData(sn, {
- currentWaypointIndex,
- })
- }
- }
-
- /**
- * 请求航线文件数据并绘制解析处理
- * @param {*} data 包含文件url等信息
- * @param {*} dockPosition 无人机位置
- * @returns
- */
- async parsingFiles (data, dockPosition = null, isShowDock = false, isShowPointBillboard = true) {
- console.log(data,'888')
- const { url, wayline_type, device_sn } = data
- indexLog = 0
- if (!url)
- return dockPosition != null
- ? {
- dronePosition: [{ lng: dockPosition.longitude, lat: dockPosition.latitude, alt: dockPosition.height }],
- }
- : {}
- this.updataMultiNestData(device_sn)
-
- const res = await analyzeKmzFile(`${url}?_t=${new Date().getTime()}`)
-
- const templateXML = await res.fileInfoObj['wpmz/template.kml']
- const waylinesXML = await res.fileInfoObj['wpmz/waylines.wpml']
-
- const templateXmlJson = XMLToJSON(templateXML)?.['Document']
- const waylinesXmlJson = XMLToJSON(waylinesXML)?.['Document']
-
- const templateXMLObj = removeTextKey(templateXmlJson.Folder)
- const { takeOffRefPoint } = removeTextKey(templateXmlJson.missionConfig)
-
- const [latitude, longitude, height] = takeOffRefPoint.split(',').map(item => _.round(item, 6))
-
- const startPoint = { latitude, longitude, height: Number.isNaN(height) ? 0 : height }
-
- const { templateType } = templateXMLObj
-
- const missionConfig = removeTextKey(waylinesXmlJson.missionConfig)
- const waylinesXMLObj = removeTextKey(waylinesXmlJson.Folder)
-
- if (templateType === 'mapping3d') {
- if (!waylinesXMLObj[0].Placemark.length) return ElMessage.error('没有航线点位')
- } else {
- if (!waylinesXMLObj.Placemark.length) return ElMessage.error('没有航线点位')
- }
-
- if ([2, 4, 5, 7, 10].includes(Number(wayline_type))) {
- if (templateType === 'mapping3d') {
- let morePolygonData = waylinesXMLObj.map(i => ({
- data: i.Placemark,
- }))
-
- let newPolygonData = getNewPolygonData(morePolygonData)
-
- let newPolygonDisposePositions = newPolygonData.map(i =>
- Cesium.Cartesian3.fromDegrees(Number(i[0]), Number(i[1]))
- )
-
- this.viewer.entities.add({
- name: 'work-drone-route-planar-outer-polygon',
-
- polygon: {
- hierarchy: new Cesium.CallbackProperty(() => {
- return new Cesium.PolygonHierarchy(newPolygonDisposePositions)
- }, false),
- material: Cesium.Color.fromBytes(255, 228, 22, 44),
- outline: false,
- outlineWidth: 2,
- heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
- },
- })
-
- return this.drawPlanarRoute(
- waylinesXMLObj[0],
- missionConfig,
- templateXmlJson,
- dockPosition,
- data,
- startPoint,
- isShowDock,
- isShowPointBillboard,
- wayline_type
- )
- }
-
- return this.drawPlanarRoute(
- waylinesXMLObj,
- missionConfig,
- templateXmlJson,
- dockPosition,
- data,
- startPoint,
- isShowDock,
- isShowPointBillboard,
- wayline_type
- )
- } else {
- return this.drawPointRoute(
- waylinesXMLObj,
- missionConfig,
- dockPosition,
- data,
- startPoint,
- isShowDock,
- isShowPointBillboard,
- wayline_type
- )
- }
- }
-
- // 拆分航线
- async splitWayLine (data, dronePosition = null, isShowDock = false, isShowPointBillboard = true) {
- const {
- pointPlacemark,
- polygonList,
- pointList,
- templateType,
- startPoint,
- execute_height_mode,
- auto_flight_speed,
- take_off_security_height,
- buffer_distance_meters,
- missionConfig,
- waylinesXMLObjR,
- } = await analysisPointLineKmz(`${data.domain_url}${data.object_key}`)
- const { positionArray, filePositions } = this.disposeData(
- waylinesXMLObjR,
- missionConfig,
- null,
- data,
- startPoint,
- 0
- )
- let bigPointList = handlePointListForKmz({
- placemark: pointPlacemark,
- startPoint: startPoint,
- execute_height_mode: execute_height_mode,
- auto_flight_speed: auto_flight_speed,
- })
- // 路径线
- let entityConfig
- bigPointList.shift()
- let flyPositions = bigPointList.map(i => [Number(i.longitude), Number(i.latitude), Number(i.height)])
- // 渲染多个点
- let firstTopPosition = null
- let resultPosotions = []
- await Promise.all(data.wayline_file_list.map(async (item, index) => {
- // 小航线渲染线
- const kmzUrl = import.meta.env.VITE_APP_AIRLINE_URL + item.object_key
- const { pointList } = await analysisPointLineKmz(kmzUrl)
- resultPosotions.push(pointList)
- })).then(res => {
- // 目的是区分大航线点属于哪条小航线
- bigPointList.forEach(bigObj => {
- let foundIndex = -1
- // 遍历 resultPositions 查找匹配项
- resultPosotions.some((subArray, subIndex) => {
- const found = subArray.some(obj =>
- obj.latitude === bigObj.latitude &&
- obj.longitude === bigObj.longitude
- )
- if (found) {
- foundIndex = subIndex
- return true // 找到就停止搜索
- }
- return false
- })
- // 如果找到了,给 bigPointList 的对象添加标记
- if (foundIndex !== -1) {
- bigObj.log = foundIndex // 或 bigObj.index = foundIndex
- }
- })
- console.log(bigPointList, '查看结果值') // 现在 bigPointList 会有 log 属性
- bigPointList.map(async (i, index) => {
- firstTopPosition = i
- const position = Cesium.Cartesian3.fromDegrees(Number(i.longitude), Number(i.latitude), Number(findHeightByCoordinates(resultPosotions, i.longitude, i.latitude)))
- const combinedImage = await createCombinedSVG(index, i.log % 2 ? newStartPoint : newStartPointMore)
-
- entityConfig = {
- name: 'work-drone-route-point-split',
- position: position,
- billboard: {
- disableDepthTestDistance: Number.POSITIVE_INFINITY,
- image: combinedImage,
- width: 50,
- height: 50,
- verticalOrigin: Cesium.VerticalOrigin.CENTER,
- horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
- }
- }
-
- // 只在第一个点添加 label
- if (index === 0) {
- entityConfig.label = {
- text: data.task_name || '',
- font: 'bold 16px sans-serif',
- fillColor: data.status === 2 ? runningTextColor : pendingTextColor,
- pixelOffset: data.status === 2 ? runningTextOffset : pendingTextOffset,
- style: Cesium.LabelStyle.FILL_AND_OUTLINE,
- outlineWidth: 2,
- outlineColor: Cesium.Color.BLACK,
- disableDepthTestDistance: Number.POSITIVE_INFINITY,
- }
- }
-
- this.viewer.entities.add(entityConfig)
- })
- })
-
- // 渲染航线
- data.wayline_file_list.map(async (item, index) => {
- // 小航线渲染线
- const kmzUrl = import.meta.env.VITE_APP_AIRLINE_URL + item.object_key
- const { pointList } = await analysisPointLineKmz(kmzUrl)
- // if (curRouteLineData.value.droneData && splitLines.length === 1) {
- // // 把pointList的第一个点替换为无人机位置
- // pointList[0] = curRouteLineData.value.droneData[0]
- // }
- if (take_off_security_height + pointList[0].height > firstTopPosition.height) {
- // 安全高度+机巢高度 > 第一个点高度
- const positionTest = {
- longitude: pointList[0].longitude,
- latitude: pointList[0].latitude,
- height: take_off_security_height + pointList[0].height,
- }
-
- const positionTest1 = {
- longitude: pointList[1].longitude,
- latitude: pointList[1].latitude,
- height: take_off_security_height + pointList[0].height,
- }
- pointList.splice(1, 0, positionTest)
- // 往数组中插入元素
- pointList.splice(2, 0, positionTest1)
- // console.log(pointList, 'pointList')
- } else if (take_off_security_height + pointList[0].height < firstTopPosition.height) {
- const positionTest = {
- longitude: pointList[0].longitude,
- latitude: pointList[0].latitude,
- height: firstTopPosition.height,
- }
- pointList.splice(1, 0, positionTest)
- }
- const routePositions = pointList.map(i =>
- Cesium.Cartesian3.fromDegrees(Number(i.longitude), Number(i.latitude), Number(i.height))
- )
-
- this.viewer.entities.add({
- name: 'work-drone-route-line-split',
- polyline: {
- width: 4,
- positions: routePositions,
- material: data.status === 1 ? arrowLineMaterialPropertyGray : getLineColor(index + 1),
- clampToGround: false,
- },
- })
- })
- // 落点线
- ; (bigPointList || [])?.forEach((item, index) => {
- const topPosition = Cesium.Cartesian3.fromDegrees(
- Number(item.longitude),
- Number(item.latitude),
- Number(item.height)
- )
- let setting = {
- name: 'work-drone-route--planar-polyline-split',
- position: topPosition,
- polyline: getPolyLine(this.viewer, { value: bigPointList }, index),
- }
- this.viewer.entities.add(setting)
- })
- return {
- entity: entityConfig,
- routeLinePositions: positionArray,
- filePositions:
- dronePosition != null
- ? [{ lng: dronePosition.longitude, lat: dronePosition.latitude, alt: dronePosition.height }, ...filePositions]
- : filePositions,
- }
- }
-
- /**
- * 绘制面状航线
- * @param {*} lineObj 航线文件中的
- * @param {*} missionConfig 航线文件中的
- * @param {*} templateXmlJson 航线文件中的
- * @param {*} dockPosition 无人机位置
- * @param {*} data
- * @param {*} startPoint
- * @returns
- */
- async drawPlanarRoute (
- lineObj,
- missionConfig,
- templateXmlJson,
- dockPosition,
- data,
- startPoint,
- isShowDock,
- isShowPointBillboard,
- wayline_type
- ) {
- const { positionArray, filePositions } = this.disposeData(
- lineObj,
- missionConfig,
- dockPosition,
- data,
- startPoint,
- wayline_type
- )
- const { device_sn } = data
-
- const dockTransformPosition = Cesium.Cartesian3.fromDegrees(
- Number(dockPosition.longitude),
- Number(dockPosition.latitude),
- Number(dockPosition.height)
- )
-
- let coordArr = null
-
- const placemark = templateXmlJson.Folder?.Placemark
- // 取出点位
- const coordinates = placemark.Polygon?.outerBoundaryIs.LinearRing.coordinates?.['#text']?.split('\n') || []
- // 数组转换
- coordArr = coordinates.map(coordinate =>
- coordinate
- .replace(/\s+/g, '')
- .split(',')
- .map(v => Number(v))
- )
-
- // 获取当前经纬度绝对高度
- let newCoordArr = coordArr.map(item => [item[0], item[1], 0])
- let planarRouteEntity = this.viewer.entities.add({
- name: 'work-drone-route-planar-polyline',
- polyline: {
- width: 3,
- positions: positionArray,
- material: Cesium.Color.CHARTREUSE,
- zIndex: 1,
- },
-
- customData: {
- data,
- },
- })
-
- let droppointPositions = (filePositions || [])?.map(i => ({
- longitude: Number(i.lng),
- latitude: Number(i.lat),
- height: Number(i.alt),
- }))
-
- // 落点线
- ; (droppointPositions || [])?.forEach((item, index) => {
- const topPosition = Cesium.Cartesian3.fromDegrees(
- Number(item.longitude),
- Number(item.latitude),
- Number(item.height)
- )
- let setting = {
- name: 'work-drone-route--planar-polyline',
- position: topPosition,
- polyline: getPolyLine(this.viewer, { value: droppointPositions }, index),
- }
- this.viewer.entities.add(setting)
- })
-
- // 面状航线第一个点突出展示
- isShowPointBillboard && this.startingIncreasePoint(positionArray)
-
- // 面状判断当前点位数量
- const cloneCoordArr = [...newCoordArr]
-
- if (cloneCoordArr.length >= 3) {
- cloneCoordArr.push(newCoordArr[0])
- }
-
- // 绘制面状边框线--------------------
- this.viewer.entities.add({
- name: 'work-drone-route-planar-border',
-
- polyline: {
- positions: Cesium.Cartesian3.fromDegreesArrayHeights(cloneCoordArr.flat()),
- width: 4,
- material: new Cesium.PolylineOutlineMaterialProperty({
- color: new Cesium.Color.fromBytes(74, 138, 233),
- outlineWidth: 2,
- outlineColor: Cesium.Color.WHITE,
- }),
- zIndex: -1,
- clampToGround: true,
- },
- })
-
- // 绘制面状地块--------------------
- this.viewer.entities.add({
- name: 'work-drone-route-planar-polygon',
- polygon: {
- hierarchy: Cesium.Cartesian3.fromDegreesArrayHeights(newCoordArr.flat()),
- material: new Cesium.Color.fromBytes(75, 159, 221, 100),
- outline: true,
- outlineColor: new Cesium.Color.fromBytes(35, 85, 216, 255),
- zIndex: -1,
- },
- })
-
- if (this.type === 'clusterScheduling') {
- this.viewer.entities.add({
- name: 'work-drone-route-point-start',
- position: Cesium.Cartesian3.fromDegrees(filePositions[0].lng, filePositions[0].lat, filePositions[0].alt),
-
- billboard: {
- image: rwqfdImg,
- outlineWidth: 0,
- width: 50,
- height: 50,
- disableDepthTestDistance: Number.POSITIVE_INFINITY,
- },
-
- label: {
- text: data.job_name || '',
- font: 'bold 16px sans-serif',
- fillColor: Cesium.Color.WHITE,
- pixelOffset: new Cesium.Cartesian2(0, -50),
- style: Cesium.LabelStyle.FILL_AND_OUTLINE,
- outlineWidth: 2,
- outlineColor: Cesium.Color.BLACK,
- disableDepthTestDistance: Number.POSITIVE_INFINITY,
- },
- })
- } else {
- // 显示机巢位置
- isShowDock && renderingMachineNest({
- viewer: this.viewer,
- dockTransformPosition,
- dockPosition,
- data
- })
- // 起点
- isShowPointBillboard && renderingStartingPoint({
- viewer: this.viewer,
- startPosition: Cesium.Cartesian3.fromDegrees(
- Number(filePositions[0].lng),
- Number(filePositions[0].lat),
- Number(filePositions[0].alt)
- ),
- data
- })
- }
-
- return {
- entity: planarRouteEntity,
- routeLinePositions: positionArray,
- filePositions:
- dockPosition != null
- ? [{ lng: dockPosition.longitude, lat: dockPosition.latitude, alt: dockPosition.height }, ...filePositions]
- : filePositions,
- }
- }
-
- planarBillboard (color) {
- const billboard = document.createElement('canvas')
- const ctx = billboard.getContext('2d')
- const radius = 4
- ctx.beginPath()
- ctx.arc(radius, radius, radius, 0, 2 * Math.PI, false)
- ctx.fillStyle = color // 设置填充颜色
- ctx.fill()
- return billboard
- }
-
- startingIncreasePoint (wayData) {
- let positions = wayData[3]
-
- let setting = {
- name: 'work-drone-route-planar-start',
- position: positions,
- billboard: {
- image: this.planarBillboard('#2D8CF0'),
- pixelOffset: new Cesium.Cartesian2(146, 72),
- zIndex: 2,
- },
- }
-
- this.viewer.entities.add(setting)
- }
-
- /**
- * 绘制点航线
- * @param {*} lineObj 航线文件中的
- * @param {*} missionConfig 航线文件中的
- * @param {*} dockPosition 无人机位置
- * @param {*} data 任务信息,航线地址等
- * @param {*} startPoint
- * @param {*} isShowDock 是否显示机巢位置
- * @returns
- */
- async drawPointRoute (
- lineObj,
- missionConfig,
- dockPosition,
- data,
- startPoint,
- isShowDock,
- isShowPointBillboard,
- wayline_type
- ) {
- const { positionArray, filePositions } = this.disposeData(
- lineObj,
- missionConfig,
- dockPosition,
- data,
- startPoint,
- wayline_type
- )
-
- const dockTransformPosition = Cesium.Cartesian3.fromDegrees(
- Number(dockPosition.longitude),
- Number(dockPosition.latitude),
- Number(dockPosition.height)
- )
- // 路径线
- let polyline
-
- indexLog = indexLog + 1
-
- if (this.type === 'clusterScheduling') {
- filePositions.forEach((item, index) => {
- let position = Cesium.Cartesian3.fromDegrees(item.lng, item.lat, item.alt)
- if (index === 0) {
- this.viewer.entities.add({
- name: 'work-drone-route-point-start',
- position,
- billboard: {
- image: rwqfdImg,
- outlineWidth: 0,
- width: 50,
- height: 50,
- disableDepthTestDistance: Number.POSITIVE_INFINITY,
- },
- label: {
- text: data.job_name || '',
- font: 'bold 16px sans-serif',
- fillColor: data.type === 2 ? runningTextColor : pendingTextColor,
- pixelOffset: data.type === 2 ? runningTextOffset : pendingTextOffset,
- style: Cesium.LabelStyle.FILL_AND_OUTLINE,
- outlineWidth: 2,
- outlineColor: Cesium.Color.BLACK,
- disableDepthTestDistance: Number.POSITIVE_INFINITY,
- },
- })
- } else if (index === filePositions.length - 1) {
- this.viewer.entities.add({
- name: 'work-drone-route-point-end',
- position,
- billboard: {
- image: newEndPointImg,
- outlineWidth: 0,
- width: 40,
- height: 40,
- scale: 1.0,
- verticalOrigin: Cesium.VerticalOrigin.TOP, // 添加这行确保图标正确显示
- pixelOffset: new Cesium.Cartesian2(0, -20), // 根据需要调整偏移量
- },
- })
- } else {
- this.viewer.entities.add({
- name: 'work-drone-route-point-approach',
- position,
- billboard: {
- image: newNumPoint,
- width: 50,
- height: 50,
- },
-
- label: {
- text: `${index}`,
- font: 'bold 14px serif',
- fillColor: Cesium.Color.WHITE,
- pixelOffset: new Cesium.Cartesian2(1, 0), // 根据需要调整偏移量
- eyeOffset: new Cesium.Cartesian3(0, 0, -10), // 使标签在点的上方
- },
-
- offset: new Cesium.Cartesian2(10, 30),
- })
- }
- })
- polyline = this.viewer.entities.add({
- name: 'work-drone-route-point-polyline',
- polyline: {
- width: 4,
- positions: positionArray,
- material: data.type === 2 ? getLineColor(indexLog) : arrowLineMaterialPropertyGray,
- clampToGround: false,
- },
- })
- } else {
- // 显示机巢位置
- isShowDock && renderingMachineNest({
- viewer: this.viewer,
- dockTransformPosition,
- dockPosition
- })
- // 起点
- isShowPointBillboard && renderingStartingPoint({
- viewer: this.viewer,
- startPosition: Cesium.Cartesian3.fromDegrees(
- Number(filePositions[0].lng),
- Number(filePositions[0].lat),
- Number(filePositions[0].alt)
- ),
- data
- })
- // 终点
- isShowPointBillboard &&
- this.viewer.entities.add({
- name: 'work-drone-route-point-end',
- position: positionArray[positionArray.length - 1],
- billboard: {
- image: new Cesium.ConstantProperty(endPointImg),
- width: 30,
- height: 30,
- verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 底部对齐
- },
- })
- console.log(data.status, '失败')
- polyline = this.viewer.entities.add({
- name: 'work-drone-route-point-polyline',
- polyline: {
- width: 4,
- positions: positionArray,
- material: data.status === 5 ? arrowLineMaterialPropertyGray : getLineColor(indexLog),
- clampToGround: false,
- },
- customData: {
- data,
- },
- })
- }
-
- let droppointPositions = (filePositions || [])?.map(i => ({
- longitude: Number(i.lng),
- latitude: Number(i.lat),
- height: Number(i.alt),
- }))
-
- // 落点线
- ; (droppointPositions || [])?.forEach((item, index) => {
- const topPosition = Cesium.Cartesian3.fromDegrees(
- Number(item.longitude),
- Number(item.latitude),
- Number(item.height)
- )
-
- let setting = {
- name: 'work-drone-route--point-polyline',
- position: topPosition,
- polyline: getPolyLine(this.viewer, { value: droppointPositions }, index),
- }
- this.viewer.entities.add(setting)
- })
-
- return {
- entity: polyline,
- routeLinePositions: positionArray,
- filePositions:
- dockPosition !== null
- ? [{ lng: dockPosition.longitude, lat: dockPosition.latitude, alt: dockPosition.height }, ...filePositions]
- : filePositions,
- }
- }
-
- /**
- * 通用处理数据
- * @param {*} lineObj
- * @param {*} missionConfig
- * @param {*} dronePosition
- * @param {*} startPoint
- * @returns {} {positionArray: 带拼接点位置, filePositions:光航线点位置}
- */
- disposeData (lineObj, missionConfig, dronePosition, data, startPoint, wayline_type) {
- const { device_sn } = data
- const executeHeightMode = lineObj.executeHeightMode === 'WGS84'
-
- let filePositions = lineObj.Placemark.map(item => {
- const [lng, lat] = item.Point.coordinates.split(',')
- const height = item.executeHeight
-
- return {
- lng: Number(lng),
- lat: Number(lat),
- alt: Number(height),
- }
- })
-
- let safetyHeight
-
- if (executeHeightMode) {
- let startHeight = new Decimal(missionConfig.takeOffSecurityHeight).plus(dronePosition?.height || 0).toNumber()
-
- safetyHeight = startHeight < filePositions[0].alt ? filePositions[0].alt : startHeight
- } else {
- safetyHeight =
- missionConfig.takeOffSecurityHeight < filePositions[0].alt
- ? filePositions[0].alt
- : missionConfig.takeOffSecurityHeight
- }
-
- let safetyPositions = []
-
- if (dronePosition != null) {
- safetyPositions = [
- {
- lng: Number(dronePosition.longitude),
- lat: Number(dronePosition.latitude),
- alt: Number(dronePosition.height),
- },
-
- {
- lng: Number(dronePosition.longitude),
- lat: Number(dronePosition.latitude),
- alt: Number(safetyHeight),
- },
-
- {
- lng: filePositions[0].lng,
- lat: filePositions[0].lat,
- alt: Number(safetyHeight),
- },
- ]
- }
-
- let pointAll = [...safetyPositions, ...filePositions]
-
- const currentRoutePositions = pointAll.map((item, index) => {
- let height = executeHeightMode ? item.alt : item.alt + (dronePosition != null ? dronePosition.height : 0)
-
- if (index == 0) height = item.alt
-
- return Cesium.Cartesian3.fromDegrees(item.lng, item.lat, height)
- })
-
- this.updataMultiNestData(device_sn, {
- currentRoutePositions,
- })
-
- if ([0].includes(wayline_type)) {
- let startHeight = executeHeightMode
- ? filePositions[0].alt
- : filePositions[0].alt + (dronePosition != null ? dronePosition.height : 0)
- let endHeight = executeHeightMode
- ? filePositions[1].alt
- : filePositions[1].alt + (dronePosition != null ? dronePosition.height : 0)
-
- this.updataMultiNestData(device_sn, {
- showPopupPosition: Cesium.Cartesian3.midpoint(
- Cesium.Cartesian3.fromDegrees(filePositions[0].lng, filePositions[0].lat, startHeight),
- Cesium.Cartesian3.fromDegrees(filePositions[1].lng, filePositions[1].lat, endHeight),
- new Cesium.Cartesian3()
- ),
- })
- } else {
- let startHeight = executeHeightMode
- ? safetyPositions[1].alt
- : safetyPositions[1].alt + (dronePosition != null ? dronePosition.height : 0)
- let endHeight = executeHeightMode
- ? safetyPositions[2].alt
- : safetyPositions[2].alt + (dronePosition != null ? dronePosition.height : 0)
-
- this.updataMultiNestData(device_sn, {
- showPopupPosition: Cesium.Cartesian3.midpoint(
- Cesium.Cartesian3.fromDegrees(safetyPositions[1].lng, safetyPositions[1].lat, startHeight),
- Cesium.Cartesian3.fromDegrees(safetyPositions[2].lng, safetyPositions[2].lat, endHeight),
- new Cesium.Cartesian3()
- ),
- })
- }
-
- return {
- positionArray: currentRoutePositions,
- filePositions: filePositions.map(item => {
- let height = executeHeightMode ? item.alt : item.alt + (dronePosition != null ? dronePosition.height : 0)
-
- return {
- ...item,
- alt: Number(height),
- }
- }),
- }
- }
-
- /**
- * 删除entity
- */
- removeAllRouteEntity () {
- const entities = this.viewer?.entities.values.filter(i => {
- return i?.name && i?.name.includes('work-drone-route-')
- })
-
- Array.isArray(entities) &&
- entities.forEach(item => {
- this.viewer?.entities.remove(item)
- })
- }
-
- // 当前无人机视椎体相关
- setCreateFrustum (host, zoomFactor = {}, cameraParams = {}) {
- if (!host) return
-
- const parent_sn = host?.parent_sn
-
- if (parent_sn) {
- const curDroneData = this.getCurDroneData(parent_sn)
-
- curDroneData.viewInfoFrustum?.destroyed()
-
- const attitude_head = 180 + host?.attitude_head
- const gimbal_pitch = 90 - Number(host?.payloads[0]?.gimbal_pitch) || 0
-
- let fov = 60
-
- if (
- zoomFactor?.value?.set === true &&
- cameraParams?.value?.set === true &&
- curDroneData?.camera_type &&
- ['wide', 'ir', 'zoom'].includes(curDroneData?.camera_type)
- ) {
- if (curDroneData.camera_type === 'wide') {
- fov = 65
- } else {
- const cameraMultiplier = getZoomFactor(host?.cameras?.[0], curDroneData.camera_type)
- switch (cameraMultiplier) {
- case 2: fov = 30; break
- case 3: fov = 19; break
- case 4: fov = 15; break
- case 5: fov = 13; break
- case 6: fov = 11; break
- case 7: fov = 9; break
- case 8: fov = 8.34; break
- case 9: fov = 7.68; break
- case 10: fov = 6.9; break
- case 14: fov = 5; break
- default: fov = 55.946 * Math.exp(-0.4504 * cameraMultiplier) + 6.323
- }
- }
- }
-
- this.updataMultiNestData(parent_sn, {
- viewInfoFrustum: new CreateFrustum(this.viewer, {
- position: {
- longitude: host?.longitude,
- latitude: host?.latitude,
- altitude: host?.height,
- },
- width: store.state.common.liveResolution.width || 960,
- height: store.state.common.liveResolution.height || 720,
- fov: fov,
- near: 0.01,
- far: 250,
- roll: gimbal_pitch,
- pitch: 0,
- heading: attitude_head,
-
- isShowVideoPlan: this.isShowVideoPlan,
- }),
- })
- }
- }
-
- setLiveStatusWs (data) {
- const { device_sn, live_status } = data
-
- if (device_sn && live_status) {
- this.updataMultiNestData(device_sn, {
- camera_type: live_status?.[0].video_type || 'wide',
- })
- }
- }
-
- removeCreateFrustum (host) {
- const parent_sn = host?.parent_sn
-
- if (parent_sn) {
- const curDroneData = this.getCurDroneData(parent_sn)
-
- curDroneData.viewInfoFrustum?.clear()
- }
- }
-
- // 设置当前无人机视椎体信息面板
- setFrustumInfoDetails (data) {
- const { dock_sn, distance_to_airport, distance_to_next_point, estimated_time_to_next_point, plane_mileage } = data
-
- if (dock_sn) {
- const aircraftEntity = this.viewer?.entities.values.find(i => {
- return i?.name && i?.name.includes(`aircraft-glf-${dock_sn}`)
- })
-
- if (aircraftEntity) {
- // 预计到达下一个航点时间
- let arrivalTime = estimated_time_to_next_point
-
- if (arrivalTime > 60) {
- const minute = Math.floor(arrivalTime / 60)
- const second = Math.round(arrivalTime % 60)
- arrivalTime = `${minute}m${second}s`
- } else {
- arrivalTime = Math.round(arrivalTime) + 's'
- }
-
- aircraftEntity.label = new Cesium.LabelGraphics({
- text: `离机场平面距离:${distance_to_airport}m\n离下个航点平面距离:${Math.round(
- distance_to_next_point
- )}m\n到达下个航点剩余时间:${arrivalTime}\n航线总平面里程:${Math.round(plane_mileage)}m`,
- font: '13px monospace',
- showBackground: true,
- horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
- verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
- disableDepthTestDistance: Number.POSITIVE_INFINITY,
- pixelOffset: new Cesium.Cartesian2(0, -40),
- show: true,
- })
-
- return
- }
- }
- }
-
- removeFrustumInfoDetails (host) {
- const parent_sn = host?.parent_sn
-
- if (parent_sn) {
- const aircraftEntity = this.viewer?.entities.values.find(i => {
- return i?.name && i?.name.includes(`aircraft-glf-${parent_sn}`)
- })
-
- if (aircraftEntity) {
- aircraftEntity.label.show = false
- }
- }
- }
-
- // 设置无人机模型
- setAircraftGltf (host) {
- const parent_sn = host?.parent_sn
- const attitude_head = Number(host?.payloads?.[0]?.gimbal_yaw) - 90 || 0
-
- if (parent_sn) {
- const aircraftEntity = this.viewer?.entities.values.find(i => {
- return i?.name && i?.name.includes(`aircraft-glf-${parent_sn}`)
- })
-
- const position = Cesium.Cartesian3.fromDegrees(host?.longitude, host?.latitude, host?.height)
- // ✅ 新增:Heading / Pitch / Roll(单位:度 -> 弧度)
- const heading = Cesium.Math.toRadians(attitude_head) // 航向角
- const pitch = Cesium.Math.toRadians(0) // 俯仰角
- const roll = Cesium.Math.toRadians(0) // 横滚角
-
- // ✅ 新增:构造 HeadingPitchRoll 和 四元数
- const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll)
- const orientation = Cesium.Transforms.headingPitchRollQuaternion(position, hpr)
-
- if (aircraftEntity) {
- aircraftEntity.position = new Cesium.ConstantPositionProperty(position)
- aircraftEntity.orientation = new Cesium.ConstantProperty(orientation)
-
- return
- }
-
- if (parent_sn) {
- this.viewer?.entities.add({
- name: `aircraft-glf-${parent_sn}`,
- position,
- orientation,
- label: {},
- model: {
- uri: aircraftGltf, // 或 .glb
- scale: 64, // 缩放比例
- minimumPixelSize: 64, // 最小像素尺寸(保证模型远处可见)
- maximumScale: 64, // 最大缩放(可选)
- },
- })
- }
- }
- }
-
- // 移除无人机模型
- removeAircraftGltf (host) {
- const parent_sn = host?.parent_sn
-
- if (parent_sn) {
- const aircraftEntity = this.viewer?.entities.values.find(i => {
- return i?.name && i?.name.includes(`aircraft-glf-${parent_sn}`)
- })
-
- if (aircraftEntity) {
- this.viewer?.entities.remove(aircraftEntity)
- }
- }
- }
-
- // 移除所有无人机视椎体
- removeViewInfoFrustum () {
- this.multiNestData.forEach(item => {
- if ('viewInfoFrustum' in item && item.viewInfoFrustum) {
- item.viewInfoFrustum?.clear()
-
- delete item.viewInfoFrustum
- }
- })
- }
-
- mapEntityRemove (host) {
- const parent_sn = host?.parent_sn
-
- if (parent_sn) {
- const curDroneData = this.getCurDroneData(parent_sn)
-
- curDroneData.viewInfoFrustum?.clear()
-
- delete curDroneData.viewInfoFrustum
-
- const entities = this.viewer?.entities.values.filter(i => {
- return i?.name && i?.name.includes(`aircraft-glf-${parent_sn}`)
- })
-
- Array.isArray(entities) &&
- entities.forEach(item => {
- this.viewer?.entities.remove(item)
- })
- }
- }
-
- mapEntityRemoveAll () {
- const entities = this.viewer?.entities.values.filter(i => {
- return i?.name && i?.name.includes(`aircraft-glf-`)
- })
-
- Array.isArray(entities) &&
- entities.forEach(item => {
- this.viewer?.entities.remove(item)
- })
-
- this.multiNestData.length > 0 &&
- this.multiNestData.forEach(item => {
- item.viewInfoFrustum?.clear()
- })
- }
-
- /**
- * 更新多机场数据
- * @param {*} device_sn 机巢SN
- * @param {*} data 机巢对应需绑定数据
- */
- updataMultiNestData (device_sn, data) {
- const isHave = this.multiNestData.find(item => item.device_sn === device_sn)
- const isHaveInd = this.multiNestData.findIndex(item => item.device_sn === device_sn)
-
- if (isHave) {
- this.multiNestData[isHaveInd] = {
- ...this.multiNestData[isHaveInd],
-
- ...data,
- }
- } else {
- this.multiNestData.push({
- device_sn,
- ...data,
- })
- }
- }
-
- /**
- * 根据机巢sn获取当前机巢对应的相关信息
- * @param {*} device_sn
- */
- getCurDroneData (device_sn) {
- const isHave = this.multiNestData.find(item => item.device_sn === device_sn)
-
- if (isHave) return this.multiNestData.find(item => item.device_sn === device_sn)
-
- return false
- }
-
- showDetailMapPopup (data) {
- const that = this
- this.detailsMapPopupData = data.map(item => {
- return {
- ...item,
- show: true,
- uuid: uuidv4(),
- }
- })
- this.viewer.scene.postRender.addEventListener(that.labelBoxRenderHandler)
- }
-
- showSingleDetailMapPopup (data) {
- this.detailsMapPopupData.forEach(
- i => i?.job_id === data?.job_id && i?.device_sn === data?.device_sn && (i.show = true)
- )
- }
-
- getLabelDom (data) {
- const that = this
- const vNode = h(MapPopUpBox, { data, removeLabel: that.removeLabelHandler })
- const tooltipContainer = document.createElement('div')
-
- tooltipContainer.id = data.uuid
- tooltipContainer.style.position = 'absolute'
- tooltipContainer.style.transform = 'translate(-50%,10%)'
- tooltipContainer.style.pointerEvents = 'none'
- document.querySelector('.content-map-popups').append(tooltipContainer)
- render(vNode, tooltipContainer)
- return tooltipContainer
- }
-
- labelBoxRender () {
- this.detailsMapPopupData
- .filter(i => i.show === true)
- .forEach(item => {
- const { showPopupPosition } = this.getCurDroneData(item.device_sn)
-
- let dom = document.querySelector(`[id="${item.uuid}"]`)
-
- if (!dom) {
- dom = this.getLabelDom(item)
- }
-
- const screenPosition = this.viewer?.scene.cartesianToCanvasCoordinates(showPopupPosition)
- if (screenPosition) {
- dom.style.left = `${screenPosition.x}px`
- dom.style.top = `${screenPosition.y}px`
- dom.style.display = 'block'
- }
- })
- }
-
- // 移除弹框标签
- removeLabel (data) {
- const that = this
-
- that.removeDom(data)
- }
-
- removeDom (data) {
- const that = this
-
- const dom = document.querySelector(`[id="${data.uuid}"]`)
-
- if (dom && dom.parentNode) {
- that.detailsMapPopupData.forEach(i => i.uuid === data.uuid && (i.show = false))
-
- dom.parentNode.removeChild(dom)
- }
- }
-
- destroyedCustomDom () {
- const that = this
- that.viewer?.scene.postRender.removeEventListener(that.labelBoxRenderHandler)
- }
-}
diff --git a/applications/drone-command/src/views/api.txt b/applications/drone-command/src/views/api.txt
index 5480053..348cc4e 100644
--- a/applications/drone-command/src/views/api.txt
+++ b/applications/drone-command/src/views/api.txt
@@ -1,9 +1,9 @@
-## 列表-不带分页
+## 列表
-**接口地址**:`/area/fwAreaDivide/list`
+**接口地址**:`/area/fwDefenseSceneManage/list`
**请求方式**:`GET`
@@ -15,7 +15,7 @@
**响应数据类型**:`*/*`
-**接口描述**:<p>传入filterSelected或sceneId或areaTypeKeys或isSetSceneManage或flyTime</p>
+**接口描述**:<p>传入time</p>
@@ -27,11 +27,7 @@
| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | schema |
| -------- | -------- | ----- | -------- | -------- | ------ |
-|areaTypeKeys|区域类型key集合(逗号分隔)|query|false|string||
-|filterSelected|是否过滤已被选择的数据(1过滤 0不过滤)|query|false|integer(int32)||
-|flyTime|时间筛选(yyyy-MM-dd HH:mm:ss)|query|false|string(date-time)||
-|isSetSceneManage|是否按场景管理过滤(1是 0或空否)|query|false|integer(int32)||
-|sceneId|场景id|query|false|integer(int64)||
+|time|时间筛选(yyyy-MM-dd HH:mm:ss)|query|false|string(date-time)||
**响应状态**:
@@ -39,7 +35,7 @@
| 状态码 | 说明 | schema |
| -------- | -------- | ----- |
-|200|OK|R«List«FwAreaDivideVO»»|
+|200|OK|R«List«FwDefenseSceneManageVO»»|
|401|Unauthorized||
|403|Forbidden||
|404|Not Found||
@@ -51,28 +47,18 @@
| 参数名称 | 参数说明 | 类型 | schema |
| -------- | -------- | ----- |----- |
|code|状态码|integer(int32)|integer(int32)|
-|data|承载数据|array|FwAreaDivideVO|
+|data|承载数据|array|FwDefenseSceneManageVO|
|  areaCode|区域编码|string||
-|  areaName|区域名称|string||
-|  areaTypeKeys|区域面的类型集合逗号隔开|string||
-|  controlLevel|管控级别:日常/重点/严密|string||
-|  deviceIds|关联设备ID,逗号分隔|string||
-|  flyDateEnd|可飞行日期时间-结束|string(date-time)||
-|  flyDateStart|可飞行日期时间-开始|string(date-time)||
-|  fwAreaDivideExtList|面数据|array|FwAreaDivideExtVO|
-|    areaCode|区域编码|string||
-|    areaDivideId|关联主表区域划分ID|integer||
-|    areaTypeKey|区域类型数字字典key|string||
-|    areaTypeValue|区域类型数字字典value|string||
-|    geomJson|几何JSON|string||
-|    id|主键id|integer||
-|  id|主键id|integer(int64)||
-|  policeStationContactPerson|派出所联系人|string||
-|  policeStationContactPhone|派出所联系电话|string||
-|  policeStationId|关联派出所id|string||
-|  policeStationName|派出所名称|string||
-|  responseMechanism|响应机制内容|string||
-|  triggerCondition|触发条件|string||
+|  defenseLeader|防控负责人|string||
+|  defenseSceneId|关联场景配置ID|integer(int64)||
+|  defenseSceneName|场景配置名称|string||
+|  effectiveDateEnd|有效时间-结束|string(date-time)||
+|  effectiveDateStart|有效时间-开始|string(date-time)||
+|  id||integer(int64)||
+|  latitude|指挥点纬度|number(double)||
+|  leaderPhone|防控负责人电话|string||
+|  longitude|指挥点经度|number(double)||
+|  sceneName|场景名称|string||
|msg|返回消息|string||
|success|是否成功|boolean||
@@ -84,29 +70,16 @@
"data": [
{
"areaCode": "",
- "areaName": "",
- "areaTypeKeys": "",
- "controlLevel": "",
- "deviceIds": "",
- "flyDateEnd": "",
- "flyDateStart": "",
- "fwAreaDivideExtList": [
- {
- "areaCode": "",
- "areaDivideId": 0,
- "areaTypeKey": "",
- "areaTypeValue": "",
- "geomJson": "",
- "id": 0
- }
- ],
+ "defenseLeader": "",
+ "defenseSceneId": 0,
+ "defenseSceneName": "",
+ "effectiveDateEnd": "",
+ "effectiveDateStart": "",
"id": 0,
- "policeStationContactPerson": "",
- "policeStationContactPhone": "",
- "policeStationId": "",
- "policeStationName": "",
- "responseMechanism": "",
- "triggerCondition": ""
+ "latitude": 0,
+ "leaderPhone": "",
+ "longitude": 0,
+ "sceneName": ""
}
],
"msg": "",
diff --git a/applications/drone-command/src/views/areaManage/areaStatistics/index.vue b/applications/drone-command/src/views/areaManage/areaStatistics/index.vue
index 79bfc7d..e493c7b 100644
--- a/applications/drone-command/src/views/areaManage/areaStatistics/index.vue
+++ b/applications/drone-command/src/views/areaManage/areaStatistics/index.vue
@@ -6,9 +6,9 @@
clearable @clear="handleSearch" />
</el-form-item>
- <el-form-item label="区域类型" prop="areaType">
+ <el-form-item label="区域类型" prop="areaTypeKeys">
<el-select class="command-select" popper-class="command-select-popper"
- v-model="searchParams.areaType" placeholder="请选择" clearable @change="handleSearch">
+ v-model="searchParams.areaTypeKeys" placeholder="请选择" clearable @change="handleSearch">
<el-option v-for="item in dictObj.areaType" :key="item.dictKey" :label="item.dictValue"
:value="item.dictKey" />
</el-select>
@@ -33,9 +33,9 @@
<el-table class="command-table" :data="list">
<el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
<el-table-column prop="areaName" show-overflow-tooltip label="区域名称" />
- <el-table-column prop="areaType" show-overflow-tooltip label="区域类型">
+ <el-table-column prop="areaTypeKeys" show-overflow-tooltip label="区域类型">
<template v-slot="{ row }">
- {{ getDictLabel(row.areaType, dictObj.areaType) }}
+ {{ getDictLabel(row.areaTypeKeys, dictObj.areaType) }}
</template>
</el-table-column>
<el-table-column prop="sceneName" show-overflow-tooltip label="关联场景" />
@@ -79,7 +79,7 @@
// 初始化查询参数
const initSearchParams = () => ({
areaName: '', // 区域名称
- areaType: '', // 区域类型
+ areaTypeKeys: '', // 区域类型
sceneType: '', // 场景类型
current: 1, // 当前页
size: 10, // 每页大小
diff --git a/applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue b/applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue
index 92c6ef8..9f490c4 100644
--- a/applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue
+++ b/applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue
@@ -266,7 +266,7 @@
const formRef = ref(null) // 表单实例
const formData = ref(initForm()) // 表单数据
const visible = defineModel() // 弹框显隐
-const dialogMode = ref('add') // 弹框模式
+const dialogMode = ref() // 弹框模式
const submitting = ref(false) // 提交中
const readonly = computed(() => dialogMode.value === 'view')
const route = useRoute()
@@ -301,6 +301,9 @@
let shapeDisplaySource
const deviceRangePrimitiveMap = new Map()
const activeToolMode = ref('')
+const suppressDeviceFly = ref(false)
+let lastSelectedDeviceIds = []
+let deviceRangeRenderToken = 0
const rules = {
areaName: fieldRules(true, 50),
@@ -482,6 +485,8 @@
async function renderDeviceRanges (rows) {
if (!viewer) return
+ deviceRangeRenderToken += 1
+ const renderToken = deviceRangeRenderToken
const selectedRows = Array.isArray(rows) ? rows : []
const nextIds = new Set(selectedRows.map(row => String(row?.id)))
for (const [id, primitive] of deviceRangePrimitiveMap.entries()) {
@@ -504,9 +509,11 @@
return { row, primitive }
})
)
+ if (renderToken !== deviceRangeRenderToken) return
primitives.forEach(({ row, primitive }) => {
if (!primitive) return
const id = String(row?.id)
+ if (!nextIds.has(id)) return
deviceRangePrimitiveMap.set(id, primitive)
viewer.scene.primitives.add(primitive)
})
@@ -668,6 +675,69 @@
material: style.outline,
},
})
+ })
+}
+
+function pushRadiusBoundingPoints (positions, center, radius) {
+ if (!center || !Number.isFinite(Number(radius)) || radius <= 0) return
+ const resolved = resolveLngLatPoint(center) || {}
+ const lng = Number(resolved.lng ?? resolved.longitude)
+ const lat = Number(resolved.lat ?? resolved.latitude)
+ if (!Number.isFinite(lng) || !Number.isFinite(lat)) return
+ const baseLat = Cesium.Math.toRadians(lat)
+ const earthRadius = Cesium.Ellipsoid.WGS84.maximumRadius
+ const deltaLat = radius / earthRadius
+ const deltaLng = radius / (earthRadius * Math.max(Math.cos(baseLat), 0.000001))
+ const points = [
+ { lng: Cesium.Math.toDegrees(Cesium.Math.toRadians(lng) + deltaLng), lat },
+ { lng: Cesium.Math.toDegrees(Cesium.Math.toRadians(lng) - deltaLng), lat },
+ { lng, lat: Cesium.Math.toDegrees(baseLat + deltaLat) },
+ { lng, lat: Cesium.Math.toDegrees(baseLat - deltaLat) },
+ ]
+ points.forEach(point => {
+ positions.push(Cesium.Cartesian3.fromDegrees(point.lng, point.lat, resolved.height || 0))
+ })
+}
+
+function collectShapeBoundingPositions () {
+ const positions = []
+ if (!viewer || !shapeList.value.length) return positions
+ shapeList.value.forEach(shape => {
+ let points = getShapeDisplayPoints(shape)
+ if (!Array.isArray(points) || !points.length) {
+ points = resolveControlPoints(shape?.meta, shape?.drawType)
+ }
+ if (Array.isArray(points) && points.length) {
+ points.forEach(point => {
+ const resolved = resolveLngLatPoint(point)
+ const normalized = normalizeShapePoint(resolved)
+ if (!normalized) return
+ positions.push(Cesium.Cartesian3.fromDegrees(normalized.lng, normalized.lat, normalized.height))
+ })
+ }
+ if (shape?.drawType === 'buffer' && shape?.meta?.center && Array.isArray(shape?.meta?.bufferRadii)) {
+ const maxRadius = Math.max(...shape.meta.bufferRadii.filter(item => Number.isFinite(item)))
+ pushRadiusBoundingPoints(positions, shape.meta.center, maxRadius)
+ }
+ if (shape?.drawType === 'ellipse' && shape?.meta?.center) {
+ const major = Number(shape?.meta?.semiMajor)
+ const minor = Number(shape?.meta?.semiMinor)
+ const radius = Number.isFinite(major) && Number.isFinite(minor) ? Math.max(major, minor) : null
+ pushRadiusBoundingPoints(positions, shape.meta.center, radius)
+ }
+ })
+ return positions
+}
+
+function fitViewToShapeList () {
+ if (!viewer || !shapeList.value.length) return
+ const positions = collectShapeBoundingPositions()
+ if (positions.length < 2) return
+ const boundingSphere = Cesium.BoundingSphere.fromPoints(positions)
+ const range = Math.max(boundingSphere.radius * 2.4, 200)
+ viewer.camera.flyToBoundingSphere(boundingSphere, {
+ duration: 0,
+ offset: new Cesium.HeadingPitchRange(viewer.camera.heading, Cesium.Math.toRadians(-90), range),
})
}
@@ -970,6 +1040,7 @@
if (!viewer) return
if (shapeList.value.length) {
renderShapeList()
+ fitViewToShapeList()
return
}
if (!formData.value?.geom) return
@@ -1010,9 +1081,15 @@
const ids = rows.map(item => item.id)
formData.value.deviceIds = ids.join(',')
renderDeviceRanges(rows)
- const target = rows[rows.length - 1]
- if (target) {
- void flyToDevice(target)
+ if (suppressDeviceFly.value) {
+ lastSelectedDeviceIds = [...ids]
+ return
+ }
+ const prevSet = new Set(lastSelectedDeviceIds)
+ const addedRow = rows.find(row => !prevSet.has(row.id))
+ lastSelectedDeviceIds = [...ids]
+ if (addedRow) {
+ void flyToDevice(addedRow)
}
}
@@ -1074,7 +1151,11 @@
viewPolygon()
}
await nextTick()
+ suppressDeviceFly.value = true
syncDeviceSelection()
+ lastSelectedDeviceIds = selectedDeviceRows.value.map(row => row.id)
+ await nextTick()
+ suppressDeviceFly.value = false
if (!readonly.value) {
renderDeviceRanges(selectedDeviceRows.value)
}
diff --git a/applications/drone-command/src/views/areaManage/precinctInfo/FormDiaLog.vue b/applications/drone-command/src/views/areaManage/precinctInfo/FormDiaLog.vue
index 50e15ee..f698ff7 100644
--- a/applications/drone-command/src/views/areaManage/precinctInfo/FormDiaLog.vue
+++ b/applications/drone-command/src/views/areaManage/precinctInfo/FormDiaLog.vue
@@ -111,7 +111,7 @@
</template>
<script setup>
-import { computed, nextTick, ref } from 'vue'
+import { computed, nextTick, ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { fwPoliceStationDetailApi, fwPoliceStationSubmitApi } from './precinctInfoApi'
import { fieldRules } from '@ztzf/utils'
@@ -121,6 +121,7 @@
import { saveOperationLog } from '@ztzf/apis'
import { useRoute } from 'vue-router'
import positionIcon from '@/assets/images/areaManage/positionIcon.png'
+import { MapTooltip } from '@/utils/cesium/shapeTools/Tooltip'
const initForm = () => ({
address: '', // 位置
@@ -185,6 +186,10 @@
let viewer
let redPointEntity
let leftClickBound = false
+let mouseMoveBound = false
+let mapTooltip
+let publicCesium
+const tooltipEventKey = 'precinct-info-point-tip'
const labelParams = {
font: '18px',
fillColor: Cesium.Color.WHITE, // 文字颜色:白色
@@ -271,12 +276,38 @@
await nextTick()
const map = inlineMapRef.value?.getMap()
viewer = map?.viewer || null
+ publicCesium = map?.publicCesium || null
if (viewer && !leftClickBound) {
map.publicCesium?.addLeftClickEvent?.(null, LeftClickEvent)
leftClickBound = true
}
+ if (viewer && !readonly.value) {
+ ensurePointTooltip()
+ }
if (formData.value.longitude != null && formData.value.latitude != null) {
setMapPoint(formData.value.longitude, formData.value.latitude)
+ }
+}
+
+function ensurePointTooltip() {
+ if (!viewer || !publicCesium || mouseMoveBound) return
+ mapTooltip ||= new MapTooltip(viewer, { offset: { x: 16, y: 24 } })
+ publicCesium.addMouseHandler?.(
+ null,
+ movement => {
+ if (!visible.value || readonly.value) return
+ mapTooltip?.show('点击地图选择派出所位置', movement.endPosition)
+ },
+ tooltipEventKey
+ )
+ mouseMoveBound = true
+}
+
+function clearPointTooltip() {
+ mapTooltip?.hide()
+ if (publicCesium && mouseMoveBound) {
+ publicCesium.removeMouseHandler?.(tooltipEventKey)
+ mouseMoveBound = false
}
}
@@ -296,6 +327,7 @@
formData.value = dialogMode.value === 'add' ? initForm() : row
redPointEntity = null
leftClickBound = false
+ clearPointTooltip()
if (dialogMode.value !== 'add') {
await loadDetail()
}
@@ -305,6 +337,15 @@
}, 500)
}
+watch(
+ () => visible.value,
+ val => {
+ if (!val) {
+ clearPointTooltip()
+ }
+ }
+)
+
defineExpose({ open })
</script>
<style scoped lang="scss">
diff --git a/applications/drone-command/src/views/areaManage/sceneConfig/FormDiaLog.vue b/applications/drone-command/src/views/areaManage/sceneConfig/FormDiaLog.vue
index 43d3a82..464fac4 100644
--- a/applications/drone-command/src/views/areaManage/sceneConfig/FormDiaLog.vue
+++ b/applications/drone-command/src/views/areaManage/sceneConfig/FormDiaLog.vue
@@ -10,6 +10,7 @@
:terrain="true"
:layer-mode="4"
:boundary="false"
+ :zoom-to-boundary="dialogMode === 'add'"
/>
</div>
<div class="right-container">
@@ -23,7 +24,7 @@
<div class="detail-title">场景详情</div>
<el-row>
<el-col :span="24">
- <div class="label">场景配置名称</div>
+ <div class="label">配置名称</div>
<div class="val">{{ formData.sceneName }}</div>
</el-col>
<el-col :span="24">
@@ -60,7 +61,7 @@
:disabled="readonly"
label-width="96px"
>
- <el-form-item label="场景配置名称" prop="sceneName">
+ <el-form-item label="配置名称" prop="sceneName">
<el-input
class="command-input"
v-model="formData.sceneName"
@@ -160,12 +161,13 @@
import * as Cesium from 'cesium'
import { fwAreaDivideDetailApi, fwAreaDivideListApi } from '../partition/partitionApi'
import { buildEllipsePositions } from '@/utils/cesium/shapeTools'
+import { cartesian3Convert } from '@/utils/cesium/mapUtil'
import { saveOperationLog } from '@ztzf/apis'
import { useRoute } from 'vue-router'
import { AREA_TYPE_STYLE_MAP, BUFFER_LEVEL_STYLES, DEFAULT_AREA_STYLE } from '@ztzf/constants'
const initForm = () => ({
- sceneName: '', // 场景配置名称
+ sceneName: '', // 配置名称
sceneType: '', // 场景类型
deviceMode: '', // 设备模式
areaDivideIds: '', // 关联区域ID
@@ -176,7 +178,7 @@
const formRef = ref(null) // 表单实例
const formData = ref(initForm()) // 表单数据
const visible = defineModel() // 弹框显隐
-const dialogMode = ref('add') // 弹框模式
+const dialogMode = ref() // 弹框模式
const submitting = ref(false) // 提交中
const readonly = computed(() => dialogMode.value === 'view')
const titleEnum = ref({ edit: '编辑', view: '查看', add: '新增' })
@@ -272,6 +274,28 @@
return { lng: Number(lng), lat: Number(lat), height }
}
+function resolveLngLatPoint(point) {
+ if (!point) return null
+ const lng = point?.lng ?? point?.longitude
+ const lat = point?.lat ?? point?.latitude
+ if (Number.isFinite(Number(lng)) && Number.isFinite(Number(lat))) {
+ return {
+ lng: Number(lng),
+ lat: Number(lat),
+ height: Number.isFinite(Number(point?.height)) ? Number(point.height) : 0,
+ }
+ }
+ if (!viewer || !Number.isFinite(point?.x) || !Number.isFinite(point?.y) || !Number.isFinite(point?.z)) {
+ return null
+ }
+ const val = cartesian3Convert(point, viewer)
+ return {
+ lng: val.longitude,
+ lat: val.latitude,
+ height: Number.isFinite(val.height) ? val.height : 0,
+ }
+}
+
function buildShapePositions(points = []) {
const normalized = points.map(normalizeShapePoint).filter(Boolean)
return normalized.map(point => Cesium.Cartesian3.fromDegrees(point.lng, point.lat, point.height))
@@ -286,6 +310,20 @@
function getAreaTypeStyle(areaType) {
return AREA_TYPE_STYLE_MAP?.[String(areaType)] || DEFAULT_AREA_STYLE
+}
+
+function resolveControlPoints(meta, drawType) {
+ if (!meta) return []
+ if (Array.isArray(meta.controlPoints) && meta.controlPoints.length) {
+ return meta.controlPoints.map(resolveLngLatPoint).filter(Boolean)
+ }
+ if (drawType === 'buffer') {
+ return [resolveLngLatPoint(meta.center)].filter(Boolean)
+ }
+ if (drawType === 'ellipse') {
+ return [meta.center, meta.majorPoint, meta.minorPoint].map(resolveLngLatPoint).filter(Boolean)
+ }
+ return []
}
function parseGeomJson(geomJson) {
@@ -400,6 +438,74 @@
})
}
+function pushRadiusBoundingPoints(positions, center, radius) {
+ if (!center || !Number.isFinite(Number(radius)) || radius <= 0) return
+ const resolved = resolveLngLatPoint(center) || {}
+ const lng = Number(resolved.lng ?? resolved.longitude)
+ const lat = Number(resolved.lat ?? resolved.latitude)
+ if (!Number.isFinite(lng) || !Number.isFinite(lat)) return
+ const baseLat = Cesium.Math.toRadians(lat)
+ const earthRadius = Cesium.Ellipsoid.WGS84.maximumRadius
+ const deltaLat = radius / earthRadius
+ const deltaLng = radius / (earthRadius * Math.max(Math.cos(baseLat), 0.000001))
+ const points = [
+ { lng: Cesium.Math.toDegrees(Cesium.Math.toRadians(lng) + deltaLng), lat },
+ { lng: Cesium.Math.toDegrees(Cesium.Math.toRadians(lng) - deltaLng), lat },
+ { lng, lat: Cesium.Math.toDegrees(baseLat + deltaLat) },
+ { lng, lat: Cesium.Math.toDegrees(baseLat - deltaLat) },
+ ]
+ points.forEach(point => {
+ positions.push(Cesium.Cartesian3.fromDegrees(point.lng, point.lat, resolved.height || 0))
+ })
+}
+
+function collectSelectedAreaPositions() {
+ const positions = []
+ if (!viewer || !selectedAreaRows.value.length) return positions
+ selectedAreaRows.value.forEach(area => {
+ const shapes = resolveAreaShapes(area)
+ shapes.forEach(shape => {
+ let points = getShapeDisplayPoints(shape)
+ if (!Array.isArray(points) || !points.length) {
+ points = resolveControlPoints(shape?.meta, shape?.drawType)
+ }
+ if (Array.isArray(points) && points.length) {
+ points.forEach(point => {
+ const resolved = resolveLngLatPoint(point)
+ const normalized = normalizeShapePoint(resolved)
+ if (!normalized) return
+ positions.push(
+ Cesium.Cartesian3.fromDegrees(normalized.lng, normalized.lat, normalized.height)
+ )
+ })
+ }
+ if (shape?.drawType === 'buffer' && shape?.meta?.center && Array.isArray(shape?.meta?.bufferRadii)) {
+ const maxRadius = Math.max(...shape.meta.bufferRadii.filter(item => Number.isFinite(item)))
+ pushRadiusBoundingPoints(positions, shape.meta.center, maxRadius)
+ }
+ if (shape?.drawType === 'ellipse' && shape?.meta?.center) {
+ const major = Number(shape?.meta?.semiMajor)
+ const minor = Number(shape?.meta?.semiMinor)
+ const radius = Number.isFinite(major) && Number.isFinite(minor) ? Math.max(major, minor) : null
+ pushRadiusBoundingPoints(positions, shape.meta.center, radius)
+ }
+ })
+ })
+ return positions
+}
+
+function fitViewToSelectedAreas() {
+ if (!viewer || !selectedAreaRows.value.length) return
+ const positions = collectSelectedAreaPositions()
+ if (positions.length < 2) return
+ const boundingSphere = Cesium.BoundingSphere.fromPoints(positions)
+ const range = Math.max(boundingSphere.radius * 2.4, 200)
+ viewer.camera.flyToBoundingSphere(boundingSphere, {
+ duration: 0,
+ offset: new Cesium.HeadingPitchRange(viewer.camera.heading, Cesium.Math.toRadians(-90), range),
+ })
+}
+
watch(() => selectedAreaRows.value, renderSelectedAreas)
watch(
() => visible.value,
@@ -468,11 +574,17 @@
await nextTick()
initMap()
await getAreaList()
- if (dialogMode.value !== 'add') {
+ if (dialogMode.value === 'add') {
+ console.log(111)
+
+ mapRef.value?.zoomToAdminBoundary?.()
+ } else {
await loadDetail()
}
await nextTick()
await syncAreaSelection()
+ await nextTick()
+ fitViewToSelectedAreas()
}
defineExpose({
open,
diff --git a/applications/drone-command/src/views/areaManage/sceneConfig/index.vue b/applications/drone-command/src/views/areaManage/sceneConfig/index.vue
index 75de78d..7ef87d3 100644
--- a/applications/drone-command/src/views/areaManage/sceneConfig/index.vue
+++ b/applications/drone-command/src/views/areaManage/sceneConfig/index.vue
@@ -1,7 +1,7 @@
<template>
<basic-container>
<el-form ref="queryParamsRef" :model="searchParams" class="command-page-history-search">
- <el-form-item label="场景配置名称" prop="sceneName">
+ <el-form-item label="配置名称" prop="sceneName">
<el-input
class="command-input"
v-model="searchParams.sceneName"
@@ -36,7 +36,7 @@
</el-form>
<div class="command-table-toolbar">
- <el-button :icon="Plus" color="#284FE3" type="primary" @click="openForm('add')">新增</el-button>
+ <el-button :icon="Plus" color="#284FE3" type="primary" @click="openForm('add')">新增配置</el-button>
<el-button :icon="Delete" color="#1A2652" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button>
</div>
@@ -46,7 +46,7 @@
<el-table-column type="selection" width="46" />
<el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
- <el-table-column prop="sceneName" show-overflow-tooltip label="场景配置名称" />
+ <el-table-column prop="sceneName" show-overflow-tooltip label="配置名称" />
<el-table-column prop="sceneType" show-overflow-tooltip label="场景类型">
<template v-slot="{ row }">
{{ getDictLabel(row.sceneType, dictObj.CommandSceneType) }}
@@ -93,7 +93,7 @@
import { useRoute } from 'vue-router'
const initSearchParams = () => ({
- sceneName: '', // 场景配置名称
+ sceneName: '', // 配置名称
sceneType: '', // 场景类型
current: 1, // 当前页
size: 10, // 每页大小
diff --git a/applications/drone-command/src/views/areaManage/sceneManage/FormDiaLog.vue b/applications/drone-command/src/views/areaManage/sceneManage/FormDiaLog.vue
index 12406be..da80178 100644
--- a/applications/drone-command/src/views/areaManage/sceneManage/FormDiaLog.vue
+++ b/applications/drone-command/src/views/areaManage/sceneManage/FormDiaLog.vue
@@ -10,6 +10,7 @@
:terrain="true"
:layer-mode="4"
:boundary="false"
+ :zoom-to-boundary="dialogMode === 'add'"
/>
</div>
<div class="right-container">
@@ -110,7 +111,16 @@
/>
</el-form-item>
<el-form-item label="指挥点位置" prop="longitude">
- <div class="val">{{ formatLocation(formData) }}</div>
+ <el-input
+ class="command-input"
+ :model-value="commandPointText"
+ disabled
+ placeholder="请选择指挥点位置"
+ >
+ <template #suffix>
+ <span class="suffix-action">地图选点</span>
+ </template>
+ </el-input>
</el-form-item>
</el-form>
<div class="footer">
@@ -148,7 +158,9 @@
import { useRoute } from 'vue-router'
import { fwAreaDivideDetailApi, fwAreaDivideListApi } from '../partition/partitionApi'
import { buildEllipsePositions } from '@/utils/cesium/shapeTools'
+import { cartesian3Convert } from '@/utils/cesium/mapUtil'
import { AREA_TYPE_STYLE_MAP, BUFFER_LEVEL_STYLES, DEFAULT_AREA_STYLE } from '@ztzf/constants'
+import { MapTooltip } from '@/utils/cesium/shapeTools/Tooltip'
// 初始化表单数据
const initForm = () => ({
@@ -162,11 +174,19 @@
defenseSceneId: '', // 关联场景配置ID
})
+const commandPointText = computed(() => {
+ // 你也可以改成 `${lng}, ${lat}`
+ if (!formData.value.longitude) {
+ return ''
+ }
+ return `${formData.value.longitude}, ${formData.value.latitude}`;
+});
+
const emit = defineEmits(['success'])
const formRef = ref(null) // 表单实例
const formData = ref(initForm()) // 表单数据
const visible = defineModel() // 弹框显隐
-const dialogMode = ref('add') // 弹框模式
+const dialogMode = ref() // 弹框模式
const submitting = ref(false) // 提交中
const readonly = computed(() => dialogMode.value === 'view')
const titleEnum = ref({ edit: '编辑', view: '查看', add: '新增' })
@@ -179,6 +199,10 @@
let redPointEntity
let areaDisplaySource
let leftClickBound = false
+let mouseMoveBound = false
+let mapTooltip
+let publicCesium
+const tooltipEventKey = 'scene-manage-point-tip'
// 表单验证规则
const rules = {
@@ -263,6 +287,28 @@
return normalized.map(point => Cesium.Cartesian3.fromDegrees(point.lng, point.lat, point.height))
}
+function resolveLngLatPoint(point) {
+ if (!point) return null
+ const lng = point?.lng ?? point?.longitude
+ const lat = point?.lat ?? point?.latitude
+ if (Number.isFinite(Number(lng)) && Number.isFinite(Number(lat))) {
+ return {
+ lng: Number(lng),
+ lat: Number(lat),
+ height: Number.isFinite(Number(point?.height)) ? Number(point.height) : 0,
+ }
+ }
+ if (!viewer || !Number.isFinite(point?.x) || !Number.isFinite(point?.y) || !Number.isFinite(point?.z)) {
+ return null
+ }
+ const val = cartesian3Convert(point, viewer)
+ return {
+ lng: val.longitude,
+ lat: val.latitude,
+ height: Number.isFinite(val.height) ? val.height : 0,
+ }
+}
+
function getShapeDisplayPoints(shape) {
if (Array.isArray(shape?.displayPoints) && shape.displayPoints.length) {
return shape.displayPoints
@@ -315,6 +361,71 @@
}
})
.filter(Boolean)
+}
+
+function pushRadiusBoundingPoints(positions, center, radius) {
+ if (!center || !Number.isFinite(Number(radius)) || radius <= 0) return
+ const resolved = resolveLngLatPoint(center) || {}
+ const lng = Number(resolved.lng ?? resolved.longitude)
+ const lat = Number(resolved.lat ?? resolved.latitude)
+ if (!Number.isFinite(lng) || !Number.isFinite(lat)) return
+ const baseLat = Cesium.Math.toRadians(lat)
+ const earthRadius = Cesium.Ellipsoid.WGS84.maximumRadius
+ const deltaLat = radius / earthRadius
+ const deltaLng = radius / (earthRadius * Math.max(Math.cos(baseLat), 0.000001))
+ const points = [
+ { lng: Cesium.Math.toDegrees(Cesium.Math.toRadians(lng) + deltaLng), lat },
+ { lng: Cesium.Math.toDegrees(Cesium.Math.toRadians(lng) - deltaLng), lat },
+ { lng, lat: Cesium.Math.toDegrees(baseLat + deltaLat) },
+ { lng, lat: Cesium.Math.toDegrees(baseLat - deltaLat) },
+ ]
+ points.forEach(point => {
+ positions.push(Cesium.Cartesian3.fromDegrees(point.lng, point.lat, resolved.height || 0))
+ })
+}
+
+function collectSceneAreaPositions() {
+ const positions = []
+ if (!viewer || !areaList.value.length) return positions
+ areaList.value.forEach(area => {
+ const shapes = resolveAreaShapes(area)
+ shapes.forEach(shape => {
+ const points = getShapeDisplayPoints(shape)
+ if (Array.isArray(points) && points.length) {
+ points.forEach(point => {
+ const resolved = resolveLngLatPoint(point)
+ const normalized = normalizeShapePoint(resolved)
+ if (!normalized) return
+ positions.push(
+ Cesium.Cartesian3.fromDegrees(normalized.lng, normalized.lat, normalized.height)
+ )
+ })
+ }
+ if (shape?.drawType === 'buffer' && shape?.meta?.center && Array.isArray(shape?.meta?.bufferRadii)) {
+ const maxRadius = Math.max(...shape.meta.bufferRadii.filter(item => Number.isFinite(item)))
+ pushRadiusBoundingPoints(positions, shape.meta.center, maxRadius)
+ }
+ if (shape?.drawType === 'ellipse' && shape?.meta?.center) {
+ const major = Number(shape?.meta?.semiMajor)
+ const minor = Number(shape?.meta?.semiMinor)
+ const radius = Number.isFinite(major) && Number.isFinite(minor) ? Math.max(major, minor) : null
+ pushRadiusBoundingPoints(positions, shape.meta.center, radius)
+ }
+ })
+ })
+ return positions
+}
+
+function fitViewToSceneAreas() {
+ if (!viewer || !areaList.value.length) return
+ const positions = collectSceneAreaPositions()
+ if (positions.length < 2) return
+ const boundingSphere = Cesium.BoundingSphere.fromPoints(positions)
+ const range = Math.max(boundingSphere.radius * 2.4, 200)
+ viewer.camera.flyToBoundingSphere(boundingSphere, {
+ duration: 0,
+ offset: new Cesium.HeadingPitchRange(viewer.camera.heading, Cesium.Math.toRadians(-90), range),
+ })
}
function renderSceneAreas() {
@@ -449,13 +560,36 @@
// 初始化地图实例
function initMap() {
- if (viewer) return
const map = mapRef.value?.getMap()
if (!map?.viewer) return
- viewer = map.viewer
+ viewer ||= map.viewer
+ publicCesium ||= map.publicCesium
if (!readonly.value && !leftClickBound) {
map.publicCesium?.addLeftClickEvent?.(null, LeftClickEvent)
leftClickBound = true
+ }
+ if (!readonly.value) ensurePointTooltip()
+}
+
+function ensurePointTooltip() {
+ if (!viewer || !publicCesium || mouseMoveBound) return
+ mapTooltip ||= new MapTooltip(viewer, { offset: { x: 28, y: 24 } })
+ publicCesium.addMouseHandler?.(
+ null,
+ movement => {
+ if (!visible.value || readonly.value) return
+ mapTooltip?.show('点击地图选择指挥点位置', movement.endPosition)
+ },
+ tooltipEventKey
+ )
+ mouseMoveBound = true
+}
+
+function clearPointTooltip() {
+ mapTooltip?.hide()
+ if (publicCesium && mouseMoveBound) {
+ publicCesium.removeMouseHandler?.(tooltipEventKey)
+ mouseMoveBound = false
}
}
@@ -520,13 +654,19 @@
redPointEntity = null
await nextTick()
initMap()
+ if (dialogMode.value === 'view') {
+ clearPointTooltip()
+ }
await getSceneConfigList()
- if (dialogMode.value !== 'add') {
+ if (dialogMode.value === 'add') {
+ mapRef.value?.zoomToAdminBoundary?.()
+ } else {
await loadDetail()
}
await nextTick()
setMapPoint(formData.value.longitude, formData.value.latitude)
await loadAreaList(formData.value.defenseSceneId)
+ fitViewToSceneAreas()
}
onMounted(() => {
@@ -543,7 +683,10 @@
watch(
() => visible.value,
val => {
- if (!val) clearAreaDisplay()
+ if (!val) {
+ clearAreaDisplay()
+ clearPointTooltip()
+ }
}
)
diff --git a/applications/drone-command/src/views/areaManage/sceneManage/sceneManageApi.js b/applications/drone-command/src/views/areaManage/sceneManage/sceneManageApi.js
index 77464c3..b0ce4f4 100644
--- a/applications/drone-command/src/views/areaManage/sceneManage/sceneManageApi.js
+++ b/applications/drone-command/src/views/areaManage/sceneManage/sceneManageApi.js
@@ -9,6 +9,15 @@
})
}
+// 查list
+export const fwDefenseSceneManageListApi = params => {
+ return request({
+ url: `/drone-fw/area/fwDefenseSceneManage/list`,
+ method: 'get',
+ params,
+ })
+}
+
// 获取场景管理详情
export const fwDefenseSceneManageDetailApi = params => {
return request({
diff --git a/applications/drone-command/src/views/job/components/CancelTaskDialog.vue b/applications/drone-command/src/views/job/components/CancelTaskDialog.vue
deleted file mode 100644
index 9f039c2..0000000
--- a/applications/drone-command/src/views/job/components/CancelTaskDialog.vue
+++ /dev/null
@@ -1,152 +0,0 @@
-<template>
- <el-dialog
- v-model="dialogVisible"
- :width="400"
- :close-on-click-modal="false"
- :destroy-on-close="true"
- class="cancel-task-dialogs"
- >
- <div class="dialog-headers">
- <img @click="closeClick" src="@/assets/images/task/close.png" alt="" />
- </div>
- <div class="dialog-contents">
- <div class="dialog-text">您确定取消任务?</div>
- <div class="dialog-btns">
- <img src="@/assets/images/task/cancel-one.png" @click="handleCancel" alt="" />
- <img src="@/assets/images/task/cancel-all.png" @click="handleCancelAll" alt="" />
- </div>
- </div>
- </el-dialog>
-</template>
-<script setup>
-import { cancelJobs } from '@/api/job/task'
-import { ElMessage } from 'element-plus'
-
-const props = defineProps({
- isShowCancelTask: {
- type: Boolean,
- default: false,
- },
- rowData: {
- type: Object,
- required: true,
- },
-})
-
-const emit = defineEmits(['update:isShowCancelTask', 'refresh'])
-
-const dialogVisible = computed({
- get: () => props.isShowCancelTask,
- set: val => emit('update:isShowCancelTask', val),
-})
-
-// 取消本次
-const handleCancel = () => {
- submitCancelTask(props.rowData, 1)
- dialogVisible.value = false
-}
-// 取消所有
-const handleCancelAll = () => {
- submitCancelTask(props.rowData, 0)
- dialogVisible.value = false
-}
-// 提交取消任务请求
-const submitCancelTask = (row, isSingle) => {
- if (!row.wayline_job_info_id) {
- ElMessage.warning('该任务无法取消,数据未兼容!')
- return
- }
- cancelJobs({
- jobInfoId: row.wayline_job_info_id,
- batchNo: row.batch_no,
- isSingle: isSingle,
- }).then(res => {
- if (res.data.code !== 0) return
- ElMessage.success('取消任务成功')
- emit('refresh')
- })
-}
-const closeClick = () => {
- dialogVisible.value = false
-}
-
-defineExpose({
- submitCancelTask,
-})
-</script>
-<style lang="scss">
-.cancel-task-dialogs {
- margin: 0;
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- padding: 0;
- // width: 398px;
- // height: 181px;
- background: #0f1d33;
- box-shadow: inset 0px -50px 50px 0px rgba(27, 148, 255, 0.13);
- border-radius: 50px 0px 0px 0px;
- border: 2px solid;
- border-image: linear-gradient(
- 180deg,
- rgba(81, 168, 255, 0),
- rgba(48, 111, 202, 1),
- rgba(255, 255, 255, 1),
- rgba(27, 148, 255, 1)
- )
- 2 2;
- .el-dialog__header {
- display: none;
- }
- .dialog-headers {
- width: 100%;
- height: 60px;
- background: url('@/assets/images/task/header.png') center no-repeat;
- background-size: 100% 100%;
- color: #fff;
- font-size: 16px;
- font-weight: bold;
- display: flex;
- align-items: center;
- justify-content: center;
- img {
- position: absolute;
- top: 24px;
- right: 20px;
- cursor: pointer;
- }
- }
-
- .dialog-contents {
- padding: 40px 20px 20px 20px;
- .dialog-text {
- color: #fff;
- font-size: 14px;
- margin-bottom: 60px;
- text-align: center;
- }
-
- .dialog-btns {
- display: flex;
- justify-content: center;
- gap: 46px;
- img {
- width: 96px;
- height: 32px;
- cursor: pointer;
- }
- // .el-button {
- // width: 120px;
- // height: 32px;
- // border-radius: 2px;
-
- // &.el-button--primary {
- // background: #026ad6;
- // border-color: #026ad6;
- // }
- // }
- }
- }
-}
-</style>
diff --git a/applications/drone-command/src/views/job/components/DeviceJobDetails.vue b/applications/drone-command/src/views/job/components/DeviceJobDetails.vue
deleted file mode 100644
index a7cfd66..0000000
--- a/applications/drone-command/src/views/job/components/DeviceJobDetails.vue
+++ /dev/null
@@ -1,806 +0,0 @@
-<!-- 历史任务详情 -->
-<template>
- <el-dialog class="command-dialog" append-to-body modal-class="detailsOfHistoricalTasks" v-model="isShow" title="巡检任务详情"
- :width="pxToRem(1800)" :close-on-click-modal="false" :destroy-on-close="true">
- <div class="content">
- <div class="contentLeft">
- <!-- 执行机巢详情-->
- <execute-nest-details :wayLineJobInfoId="wayLineJobInfoId" :batchNo="batchNo"></execute-nest-details>
- <!-- 详情-->
- <div class="detailsLine">
- <img src="/src/assets/images/task/sign.svg" alt="">
- <div class="title">
- 详情
- </div>
- <div class="btn" v-show="videoData.length > 0">
- <el-button
- class="playback-video-btn"
- v-show="videoData.length == 1"
- type="primary"
- @click="openPlaybackVideoDialog(videoData[0])"
- size="small"
- >
- 回放直播
- </el-button>
-
- <el-popover placement="bottom-start" popper-class="command-custom-popover bottom">
- <template #reference>
- <el-button class="playback-video-btn" v-show="videoData.length > 1" type="primary" size="small">
- 回放直播
- </el-button>
- </template>
-
- <div class="playback-video-list">
- <div v-for="(item, index) in videoData" :key="index">
- {{ formatTimestamp(item.execute_time) }}
-
- <div class="play-btn" @click="openPlaybackVideoDialog(item)">播放</div>
- </div>
- </div>
- </el-popover>
- </div>
- </div>
- <div class="infoBox">
- <div class="itemBoxLeft">
- <div v-for="(item, index) in infoList" :key="index" class="itemCon">
- <div class="itemBox">
- <div class="itemTitle">{{ item.name }}:</div>
- <div truncated class="itemValue" v-if="item.name !== '飞行事件' && item.name !== '任务执行次数'">
- {{ item.value ? item.value : '--' }}
- </div>
- <div class="itemValue" v-if="item.name === '任务执行次数'">{{ item.value ? item.value : '0' }}次</div>
- <!-- 飞行事件 -->
- <div class="flightEvents" v-if="item.name === '飞行事件'">
- <template v-if="flightEvents.length">
- <img
- v-for="(item, index) in flightEvents"
- alt=""
- :src="item.imgUrl"
- :title="item.label"
- :key="index"
- />
- </template>
- <div class="itemValue" v-else>--</div>
- </div>
- </div>
- </div>
- </div>
-
- </div>
- <JobRelatedEvents :jobTimes="jobTimes" :batchNo="batchNo" :waylineJobId="waylineJobId" :detailName="detailName" />
- <div class="devicetitle" v-if="isShow">
- <div>
- <img src="/src/assets/images/task/sign.svg" alt="">
- <p>
- 成果数据
- <span>{{ total }}</span>
- 个
- </p>
- </div>
- <div class="rightBox" v-if="total">
- <div class="downloadBtn" @click="htlsrwxq === 100 && downloadFun()">
- <el-progress v-if="htlsrwxq !== 100" :percentage="htlsrwxq" :show-text="false" striped striped-flow
- :duration="1" />
- <div class="downloadBtnText">
- <span v-if="htlsrwxq === 100">下载</span>
- <template v-else>
- 处理中<el-icon @click="cancelDownload">
- <CircleClose />
- </el-icon>
- </template>
- </div>
- </div>
- <div class="downloadBtn" @click="showAll = !showAll" v-if="total > 5">
- {{ showAll ? '收起' : '更多' }}
- </div>
- </div>
- </div>
-<!-- <div class="imgListBox">-->
-<!-- <!– 图片显示 –>-->
-<!-- <template v-for="(item, index) in achievementList.slice(0, visibleCount)" :key="index">-->
-<!-- <div class="result-item">-->
-<!-- <el-checkbox v-model="item.checked" />-->
-<!-- <div class="itemName">{{ item.createTime }}</div>-->
-<!-- <!– 正射 –>-->
-<!-- <el-image v-if="item.resultType === 4" :src="item.smallUrl" :preview-src-list="[item.showUrl]" fit="cover"-->
-<!-- preview-teleported />-->
-<!-- <!– 全景 –>-->
-<!-- <img v-else-if="item.resultType === 5" class="quanjing" @click="clickpanorama(item)" :src="item?.smallUrl"-->
-<!-- alt="" />-->
-<!-- <!– 视频 –>-->
-<!-- <div v-else-if="item.resultType === 1" class="videotime">-->
-<!-- <img class="videoDisplay" :src="convertVideoUrlToThumbnail(item.link)" alt=""-->
-<!-- @click="enterFullScreen(index)" />-->
-<!-- <img @click="enterFullScreen(index)" class="videobutton" src="@/assets/images/task/videoshow.png"-->
-<!-- alt="" />-->
-<!-- </div>-->
-<!-- <!– 图片 –>-->
-<!-- <el-image v-else :src="item.smallUrl" :preview-src-list="[item.showUrl]" preview-teleported-->
-<!-- :initial-index="index" fit="cover" />-->
-
-
-<!-- </div>-->
-
-<!-- </template>-->
-
-<!-- </div>-->
- <div class="imgListBox">
- <template v-for="(item, index) in achievementList.slice(0, visibleCount)" :key="index">
- <div class="result-item">
- <el-checkbox v-model="item.checked" />
- <div class="itemName">{{ item.createTime }}</div>
- <!-- 正射 -->
- <el-image v-if="item.resultType === 4" :src="item?.smallUrl" fit="cover" preview-teleported @click="clickPlay(index)"/>
- <!-- 全景 -->
- <img
- v-else-if="item.resultType === 5"
- class="quanjing"
- @click="clickPlay(index)"
- :src="item?.smallUrl"
- alt=""
- />
- <!-- 视频 -->
- <div v-else-if="item.resultType === 1" class="videotime">
- <img
- class="videoDisplay"
- :src="convertVideoUrlToThumbnail(item.link)"
- alt=""
- @click="clickPlay(index)"
- />
- <img
- @click="clickPlay(index)"
- class="videobutton"
- src="@/assets/images/signMachineNest/videoshow.png"
- alt=""
- />
- </div>
- <!-- 图片 -->
- <el-image v-else :src="item?.smallUrl" fit="cover" preview-teleported @click="clickPlay(index)"/>
- </div>
- </template>
- </div>
- <el-image-viewer
- v-if="showViewer"
- :url-list="previewUrls"
- :initial-index="activeIndex"
- @close="showViewer = false"
- />
- <div class="cg-empty" v-if="achievementList.length === 0">暂无数据</div>
- </div>
-
- <div class="content-right" v-if="isShow">
- <DeviceJobDetailsMap
- :detailsData="detailsData"
- :yuanImages="yuanImages"
- :taskData="taskData"
- @showImageeclick="showImageeclick"
- :jobId="props.jobId"
- />
- <div class="content-map-popups"></div>
- </div>
- </div>
- </el-dialog>
- <el-dialog class="command-dialog" append-to-body modal-class="detailsOfHistoricalTasks" v-model="VideoShow"
- :width="pxToRem(1600)" :close-on-click-modal="false" :destroy-on-close="true">
- <div class="fullscreen">
- <video ref="fullscreenVideo" class="fullscreen-video" :src="currentVideoUrl"
- :style="{ width: pxToRem(1567), height: '80vh' }" controls preload="auto" @play="handleVideoPlay"
- @ended="handleVideoEnded"></video>
- </div>
- </el-dialog>
- <!-- 全景360 -->
- <PanoramaPopup v-if="'全景'" v-model:panoramaParamsShow="panoramaParamsShow"
- v-model:panoramaParamsUrl="panoramaParamsUrl" />
-
- <PlaybackVideo v-model:show="playbackVideo" v-model:detailData="openVideoDetail" :detailsData="detailsData" />
- <FileTypePlay v-model:show="carouselShow" :list="achievementList" :cIndex="currentIndex"></FileTypePlay>
-</template>
-
-<script setup>
-import PanoramaPopup from '@/components/PanoramaPopup/PanoramaPopup.vue'
-import PlaybackVideo from '@/components/PlaybackVideo/PlaybackVideo.vue'
-import FileTypePlay from '@/views/job/components/FileTypePlay.vue';
-import ExecuteNestDetails from './executeNestDetails.vue'
-import { getWaylinejobLiveRecordPage } from '@/api/playback'
-import { getShowImg, getSmallImg, getzsSmallImg, getzsShowImg, aLinkDownloadUtil } from '@/utils/util'
-import { getaiImagesPageAPI, cancelDownloadApi, getDownloadStatusApi, attachDownload, aiImagesPage } from '@/api/dataCenter/dataCenter'
-import { getFindAction } from '@/views/RoutePlan/PointAirLine/pointWayLineUtils'
-import { pxToRem } from '@/utils/rem'
-import JobRelatedEvents from './JobRelatedEvents.vue'
-import { getEventMediaListApi, getJobDetails, getJobInfoFiles, getJobsAllFiles, NestDetailApi } from '@/api/job/task'
-import DeviceJobDetailsMap from './DeviceJobDetailsMap.vue'
-import { droneEventList } from '../const/drc'
-import { ElMessage, ElLoading } from 'element-plus'
-import { useStore } from 'vuex'
-import EventBus from '@/utils/eventBus'
-const store = useStore()
-const htlsrwxq = computed(() => store.state.common.downloadProgress?.htlsrwxq || 100)
-const isShow = defineModel('show')
-const VideoShow = ref(false)
-const showAll = ref(false) // 是否展示全部
-let loadingData
-const fullscreenVideo = ref(null)
-const playbackVideo = ref(false)
-const carouselShow = ref(false) // 是否轮播
-const currentIndex = ref(-1)
-// 视频播放事件处理
-const handleVideoPlay = (event) => {
- if (event.target.playbackRate !== 0.75) {
- event.target.playbackRate = 0.75
-
- }
-}
-const handleVideoEnded = index => {
- // 获取当前视频
- const video = fullscreenVideo.value
-
- // 重置视频播放时间为 0
- video.currentTime = 0
-
- // 重新加载视频
- video.load()
-}
-// 原图
-const yuanImages = ref([])
-function convertVideoUrlToThumbnail (videoUrl) {
- // 检查是否是有效的视频URL
- if (!videoUrl || typeof videoUrl !== 'string') {
- return videoUrl
- }
- // 替换文件扩展名
- return videoUrl.replace(/\.mp4$/, '_small.jpg')
-}
-const infoList = ref([
- { name: '任务编号', value: '', field: 'job_info_num' },
- { name: '任务名称', value: '', field: 'name' },
- { name: '任务类型', value: '', field: 'rep_rule_type' },
- { name: '飞行事件', value: '', field: 'event_number' },
- { name: '所属机巢', value: '', field: 'device_names' },
- { name: '创建人', value: '', field: 'creator_name' },
- { name: '所属部门', value: '', field: 'dept_name' },
- { name: '任务时间', value: '', field: 'cycle_time_value' },
- { name: '任务频次', value: '', field: 'rep_rule_type' },
- { name: '关联算法', value: '', field: 'ai_type_str' },
- { name: '自定义识别区', value: '', field: 'enable_custom_area' },
- { name: '任务描述', value: '', field: 'remark' },
- // { name: '任务执行次数', value: '', field: 'job_num' },
-])
-// 机巢状态
-const flystatus = ref('')
-const detailsData = ref({
- id: null,
- remark: null,
- is_monitoring: null,
- industry_type_str: null,
- area_code: null,
- ai_type_str: null,
- enable_custom_area: null,
- begin_time: null,
- end_time: null,
- device_names: null,
- create_time: null,
- name: null,
- event_number: null,
- creator_name: null,
- way_lines: [],
-})
-// 任务成果
-const total = ref(0)
-const achievementList = ref([])
-const props = defineProps(['wayLineJobInfoId', 'waylineJobId', 'batchNo', 'jobId'])
-const wayLineJobInfoId = computed(() => props.wayLineJobInfoId)
-const jobId = computed(() => props.jobId)
-provide('wayLineJobInfoId', wayLineJobInfoId)
-provide('jobId', jobId)
-
-// 可见数量
-const visibleCount = computed(() =>
- showAll.value ? achievementList.value.length : Math.min(5, achievementList.value.length)
-)
-
-const jumpMore = () => {
- ElMessage.warning('正在加急开发中...')
-}
-
-const flightEvents = ref([])
-const jobTimes = ref([])
-const historyTime = ref('')
-const jumpDataCenter = ref(null)
-const cycle_time = ref(null)
-const detailName = ref(null)
-
-const getDetails = () => {
- const params = {
- wayLineJobInfoId: props.wayLineJobInfoId,
- // waylineJobId: props.waylineJobId,
- batchNo: props.batchNo
- }
- getJobDetails(params).then(res => {
- detailsData.value = res.data.data
- jobTimes.value = res.data.data.job_times
- jumpDataCenter.value = res.data.data
- cycle_time.value = res.data.data.cycle_time_value
- detailName.value = detailsData.value.name
-
- infoList.value.forEach(item => {
- item.value = detailsData.value?.[item.field] || '--'
- if (item.name === '任务频次') {
- item.value = detailsData?.value.rep_rule_type ? detailsData?.value.final_cycle_frequency : '1次'
- }
- if (item.name === '任务类型') {
- const { rep_rule_type = '' } = detailsData?.value || {}
- item.value = rep_rule_type ? '周期任务' : '临时任务'
- }
- if (item.name === '飞行事件') {
- const { action_modes = [] } = detailsData?.value || {}
- flightEvents.value = action_modes.flatMap(getFindAction)
- }
- if (item.name === '自定义识别区' && infoList?.value[9].value !== '--') {
- item.value = res.data.data.enable_custom_area ? '是' : '否'
- }
- })
- getJobsAllFiles({
- wayLineJobId: detailsData.value.way_lines.map(item => item.job_id).join(','),
- resultTypes: [0, 1, 2, 4, 5,15],
- orderByCreateTime: true,
- }).then(result => {
- if (result.data.code !== 200) return
- achievementList.value = result.data.data.records.map(i => ({
- ...i,
- checked: false,
- smallUrl: i.resultType === 4 ? getzsSmallImg(i.link) : getSmallImg(i.link),
- showUrl: i.resultType === 4 ? getzsShowImg(i.link) : getShowImg(i.link),
- }))
- total.value = result.data.data.total
-
- yuanImages.value = result.data.data.records.filter(item => item.resultType !== 1)
- })
- })
-}
-// 播放
-const videoRef = ref(null)
-const currentVideoIndex = ref(-1)
-
-const enterFullScreen = (index) => {
- currentVideoIndex.value = index
- VideoShow.value = true
-}
-// 计算当前视频URL
-const currentVideoUrl = computed(() => {
- return achievementList.value[currentVideoIndex.value]?.link || ''
-})
-// 关闭弹窗处理
-const handleClose = () => {
- if (fullscreenVideo.value) {
- fullscreenVideo.value.pause()
- fullscreenVideo.value.currentTime = 0
- }
-}
-// 下载
-const downloadFun = async () => {
- const list = achievementList.value.filter(i => i.checked)
- console.log('list', list)
- if (!list?.length) return ElMessage.warning('请选择文件')
- if (list.length === 1) {
- list.forEach((item, index) => {
- const suffix = item.link.split('.').pop()
- aLinkDownloadUtil(item.link, item.nickName + '.' + suffix)
- })
- } else {
- loadingData = ElLoading.service({ background: 'rgba(0, 0, 0, 0.5)', text: '打包中,请稍等...' })
- const fileIds = list.map(i => i.id)
- const res = await getDownloadStatusApi({ type: 'htlsrwxq' })
- if (!['CANCELLED', 'COMPLETED'].includes(res.data.data?.status || 'COMPLETED')) {
- return ElMessage.warning('还有正在处理的')
- }
- attachDownload({ attachIds: fileIds, type: 'htlsrwxq' }).finally(() => {
- loadingData.close()
- })
- }
-}
-// 全景预览
-const panoramaParamsShow = ref(false)
-const panoramaParamsUrl = ref(null)
-const clickpanorama = val => {
- panoramaParamsShow.value = true
- panoramaParamsUrl.value = val.link
-}
-// 从地图点击图片预览
-const showViewer = ref(false)
-const activeIndex = ref(0)
-const previewUrls = ref([])
-const showImageeclick = (list, index, categoriestype) => {
- if (categoriestype === 5) {
- panoramaParamsShow.value = true
- panoramaParamsUrl.value = list[0]
- } else {
- previewUrls.value = list
- activeIndex.value = 0
- showViewer.value = true
- }
-}
-// 取消下载
-function cancelDownload () {
- cancelDownloadApi({ type: 'htlsrwxq' }).then(res => {
- ElMessage.success('取消成功')
- }).catch(e => {
- EventBus.emit('useGlobalWS-messageHandler', { biz_code: 'DOWNLOAD_PROGRESS', data: { status: 'CANCELLED', type: 'htlsrwxq' } })
- })
-}
-
-// 执行机巢详情
-const taskData = ref([])
-const getNestDetailApi = () => {
- const params = {
- jobInfoId: props.wayLineJobInfoId,
- batchNo: props.batchNo,
- }
- NestDetailApi(params).then(res => {
- taskData.value = res.data.data
- })
-}
-
-// 获取回放直播列表
-const videoData = ref([])
-const openVideoDetail = ref({})
-const getVideoList = () => {
- getWaylinejobLiveRecordPage({ job_info_id: props.wayLineJobInfoId }).then(videoDetails => {
- videoData.value = videoDetails.data.data.records
- })
-}
-
-const openPlaybackVideoDialog = data => {
- playbackVideo.value = true
- openVideoDetail.value = data
-}
-
-
-
-function clickPlay(index) {
- carouselShow.value = true
- currentIndex.value = index
-}
-
-onMounted(() => {
- getDetails()
- getNestDetailApi()
- getVideoList()
-})
-</script>
-
-<style lang="scss">
-.detailsOfHistoricalTasks {
- .el-dialog {
- --el-dialog-margin-top: 5vh;
- }
-
- .el-pagination {
- justify-content: center;
- padding: 20px;
- }
-
- .el-dialog__body {
- height: 80vh;
- }
-}
-</style>
-
-<style lang="scss" scoped>
-.content {
- display: flex;
- height: 100%;
-
- .contentLeft {
- margin-left: 35px;
- margin-right: 24px;
- width: 1150px;
- overflow: auto;
-
- .detailsLine {
- display: flex;
- align-items: center;
- border-bottom: 2px solid #e4e7ed;
- padding-bottom: 4px;
- img {
- width: 15px;
- height: 15px;
- }
- .title {
- margin-left: 10px;
- font-size: 16px;
- color: #303133;
- }
- .btn {
- margin-left: 16px;
- }
- }
-
- .devicetitle {
- margin-bottom: 16px;
- // background: url('/src/assets/images/task/detailtitle.png') no-repeat center;
- border-bottom: 2px solid #e4e7ed;
- background-size: 100% 100%;
- display: flex;
- justify-content: space-between;
- align-content: center;
-
- .rightBox {
- display: flex;
- align-items: center;
- padding-right: 10px;
- gap: 0 10px;
-
- .downloadBtn {
- position: relative;
- display: flex;
- align-items: center;
- justify-content: center;
- height: 28px;
- padding: 0 15px;
- background: rgba(28, 92, 255, 0.14);
- border-radius: 4px 4px 4px 4px;
- border: 1px solid #1c5cff;
- cursor: pointer;
- color: #1C5CFF;
-
- .downloadBtnText {
- display: flex;
- align-items: center;
- gap: 5px;
- z-index: 5;
- }
-
- .el-progress {
- position: absolute;
- left: 0;
- top: 0;
- width: 100%;
-
- :deep() {
- .el-progress-bar__outer {
- height: 28px !important;
- border-radius: 0 !important;
- background: transparent !important;
-
- .el-progress-bar__inner {
- border-radius: 0 !important;
- }
- }
- }
- }
-
- }
- }
-
- img {
- width: 15px;
- height: 15px;
- }
-
- p {
- display: inline-block;
- margin-left: 10px;
- font-size: 16px;
- color: #363636;
- line-height: 20px;
- text-align: left;
- margin-bottom: 8px;
-
- span {
- font-size: 26px;
- color: #0282ff;
- font-weight: bold;
- }
- }
-
- .more {
- color: #2f9dff;
- font-size: 14px;
- padding-top: 5px;
- cursor: pointer;
- }
- }
-
- .infoBox {
- display: flex;
- justify-content: space-between;
-
- .itemBoxLeft {
- flex: 1;
- display: grid;
- grid-template-columns: repeat(2, 1fr);
- column-gap: 10px;
- /* 只设置列间距 */
- row-gap: 0;
- /* 取消行间距 */
- padding: 10px;
- font-size: 14px;
-
- .itemBox {
- display: flex;
- align-items: center;
- background: #fff;
- height: 43px;
- border-top: 2px solid #EFEFEF;
-
- }
-
- }
-
- .itemCon:nth-last-child(-n+2) {
- border-bottom: 2px solid #EFEFEF;
- }
-
- .itemBox:nth-last-child(-n+2) .itemTitle {
- border-bottom: 1px solid #EFEFEF;
- }
-
- .itemTitle {
- color: #0B1D38;
- width: 26%;
- text-align: right;
- background: #F3F6FF;
- height: 43px;
- line-height: 43px;
- padding-right: 10px;
- border-top: 1px solid #EFEFEF;
- }
-
- .itemValue {
- font-size: 14px;
- color: #747e91;
- text-align: center;
- width: 74%;
-
- }
-
- .flightEvents {
- display: flex;
- flex-wrap: wrap;
- gap: 10px 10px;
- width: 74%;
- justify-content: center;
-
- img {
- width: 30px;
- height: 30px;
- }
- }
-
- }
-
- .imgListBox {
- display: grid;
- grid-template-columns: repeat(5, 1fr);
- gap: 10px;
- margin-bottom: 49px;
-
- >* {
- width: 100%;
- height: 200px;
- position: relative;
- border-radius: 0px 0px 4px 4px;
- overflow: hidden;
- }
-
- :deep(.el-checkbox__inner) {
- width: 17px !important;
- height: 17px !important;
- background: rgba(37, 36, 36, 0.63) !important;
- border-radius: 4px 4px 4px 4px;
- border: 1px solid #ffffff !important;
- }
-
- .el-checkbox {
- position: absolute;
- top: 0;
- left: 6px;
- }
-
- :deep(.el-checkbox__inner:after) {
- position: absolute;
- left: 50% !important;
- top: 40% !important;
- transform: translate(-50%, -50%) rotate(45deg) !important;
- }
-
- :deep(.el-checkbox.is-checked .el-checkbox__inner) {
- background-color: #09297b !important;
- border-color: #1c5cff !important;
- }
-
- .el-image {
- width: 100%;
- height: 100%;
- cursor: pointer;
- position: relative;
- }
-
- .quanjing {
- width: 100%;
- height: 100%;
- object-fit: cover;
- cursor: pointer;
- position: relative;
- }
-
- .videotime {
- width: 100%;
- height: 100%;
- position: relative;
-
- .videoDisplay {
- width: 100%;
- height: 100%;
- object-fit: cover;
- }
- }
-
- .itemName {
- position: absolute;
- bottom: 0;
- left: 0;
- height: 23px;
- width: 100%;
- background: rgba(22, 22, 22, 0.68);
- border-radius: 0px 0px 4px 4px;
- font-family: Source Han Sans CN, Source Han Sans CN;
- font-weight: 400;
- font-size: 14px;
- color: #ffffff;
- line-height: 23px;
- z-index: 9;
- padding: 0 6px;
- }
- }
-
- .videobutton {
- position: absolute;
- top: 40%;
- left: 50%;
- transform: translate(-50%);
- cursor: pointer;
- width: 40px;
- height: 40px;
- display: inline-block;
- background: #999;
- border-radius: 50%;
- }
-
- .videoDisplay {
- display: flex;
- flex-wrap: wrap;
- width: 200px;
- height: 200px;
-
- }
-
- .videotime {
- position: relative;
- }
-
- .cg-empty {
- text-align: center;
- color: #909399;
- }
-
- }
-
- .content-right {
- position: relative;
- width: 0;
- flex-grow: 1;
- background: #19ad8d;
- margin-right: 17px;
- overflow: hidden;
-
- .content-map-popups {
- pointer-events: none;
-
- >* {
- pointer-events: auto !important;
- }
- }
- }
-}
-</style>
diff --git a/applications/drone-command/src/views/job/components/DeviceJobDetailsMap.vue b/applications/drone-command/src/views/job/components/DeviceJobDetailsMap.vue
deleted file mode 100644
index c22b3c9..0000000
--- a/applications/drone-command/src/views/job/components/DeviceJobDetailsMap.vue
+++ /dev/null
@@ -1,353 +0,0 @@
-<template>
- <div id="deviceJobDetailsMap" class="command-cesium"></div>
-</template>
-<script setup>
-import { flyVisual } from '@ztzf/utils'
-import _ from 'lodash'
-
-import * as Cesium from 'cesium'
-import { PublicCesium } from '@/utils/cesium/publicCesium'
-import photoDirection from '@/assets/images/historyMapPopup/photoDirection-new.png'
-import { getEventImage } from '@/utils/stateToImageMap/event'
-import panoramaPoint from '@/assets/images/panorama/panorama-point.png' //全景图标
-import { getShowImg } from '@/utils/util'
-import { getPanoramaList } from '@/api/panorama'
-import CreateRouteLine from '@/utils/cesium/createRouteLine'
-import { useStore } from 'vuex'
-import { getOdmToken } from '@/api/model'
-
-const createRouteLineExample = new CreateRouteLine()
-const store = useStore()
-const emit = defineEmits(['showImageeclick'])
-const props = defineProps(['detailsData', 'yuanImages', 'jobId','taskData'])
-const userAreaCode = computed(() => store.state.user.userInfo.detail.areaCode)
-let viewer = null
-let publicCesiumInstance = null
-const viewInstance = shallowRef(null)
-import { cloneDeep } from 'lodash'
-import { useIdentificationArea } from '@ztzf/hooks'
-let handler = null
-let curMoveEntity = null
-const tokenStr = ref('')
-let tipsLabel = null
-
-const initMap = () => {
- publicCesiumInstance = new PublicCesium({
- dom: 'deviceJobDetailsMap',
- terrain: true,
- flatMode: false,
- layerMode: 4,
- })
-
- viewer = publicCesiumInstance.getViewer()
- viewInstance.value = publicCesiumInstance
- viewer.scene.globe.depthTestAgainstTerrain = true
-
- createRouteLineExample.initCreateRoute(viewer)
-
- handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
- handler.setInputAction(click => {
- const pickedObjects = viewer.scene.drillPick(click.position)
- LeftClickEvent(click, pickedObjects, viewer)
- }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
-
- handler.setInputAction(e => {
- let pick = viewer.scene.drillPick(e.endPosition)
- mouseMoveEvent(e, pick, viewer)
- }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
-}
-
-function mouseMoveEvent(click, pick, viewer) {
- let startEntity = pick?.find(
- i => i.id.name === 'work-drone-route-planar-polyline' || i.id.name === 'work-drone-route-point-polyline'
- )?.id
- const position = viewer.scene.pickPosition(click.endPosition)
- const device_sn = startEntity?.customData?.data?.device_sn
- const lineBoxShow = createRouteLineExample.detailsMapPopupData?.find(i => i.device_sn === device_sn)?.show
- if (startEntity && !lineBoxShow) {
- if (!tipsLabel){
- tipsLabel = viewer.entities.add({
- position: position,
- label: {
- text: `点击显示详情`,
- font: '14px sans-serif',
- showBackground: true,
- backgroundColor: new Cesium.Color(0.165, 0.165, 0.165, 0.8),
- pixelOffset: new Cesium.Cartesian2(0, -20),
- eyeOffset: new Cesium.Cartesian3(0, 0, -10), // 使标签在点的上方
- disableDepthTestDistance: Number.POSITIVE_INFINITY, // 避免被遮挡
- }
- })
- return
- }
- tipsLabel.position = position
- }else{
- tipsLabel && viewer.entities.remove(tipsLabel)
- tipsLabel = null
- }
-}
-
-// 绘制线和飞行
-const drawLine = async () => {
- Promise.all(
- props.detailsData.way_lines.map(
- async item =>
- await createRouteLineExample.parsingFiles(
- {
- ...item,
- url: `${item.url}?_t=${new Date().getTime()}`,
- status: props.taskData.length > 1 ? props.taskData.filter(i => i.dock_sn === item.device_sn)[0].status : ''
- },
- {
- longitude: item.longitude,
- latitude: item.latitude,
- height: item.height,
- },
- true,
- false
- )
- )
- ).then(res => {
- createRouteLineExample.showDetailMapPopup(props.detailsData.way_lines)
-
- let arr = res.map(i => i.filePositions)
-
- let newArr = _.flatten(arr)
-
- flyVisual({
- positionsData: newArr.map(i => [i.lng, i.lat, i.alt]),
- viewer,
- pitch: -60,
- })
- })
-}
-
-const lastSelectedId = ref(null)
-// 鼠标左键点击事件
-const LeftClickEvent = (click, pick, viewer) => {
- // pick是一个数组
- if (pick && pick.length > 0) {
- let startEntity = pick.find(
- i => i.id.name === 'work-drone-route-planar-polyline' || i.id.name === 'work-drone-route-point-polyline'
- )?.id
-
- if (startEntity) {
- createRouteLineExample.showSingleDetailMapPopup(startEntity?.customData?.data)
- tipsLabel && viewer.entities.remove(tipsLabel)
- tipsLabel = null
- return
- }
- let imagePath = []
- let categories = ''
- let panoramic = ''
- pick.forEach(item => {
- const index = item.id.index
- if (item.id.customType == 'spotImages') {
- imagePath.push(item.id.customInfo.link)
- } else if (item.id.customType == 'aggregationSplashed') {
- imagePath.push(item.id.customInfo.link)
- } else if (item.id.customType == 'panorama') {
- //全景
- imagePath.push(item.id.customInfo.link)
- categories = item.id.customInfo.resultType
- }
- })
- if (imagePath.length === 0) return
- const finalImagePaths = categories === 5 ? imagePath : imagePath.map(i => getShowImg(i))
- emit('showImageeclick', finalImagePaths, 0, categories)
- }
-}
-
-const removeMap = () => {
- viewer.entities.removeAll()
- publicCesiumInstance?.viewerDestroy()
- publicCesiumInstance = null
- viewer = null
-}
-const {setArea,removeArea } = useIdentificationArea()
-watch(
- () => props.detailsData,
- async (newValue, oldValue) => {
- if (newValue) {
- await drawLine()
- }
- if (newValue?.ai_types && newValue.enable_custom_area){
- setArea(newValue.ai_types.split(','),viewer)
- }
- }
-)
-const zsList = ref([])
-// 获取odmtoken
-function getOdmTokenStr() {
- getOdmToken().then(res => {
- if (res.data.code !== 200) return
- tokenStr.value = res.data.data
- })
-}
-// 获取正射列表
-function getZSList(newVal) {
- let list = []
- newVal.map((item, index) => {
- item.childrens.map(zschilditem => {
- if (zschilditem?.data) {
- zschilditem?.data.map(dataitem => {
- list.push(dataitem)
- })
- }
- })
- })
- return list
-}
-// 获取正射请求地图聚合数据
-const zsMapList = ref([])
-function getZSMapList(valSn) {
- const params = {
- areaCode: userAreaCode.value,
- deviceSn: valSn,
- resultType: 4,
- wayLineJobId: props.jobId,
- }
- getPanoramaList(params).then(res => {
- const resData = res?.data?.data
- zsMapList.value = resData?.childrens || []
- zsList.value = getZSList(zsMapList.value)
-
- renderOrthophoto()
- })
-}
-
-// 渲染正射
-function renderOrthophoto() {
- zsList.value.forEach((item, index) => {
- const cur = viewInstance.value.addCustomImageryProviderDataSource(
- new Cesium.UrlTemplateImageryProvider({
- url: `${import.meta.env.VITE_APP_AREA_NAME}/webodm${item.url}?jwt=${tokenStr.value.split(' ')[1]}`,
- })
- )
- })
-}
-
-watch(
- () => props.yuanImages,
- (newValue, oldValue) => {
- // 增加箭头
- if (newValue.length === 0) return
- let eventArr = newValue.filter(i => i.resultType === 2)
- let originalArr = newValue.filter(i => i.resultType === 0)
- let panoramicArr = newValue.filter(i => i.resultType === 5)
- let orthographicArr = newValue.filter(i => i.resultType === 4)
- if (orthographicArr) {
- getZSMapList(orthographicArr[0]?.deviceSn)
- }
- originalArr.forEach((item_in, index) => {
- // 根据偏航角度展示图片拍摄的方向
- let yawAngleDegrees =
- item_in.metadata.gimbalYawDegree < 0
- ? 360 + Number(item_in.metadata.gimbalYawDegree)
- : Number(item_in.metadata.gimbalYawDegree)
-
- viewer.entities.add({
- position: Cesium.Cartesian3.fromDegrees(
- +item_in.metadata.shootPosition.lng,
- +item_in.metadata.shootPosition.lat,
- +item_in.metadata.absoluteAltitude
- ),
- id: 'spotImages' + item_in.id,
- customType: 'spotImages',
- customInfo: {
- ...item_in,
- },
- index: index,
- billboard: {
- image: photoDirection,
- outlineWidth: 0,
- width: 14,
- height: 21,
- disableDepthTestDistance: Number.POSITIVE_INFINITY,
- verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
- },
- })
- })
-
- eventArr.forEach((item, index) => {
- viewer.entities.add({
- id: `aggregationSplashed${item.id}`,
- customType: 'aggregationSplashed',
- customInfo: {
- ...item,
- },
- position: Cesium.Cartesian3.fromDegrees(
- +item.metadata.shootPosition.lng,
- +item.metadata.shootPosition.lat,
- +item.metadata.absoluteAltitude
- ),
- billboard: {
- image: getEventImage(item.status),
- width: 30,
- height: 30,
- disableDepthTestDistance: Number.POSITIVE_INFINITY,
- },
- index: index,
- properties: {
- customData: {
- data: item,
- },
- },
- })
- })
- // 全景
- panoramicArr.forEach((item, index) => {
- viewer.entities.add({
- id: `panorama${item.id}`,
- customType: 'panorama',
- customInfo: {
- ...item,
- },
- position: Cesium.Cartesian3.fromDegrees(
- +item.metadata.shootPosition.lng,
- +item.metadata.shootPosition.lat,
- +item.metadata.absoluteAltitude
- ),
- billboard: {
- image: panoramaPoint,
- width: 30,
- height: 30,
- disableDepthTestDistance: Number.POSITIVE_INFINITY,
- },
- index: index,
- properties: {
- customData: {
- data: item,
- },
- },
- })
- })
- },
- { immediate: true, deep: true }
-)
-
-onBeforeUnmount(() => {
- // 移除事件监听
- handler?.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
- handler?.destroy()
- handler = null
-
- createRouteLineExample.destroyedCustomDom()
-
- removeMap()
-})
-
-onMounted(() => {
- getOdmTokenStr()
- nextTick(() => {
- initMap()
- })
-
-})
-</script>
-<style scoped lang="scss">
-#deviceJobDetailsMap {
- position: relative;
- height: 100%;
-}
-</style>
diff --git a/applications/drone-command/src/views/job/components/FileTypePlay.vue b/applications/drone-command/src/views/job/components/FileTypePlay.vue
deleted file mode 100644
index 517ac37..0000000
--- a/applications/drone-command/src/views/job/components/FileTypePlay.vue
+++ /dev/null
@@ -1,158 +0,0 @@
-<template>
- <!-- <div class="file-type-play">-->
- <el-image-viewer
- v-if="isShow"
- ref="preview"
- :url-list="playList"
- :initial-index="currentActiveIndex"
- @close="closeImageViewer"
- @switch="onViewerSwitch"
- teleported>
- </el-image-viewer>
-</template>
-
-<script setup>
-import { getShowImg } from '@/utils/util'
-
-const currentActiveIndex = ref(0)
-
-const isShow = defineModel('show', {
- type: Boolean,
- default: true,
-})
-
-const props = defineProps(['list','cIndex'])
-
-const playList = ref([])
-
-function getQJUrl(url) {
- return `https://wrj.shuixiongit.com/dronePanorama/html/simple-index.html?path=${getShowImg(url)}`
-}
-
-function closeImageViewer() {
- isShow.value = false
-}
-
-function onViewerSwitch (index) {
- currentActiveIndex.value = index
- videoImageSwitch()
-}
-
-const videoImageSwitch = () => {
- nextTick(() => {
- const viewerCanvas = document.querySelector('.el-image-viewer__canvas');
- const viewerActions = document.querySelector('.el-image-viewer__actions');
- const parentDOM = document.querySelector('.el-image-viewer__wrapper');
-
- // const maskLayer = document.querySelector('.el-image-viewer__mask'); // 获取遮罩层
-
- // 清理之前可能存在的视频或全景图元素
- const existingVideo = parentDOM?.querySelector('.preview-video');
- const existingIframe = parentDOM?.querySelector('.preview-panorama');
-
- if (existingVideo) existingVideo.remove();
- if (existingIframe) existingIframe.remove();
-
- const item = props.list[currentActiveIndex.value];
-
- if (item?.resultType === 1) {
- // ========== 处理视频 ==========
- // 隐藏图片查看器的UI
- if (viewerActions) viewerActions.style.display = 'none';
- if (viewerCanvas) viewerCanvas.style.display = 'none';
-
- // 创建并添加视频元素
- const videoDom = document.createElement('video');
- videoDom.className = 'preview-video';
- // videoDom.autoplay = true;
- videoDom.muted = true;
- videoDom.playsInline = true;
- videoDom.loop = true;
- videoDom.controls = true;
- videoDom.preload = 'auto';
- videoDom.src = item.object_key || item.showUrl;
-
- videoDom.style.position = 'relative';
- // 添加视频样式
- videoDom.style.width = '100%';
- videoDom.style.height = '100%';
- videoDom.style.objectFit = 'contain';
-
- // 添加事件监听器
- videoDom.addEventListener('play', (event) => {
- // 设置播放速度为 0.75
- if (event.target.playbackRate !== 0.75) {
- event.target.playbackRate = 0.75;
- }
- });
-
- videoDom.addEventListener('ended', () => {
- // 重置视频播放时间为 0 并重新加载
- videoDom.currentTime = 0;
- videoDom.load();
- });
-
- if (parentDOM) {
- parentDOM.appendChild(videoDom);
- }
- } else if (item?.resultType === 5) {
- // ========== 处理全景图 ==========
- // 隐藏图片查看器的UI(如果全景图不需要查看器UI)
- // if (maskLayer) maskLayer.style.display = 'none';
- if (viewerActions) viewerActions.style.display = 'none';
- if (viewerCanvas) viewerCanvas.style.display = 'none';
-
- // 创建并添加iframe元素
- const iframeDom = document.createElement('iframe');
- iframeDom.className = 'preview-panorama';
- iframeDom.src = getQJUrl(item?.link); // 使用你的全景图URL生成函数
- iframeDom.frameBorder = '0'; // 注意:frameBorder 是 camelCase
- iframeDom.allow = 'fullscreen'; // 允许全屏,如果需要
- iframeDom.title = '全景图浏览';
-
- // 添加iframe样式
- // iframeDom.style.zIndex = '2000'; // 比遮罩层的 2000 高
- iframeDom.style.position = 'relative';
- iframeDom.style.width = '100%';
- iframeDom.style.height = '100%';
- iframeDom.style.border = 'none';
-
- // 如果需要,可以设置iframe的加载策略
- iframeDom.loading = 'eager';
-
- if (parentDOM) {
- parentDOM.appendChild(iframeDom);
- }
- } else {
- // ========== 处理图片(或其他类型) ==========
- // 显示图片查看器的UI
- if (viewerCanvas) viewerCanvas.style.display = 'flex';
- if (viewerActions) viewerActions.style.display = 'flex';
- }
- });
-};
-
-watch(isShow, async newVal => {
- if (newVal) {
- playList.value = props.list.map((item) => item.showUrl)
- currentActiveIndex.value = props.cIndex
- videoImageSwitch()
-
- }
-})
-</script>
-<style lang="scss">
-.el-image-viewer__wrapper {
- .el-image-viewer__canvas {
- img {
- height: 90%;
- top: 5%;
- }
- }
-
- .preview-video {
- max-height: 96%;
- }
-}
-
-</style>
\ No newline at end of file
diff --git a/applications/drone-command/src/views/job/components/JobRelatedEvents.vue b/applications/drone-command/src/views/job/components/JobRelatedEvents.vue
deleted file mode 100644
index 7296230..0000000
--- a/applications/drone-command/src/views/job/components/JobRelatedEvents.vue
+++ /dev/null
@@ -1,392 +0,0 @@
-<template>
- <div class="JobRelatedEvents">
- <div class="machineTableDetailsTitle">
- <img src="/src/assets/images/task/sign.svg" alt="">
- <div class="title">
- 关联事件<span>{{ total }}</span>件
- </div>
- <div class="exportBtn" v-if="total > 0 ">
- <el-button
- :loading="isShowExportTextLoading"
- :disabled="isShowExportTextLoading"
- class="playback-video-btn" type="primary" @click="exportTheTask" size="small">
- {{ isShowExportTextLoading ? '导出中' : '导出报告' }}
- </el-button>
- </div>
- </div>
- <div class="search">
- <div class="searchBox">
- <div class="item">
- <div class="itemchild">模糊查询:</div>
- <el-input v-model="params.event_name" placeholder="请输入事件名称" clearable></el-input>
- </div>
- <!-- <div class="item">
- <div class="itemchild">任务执行日期:</div>
- <el-date-picker
- popper-class="custom-date-picker"
- class="tasktimer"
- v-model="taskData"
- type="date"
- placeholder="请选择日期"
- value-format="YYYY-MM-DD"
- :disabled-date="disabledDate"
- @change="changeselect"
- />
- </div> -->
- <div class="item">
- <div class="itemchild">关联算法:</div>
- <TaskAlgorithmBusiness ref="algorithmRef" :setWidth="186" :showAlgorithm="true" @algorithmChange="algorithmChange" />
- </div>
- </div>
- <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="command-table">
- <el-table
- v-loading="loading"
- :row-class-name="tableRowClassName"
- :data="list"
- style="width: 100%"
- :row-style="{ height: pxToRem(48), fontSize: pxToRem(14), 'text-align': 'center' }"
- :header-cell-style="{ 'text-align': 'center', height: pxToRem(36), fontSize: pxToRem(14) }"
- >
- <el-table-column label="序号" type="index" :width="pxToRemNum(60)">
- <template #default="{ $index }">
- {{ ($index + 1 + (sizeParams.current - 1) * sizeParams.size).toString().padStart(2, '0') }}
- </template>
- </el-table-column>
- <el-table-column show-overflow-tooltip prop="event_num" label="事件编号" :width="pxToRemNum(160)">
- <template #default="scope">
- <el-tooltip-copy :content="scope.row.event_num" :showCopyText="true">
- {{ scope.row.event_num }}
- </el-tooltip-copy>
- </template>
- </el-table-column>
- <el-table-column show-overflow-tooltip prop="event_name" label="事件名称" align="center"/>
- <el-table-column show-overflow-tooltip prop="remark" label="事件地址" align="center"/>
- <el-table-column show-overflow-tooltip prop="ai_types" label="关联算法" align="center"/>
- <el-table-column show-overflow-tooltip prop="work_type" label="事件来源" align="center">
- <template #default="scope">
- {{scope.row.work_type === '0' ? '巡检任务' :
- scope.row.work_type === '1' ? '人工上报' :
- scope.row.work_type === '2' ? '识别分析' : '' }}
- </template>
- </el-table-column>
- <el-table-column show-overflow-tooltip prop="create_time" label="事件时间" align="center"/>
- <el-table-column prop="status" label="事件状态" align="center">
- <template #default="scope">
- <div class="pending" v-if="scope.row.status === 0">待处理</div>
- <div class="reviewed" v-if="scope.row.status === 2">待审核</div>
- <div class="processing" v-if="scope.row.status === 3">处理中</div>
- <div class="done" v-if="scope.row.status === 4">已完成</div>
- <div class="ended" v-if="scope.row.status === 5">已完结</div>
- </template>
- </el-table-column>
- <el-table-column label="操作" :width="pxToRemNum(150)">
- <template #default="scope">
- <div class="btnGroups">
- <el-popover
- v-if="scope.row.status === 3 || scope.row.status === 0"
- popper-class="command-custom-qrcode-popover"
- :width="pxToRem(119)"
- :visible="scope.row.showQR"
- placement="top"
- title=""
- trigger="click"
- >
- <template #reference>
- <el-button type="text" @click="QRCode(scope.row)">事件导航</el-button>
- </template>
- <div class="qrcode-content">
- <div class="close-btn" @click.stop="scope.row.showQR = false">×</div>
- <CreateQRcode v-if="scope.row.showQR" :latAndLon="latAndLon"></CreateQRcode>
- </div>
- </el-popover>
- <el-button icon="el-icon-view" type="text" @click="examine(scope.row)">查看</el-button>
- </div>
- </template>
- </el-table-column>
- </el-table>
- </div>
- <el-pagination v-if="list.length > 0"
- class="command-pagination"
- popper-class="custom-pagination-dropdown"
- background
- :page-sizes="[10, 20, 30, 50]"
- v-model:current-page="sizeParams.current"
- v-model:page-size="sizeParams.size"
- layout=" prev, pager, next,sizes, jumper"
- :total="total"
- @change="pageChange"
- />
- </div>
-</template>
-<script setup>
-import { useStore } from 'vuex'
-import { wgs84ToGcj02 } from '@/utils/coordinateTransformation'
-import CreateQRcode from '@/components/CreateQRcode/CreateQRcode.vue'
-import { getDeviceEventList } from '@/api/job/task'
-import { dayjs, ElMessage } from 'element-plus'
-import { useRouter } from 'vue-router';
-import { NestDetailApi, exportTaskReports } from '@/api/home'
-import { pxToRem, pxToRemNum } from '@/utils/rem'
-import TaskAlgorithmBusiness from './TaskAlgorithmBusiness.vue'
-const router = useRouter();
-const emit = defineEmits(['refresh'])
-const props = defineProps(['jobTimes', 'batchNo', 'waylineJobId','detailName'])
-const taskData = ref('')
-const algorithmRef = ref()
-
-const algorithmChange = val => {
- params.value.ai_types = val
- handleSearch()
-}
-const loading = ref(true)
-const list = ref()
-const params = ref({
- way_line_job_info_id: null,
- event_name: '',
- ai_types: [], // 算法类型
- start_date: null,
- end_date: null,
-})
-const sizeParams = ref({
- current: 1,
- size: 5,
-})
-// 日期选择
-const changeselect = () => {
- params.value.start_date = taskData.value.length ? `${taskData.value} 00:00:00` : null
- params.value.end_date = taskData.value.length ? `${taskData.value} 23:59:59` : null
-}
-const store = useStore()
-const child_sn = computed(() => store.state.home.singleUavHome.child_sn)
-const total = ref(0)
-const examine = row => {
- const orderNumber = row.event_num
- router.push({
- path: `/tickets/ticket`,
- query: {
- orderNumber,
- },
- });
-}
-const wayLineJobInfoId = inject('wayLineJobInfoId')
-
-const getList = () => {
- if (!wayLineJobInfoId.value) return
- params.value.way_line_job_info_id = wayLineJobInfoId.value
- params.value.batch_no = props.batchNo
- loading.value = true
- getDeviceEventList(
- {
- ...params.value,
- }, sizeParams.value
- ).then(res => {
- const resData = res?.data?.data || {}
- list.value = resData.records.map(item => ({
- ...item,
- showQR: false, // 为每条数据添加showQR状态
- }))
- total.value = resData.total
- loading.value = false
- // store.commit('setJobEventList', list.value)
- })
-}
-
-const pageChange = val => {
- sizeParams.value.current = val
- getList()
-}
-// 表格隔行变色
-const tableRowClassName = ({ row, rowIndex }) => {
- if (rowIndex % 2 === 1) {
- return 'warning-row'
- } else {
- return 'success-row'
- }
-}
-// 禁用当天之前的日期
-const disabledDate = time => {
- const curTime = dayjs(time).format('YYYY-MM-DD')
- // console.log(curTime)
- return !(props.jobTimes || []).includes(curTime)
-}
-const handleReset = () => {
- params.value.ai_types = []
- params.value.event_name = ''
- params.value.end_date = null
- params.value.start_date = null
- taskData.value = ''
- algorithmRef.value?.handleClear()
- algorithmChange([])
- getList()
-}
-const handleSearch = () => {
- getList()
-}
-// 二维码
-const latAndLon = ref(null)
-const QRCode = item => {
- const [gcjLng, gcjLat] = wgs84ToGcj02(Number(item.longitude), Number(item.latitude))
- latAndLon.value = `${gcjLng},${gcjLat}`
- list.value.forEach(i => {
- i.showQR = i.id === item.id ? !i.showQR : false
- })
-}
-// 导出任务报表
-const isShowExportTextLoading = ref(false)
-const exportTheTask = () => {
- isShowExportTextLoading.value = true
- const params = {
- wayLineJobInfoId: wayLineJobInfoId.value,
- waylineJobId: props.waylineJobId,
- batchNo: props.batchNo,
- }
- exportTaskReports(params).then(res => {
- const elink = document.createElement('a')
- elink.download = props.detailName + '.docx'
- elink.style.display = 'none'
- const blob = new Blob([res.data])
- elink.href = URL.createObjectURL(blob)
- document.body.appendChild(elink)
- elink.click()
- document.body.removeChild(elink)
- isShowExportTextLoading.value = false
- })
-}
-onMounted(() => {
- getList()
-})
-</script>
-<style scoped lang="scss">
-.JobRelatedEvents {
- .machineTableDetailsTitle {
- display: flex;
- border-bottom: 2px solid #e4e7ed;
- margin-bottom: 16px;
- align-content: center;
- .exportBtn {
- margin-left: 16px;
- }
- img {
- width: 15px;
- height: 15px;
- }
- .title {
- margin-left: 10px;
- font-size: 16px;
- color: #363636;
-
- span {
- font-size: 26px;
- color: #0282ff;
- font-weight: bold;
- margin: 0 4px
- }
- }
- }
-
-// 待处理
-.pending {
- color: #ff7411;
-}
-// 待审核
-.reviewed {
- color: #ff472f;
-}
-// 处理中
-.processing {
- color: #FFC300;
-}
-// 已完成
-.done {
- color: #06d957;
-}
-// 已完结
-.ended {
- color: #06d957;
-}
-.search {
- display: flex;
- justify-content: space-between;
- margin-bottom: 19px; // 添加底部间距
- .item {
- position: relative;
- display: flex;
- align-items: center;
- font-size: 14px;
- color: #363636;
- margin-right: 20px;
- .itemchild {
- white-space: nowrap;
- margin-right: 5px;
- }
- :deep(.el-date-editor.el-input__wrapper) {
- width: 200px; // 调整日期选择器宽度
- }
- }
- .item:nth-last-child {
- margin-right: 113px;
- }
-}
-.searchBox {
- display: flex;
-}
-
-.search-btn {
- display: flex;
- justify-content: space-between;
- font-size: 14px;
-
- .btn {
- width: 80px;
- height: 32px;
- border-radius: 4px 4px 4px 4px;
- line-height: 32px;
- display: flex;
- justify-content: center;
- align-items: center;
-
- img {
- width: 14px;
- height: 14px;
- }
- }
- .search {
- background: #409EFF;
- color: #fff;
- margin-right: 10px;
- cursor: pointer;
- }
- .clear {
- border: 1px solid #D2D1D1;
- color: #676767;
- margin-right: 10px;
- cursor: pointer;
- }
- .add {
- background: #FF9243;
- color: #fff;
- }
- }
- .btnItem {
- height: 27px;
- line-height: 27px;
- border-radius: 0px 0px 0px 0px;
- border: 1px solid #51a8ff;
- font-family: Segoe UI, Segoe UI;
- font-weight: 400;
- font-size: 14px;
- color: #1C5CFF;
- padding: 0 10px;
- display: inline-block;
- margin-right: 10px;
- cursor: pointer;
- }
-}
-
-</style>
diff --git a/applications/drone-command/src/views/job/components/SearchBox.vue b/applications/drone-command/src/views/job/components/SearchBox.vue
deleted file mode 100644
index e3bf99c..0000000
--- a/applications/drone-command/src/views/job/components/SearchBox.vue
+++ /dev/null
@@ -1,590 +0,0 @@
-<template>
- <div class="search-box-test" :class="{ 'is-expand': isExpand }">
- <el-form :model="searchForm" inline>
- <div class="search-first">
- <el-form-item>
- <el-input v-model="searchForm.key_word" placeholder="请输入任务编号/名称" clearable />
- </el-form-item>
- <el-form-item label="行政区划:">
- <el-tree-select
- popper-class="custom-tree-select"
- v-model="searchForm.area_code"
- :data="deptTreeData"
- :default-expanded-keys="[searchForm.area_code]"
- check-strictly
- node-key="id"
- :props="treeProps"
- clearable
- @change="handleNodeClick"
- />
- </el-form-item>
- <el-form-item label="所属机巢:">
- <el-select
- :teleported="false"
- v-model="searchForm.device_sn"
- placeholder="请选择"
- clearable
- @change="handleSearch"
- >
- <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>
- <el-date-picker
- popper-class="custom-date-picker"
- v-model="dateRange"
- type="daterange"
- range-separator="至"
- start-placeholder="开始日期"
- end-placeholder="结束日期"
- :value-format="timeFormat"
- @change="handleDateChange"
- />
- </el-form-item>
- <el-form-item>
- <div class="time-card">
- <div
- class="card-item"
- :class="item === checked ? 'active' : ''"
- v-for="(item, index) in timeList"
- :key="index"
- @click="timeClick(item, index)"
- >
- {{ timeListStr[index] }}
- </div>
- </div>
- </el-form-item>
- <div class="more" v-if="isExpand" @click="toggleExpand">收起</div>
- <div class="more" v-else @click="toggleExpand">更多</div>
- <div class="search-btn" :style="{ bottom: isExpand ? '5px' : '-3px' }">
-
- <el-button type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button>
- <el-button icon="el-icon-refresh" @click="handleReset">重置</el-button>
- </div>
- <el-form-item label="任务算法:" v-if="isExpand" class="taskAlgorithm">
- <!-- <TaskAlgorithmBusiness
- ref="algorithmRef"
- :setWidth="160"
- :showAlgorithm="true"
- @algorithmChange="algorithmChange"
- /> -->
- <el-tree-select
- style="z-index: 1000"
- :teleported="false"
- class="custom-tree-select"
- :style="{ width: pxToRem(186) }"
- v-model="dictKey"
- :data="dataList"
- :default-expanded-keys="[dictKey]"
- :props="treePropsSF"
- :render-after-expand="false"
- :default-checked-keys="checkedKeys"
- node-key="id"
- multiple
- show-checkbox
- collapse-tags
- collapse-tags-tooltip
- clearable
- @check="handleCheck"
- @clear="handleClear"
- />
- </el-form-item>
- <el-form-item label="所属部门:" v-if="isExpand">
- <el-tree-select
- popper-class="custom-tree-select"
- v-model="searchForm.create_dept"
- :data="deptData"
- :default-expanded-keys="[searchForm.create_dept]"
- check-strictly
- node-key="id"
- :props="treeDeptProps"
- clearable
- @change="(id) => handleDeptNodeClick({id})"
- />
- </el-form-item>
- <el-form-item label="任务状态:" v-if="isExpand">
- <el-select
- style="z-index: 1000"
- :teleported="false"
- v-model="searchForm.status"
- placeholder="请选择"
- clearable
- multiple
- collapse-tags
- collapse-tags-tooltip
- @change="handleSearch"
- >
- <el-option
- v-for="item in statusOptions"
- :key="item.value"
- :label="item.label"
- :value="item.value"
- />
- </el-select>
- </el-form-item>
- <el-form-item label="任务类型:" v-if="isExpand">
- <el-select
- :teleported="false"
- v-model="searchForm.is_circle_job"
- placeholder="请选择"
- clearable
- @change="handleSearch"
- >
- <el-option label="周期任务" :value="1" />
- <el-option label="临时任务" :value="0" />
- </el-select>
- </el-form-item>
- </div>
- </el-form>
- </div>
-</template>
-<script setup>
-import { pxToRem } from '@/utils/rem';
-import { ElMessage } from 'element-plus';
-import { deptsByAreaCode, getSFDictionaryTree } from '@/api/job/task';
-import { getDeptLazyTree } from '@/api/system/dept'
-// import TaskAlgorithmBusiness from './TaskAlgorithmBusiness.vue';
-import dayjs from 'dayjs';
-import { useStore } from 'vuex';
-import { getRegionTreeAll } from '@/api/job/task';
-import { getDeviceRegion } from '@/api/job/task';
-const route = useRoute();
-const store = useStore();
-const userAreaCode = computed(() => store.getters.userInfo.detail.areaCode);
-const selectedAreaCode = computed(() => store.state.user.selectedAreaCode);
-
-const algorithmRef = ref(null)
-const isExpand = ref(false);
-const toggleExpand = () => {
- isExpand.value = !isExpand.value;
-};
-const timeFormat = 'YYYY-MM-DD HH:mm:ss';
-
-const dateRange = ref([]);
-
-const treeProps = {
- label: 'name',
- value: 'id',
- children: 'childrens',
-};
-const searchForm = reactive({
- ai_types: [], // 算法类型
- area_code: userAreaCode.value, // 区域code
- create_dept: '', // 创建部门
- date_enum: 'CURRENT_MONTH', // 日期枚举,可用值:TODAY,CURRENT_WEEK,CURRENT_MONTH,CURRENT_YEAR
- device_sn: '', // 设备编号
- end_date: null, // 结束时间
- industry_type: '', // 行业key
- key_word: '', // 模糊搜索关键词(匹配名称/昵称/设备sn)
- start_date: null, // 开始时间
- status: [], // 作业状态
- is_circle_job: null, // 任务类型
-});
-
-// 从单机巢任务传参值
-const signDevice_sn = ref('');
-
-const statusOptions = [
- { label: '待执行', value: 1 },
- { label: '执行中', value: 2 },
- { label: '已执行', value: 3 },
- // { label: '已取消', value: 4 },
- { label: '执行失败', value: 5 },
- { label: '取消执行', value: 7 },
-];
-
-const emit = defineEmits(['search', 'addTask']);
-
-const algorithmChange = val => {
- searchForm.ai_types = val;
- handleSearch()
-};
-
-const businessChange = val => {
- searchForm.industry_type = val;
-};
-
-let deptData = ref([]);
-// 所属部门信息
-const getDeptsByAreaCode = () => {
- getDeptLazyTree({ parentId: 0, level: 0 }).then(res => {
- deptData.value = res.data.data;
- });
-};
-
-// 部门
-let deptTreeData = ref([]);
-// 机巢
-let machineData = ref([]);
-
-// 记录选中行政区划值
-let area_code_log = ref('')
-// 记录选中部门
-let dept_id_log = ref('')
-
-// 部门下得机巢
-const requestDockInfo = () => {
- getRegionTreeAll({ parentCode: userAreaCode.value }).then(res => {
- deptTreeData.value = res.data.data ? [res.data.data] : [];
-
- area_code_log.value = userAreaCode.value
- roadDroneList()
- });
-};
-
-function handleNodeClick(data) {
- area_code_log.value = data?.id
- roadDroneList()
-}
-
-// 筛选部门管理
-function handleDeptNodeClick(data) {
- dept_id_log.value = data.id
- roadDroneList()
-}
-
-// 算法
-const treePropsSF = {
- label: 'dictValue',
- value: 'id',
- children: 'children',
-}
-// 部门
-const treeDeptProps = {
- label: 'title',
- value: 'id',
- children: 'children',
-}
-const dictKey = ref('')
-const dataList = ref([])
-function getAlgorithmList() {
- getSFDictionaryTree({code:'SF'}).then((res) => {
- if (res.data.code === 200) {
- const result = res.data.data[0].children
- // 过滤第一层数据
- const filteredData = result.map(item => {
- // 过滤第二层数据
- const children = item.children?.map(child => ({
- ...child,
- children: [] // 清空第三层数据
- }))
- return {
- ...item,
- children: children || []
- }
- })
- dataList.value = filteredData
- }
- })
-}
-
-function handleSFNodeClick(data) {
- if (data.children && data.children.length) {
- // 获取子节点dictKey
- const childDictKeys = data.children.map(child => child.dictKey)
- searchForm.ai_types = childDictKeys
- } else {
- searchForm.ai_types = [data.dictKey]
- }
- // 更新列表
- handleSearch()
-}
-function handleClear() {
- dictKey.value = ''
- searchForm.ai_types = []
- handleSearch()
-}
-
-async function roadDroneList() {
- // 处理机巢数据
- searchForm.device_sn = ''
- machineData.value = ''
- const droneList = await getDeviceRegion({ area_code: area_code_log.value, dept_id: dept_id_log.value })
-
- machineData.value = droneList?.data?.data
- // 默认选中值
- if (signDevice_sn.value) {
- searchForm.device_sn = signDevice_sn.value
- }
- handleSearch()
-}
-
-// 日期 和周期
-let timeList = ['today', 'week', 'month', 'year'];
-let timeListStr = ['今日', '本周', '本月', '本年'];
-let timeListEnum = ['TODAY', 'CURRENT_WEEK', 'CURRENT_MONTH', 'CURRENT_YEAR'];
-let checked = ref('');
-
-// 根据 周期 获取时间段
-function getDateRange(date_enum) {
- // 设置对应的日期范围
- switch (date_enum) {
- case 'today':
- dateRange.value = [dayjs().startOf('day').format(timeFormat), dayjs().endOf('day').format(timeFormat)]
- break
- case 'week':
- dateRange.value = [dayjs().startOf('week').format(timeFormat), dayjs().format(timeFormat)]
- break
- case 'month':
- dateRange.value = [dayjs().startOf('month').format(timeFormat), dayjs().format(timeFormat)]
- break
- case 'year':
- dateRange.value = [dayjs().startOf('year').format(timeFormat), dayjs().format(timeFormat)]
- break
- }
-}
-
-let timeClick = (item, index) => {
- if (checked.value === item) {
- checked.value = null
- searchForm.date_enum = undefined
- dateRange.value = []
- } else {
- checked.value = item
- searchForm.date_enum = timeListEnum[index]
- getDateRange(item)
- }
- handleSearch()
-}
-// 搜索
-const handleSearch = () => {
- if (!dateRange.value) {
- dateRange.value = [];
- }
- // if (dateRange.value && dateRange.value.length) {
- // // 有值时 清除 日 本周 本月 本年状态
- // checked.value = '';
- // searchForm.date_enum = '';
- // }
- // 提交至store
- let params = {
- ...searchForm,
- start_date: dateRange.value.length
- ? dayjs(dateRange?.value[0]).startOf('day').format(timeFormat)
- : null,
- end_date: dateRange.value.length
- ? dayjs(dateRange?.value[1]).endOf('day').format(timeFormat)
- : null,
- };
- store.commit('setTaskSearchParams', params);
- emit('search', params);
-};
-const handleDateChange = val => {
- checked.value = null
- searchForm.date_enum = undefined
- handleSearch()
-};
-
-// 重置
-const handleReset = () => {
- dateRange.value = [];
- Object.keys(searchForm).forEach(key => {
- searchForm[key] = '';
- });
- searchForm.ai_types = [];
- searchForm.date_enum = 'CURRENT_MONTH';
- searchForm.area_code = userAreaCode.value;
- checked.value = 'month';
- signDevice_sn.value = ''
- // 清除算法
- dictKey.value = '';
- // algorithmRef.value?.clear()
- area_code_log.value = userAreaCode.value
- dept_id_log.value = ''
- roadDroneList()
- // handleSearch();
-};
-
-// 新增任务
-const addTask = () => {
- emit('addTask');
-};
-
-// 监听从巡检任务概况提交的数据
-// watch(
-// () => store.state.task.jumpTask,
-// newVal => {
-
-// if (newVal && Object.keys(newVal).length) {
-// // 兼容当日任务 计划任务 历史任务传参
-// if (newVal.clickValue) {
-// checked.value = newVal.clickValue;
-// searchForm.date_enum = newVal.date_enum;
-// }
-// if (newVal.date_value) {
-// dateRange.value = newVal.date_value;
-// }
-// if (newVal.status) {
-// searchForm.status = newVal.status;
-// }
-// if (newVal.device_sn) {
-// searchForm.device_sn = newVal.device_sn;
-// // 不直接赋值的原因: 回选会被清空值
-// signDevice_sn.value = newVal.device_sn;
-// }
-// } else {
-// checked.value = 'today';
-// searchForm.date_enum = 'TODAY';
-// }
-// handleSearch();
-// },
-// { immediate: true, deep: true }
-// );
-
-const checkedKeys = ref([])
-
-const handleCheck = (data, { checkedKeys, checkedNodes }) => {
- dictKey.value = checkedKeys
- // 获取所有选中节点的 dictKey
- const selectedDictKeys = checkedNodes.map(node => node.dictKey).filter(Boolean)
- searchForm.ai_types = selectedDictKeys
- handleSearch()
-}
-
-onBeforeUnmount(() => {
- checked.value = 'today';
- searchForm.date_enum = 'TODAY';
-});
-
-onMounted(() => {
- requestDockInfo();
- getDeptsByAreaCode()
- getAlgorithmList();
- // 日历传值
- const calendarday = ref(route.query.day);
-
- if (calendarday.value) {
- const date = dayjs(calendarday.value).format(timeFormat);
-
- const dateArray = [date, date];
-
- dateRange.value = dateArray;
- searchForm.date_enum = ''
- const find = store.state.tags.bsTagList.find(i => i.path === '/job/jobstatistics')
- find && (find.query = {})
- } else {
- checked.value = 'month'
- getDateRange('month')
- }
- // 查询一次
- // handleSearch()
-});
-</script>
-<style lang="scss"></style>
-<style lang="scss" scoped>
-// .taskAlgorithm {
-// :deep() {
-// .task-algorithm > div {
-// width: 233px !important;
-// }
-// }
-// }
-.search-box-test {
- transition: all 0.3s;
-
- .search-first {
- position: relative;
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- gap: 20px 15px; // 设置行间距和列间距
-
- .more {
- cursor: pointer;
- width: 32px;
- height: 32px;
- color: #1c5cff;
- line-height: 32px;
- font-size: 16px;
- margin: 0 28px;
- }
-
- .search-btn {
- // position: absolute;
- // right: 0;
- display: flex;
- justify-content: space-between;
- cursor: pointer;
- font-size: 14px;
-
- .btn {
- width: 80px;
- height: 32px;
- border-radius: 4px 4px 4px 4px;
- line-height: 32px;
- display: flex;
- justify-content: center;
- align-items: center;
- // margin-left: px;
- img {
- width: 14px;
- height: 14px;
- }
- }
- .search {
- background: #409eff;
- color: #fff;
- margin-right: 10px;
- }
- .clear {
- border: 1px solid #d2d1d1;
- color: #676767;
- margin-right: 10px;
- }
- .add {
- background: #ff9243;
- color: #fff;
- }
- }
-
- .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;
- margin-right: 20px;
-
- .el-form-item__label {
- color: #363636;
- line-height: 32px;
- }
- }
- }
-}
-</style>
diff --git a/applications/drone-command/src/views/job/components/TaskAlgorithmBusiness.vue b/applications/drone-command/src/views/job/components/TaskAlgorithmBusiness.vue
deleted file mode 100644
index 231de4a..0000000
--- a/applications/drone-command/src/views/job/components/TaskAlgorithmBusiness.vue
+++ /dev/null
@@ -1,131 +0,0 @@
-<!-- 关联算法和综合业务 -->
-<template>
- <div class="task-algorithm" v-if="showAlgorithm">
- <el-select
- class="command-select"
- :teleported="false"
- :style="{ width: pxToRem(setWidth) }"
- v-model="ai_types"
- multiple
- collapse-tags
- collapse-tags-tooltip
- placeholder="支持多项选择"
- clearable
- @clear="handleClear"
- @change="handleAlgorithmChange"
- >
- <el-option v-for="item in taskAlgorithm" :key="item.id" :label="item.dictValue" :value="item.dictKey" />
- </el-select>
- </div>
- <div class="task-business" v-if="showBusiness">
- <el-select class="command-select"
- :teleported="false"
- :style="{ width: pxToRem(setWidth) }"
- v-model="industry_type"
- placeholder="请选择类型"
- clearable
- @change="handleBusinessChange"
- >
- <el-option v-for="item in taskBusiness" :key="item.id" :label="item.dictValue" :value="item.dictKey" />
- </el-select>
- </div>
-</template>
-
-<script setup>
-import { getMultipleDictionary } from '@/api/job/task'
-import { pxToRem } from '@/utils/rem';
-
-// 接收父组件传参
-const props = defineProps({
- showAlgorithm: {
- type: Boolean,
- default: false,
- },
- showBusiness: {
- type: Boolean,
- default: false,
- },
- setWidth: {
- type: String,
- default: '100%',
- },
-})
-
-// 算法
-let ai_types = ref('')
-let taskAlgorithm = ref([])
-// 综合业务
-let industry_type = ref('')
-let taskBusiness = ref([])
-
-// 请求字典字段
-const requestDictionary = () => {
- getMultipleDictionary('SF,HYLB').then(res => {
- if (res.code !== 0) {
- // 处理数据
- taskAlgorithm.value = res.data.data['SF']
- taskBusiness.value = res.data.data['HYLB']
- }
- })
-}
-const emit = defineEmits(['algorithmChange', 'businessChange'])
-// 算法选择事件
-const handleAlgorithmChange = value => {
- emit('algorithmChange', value)
-}
-// 业务选择事件
-const handleBusinessChange = value => {
- emit('businessChange', value)
-}
-
-function handleClear() {
- ai_types.value = []
- emit('algorithmChange', [])
-}
-
-defineExpose({
- handleClear,
-})
-
-onMounted(() => {
- requestDictionary()
-})
-</script>
-<style scoped lang="scss">
-// :deep(.el-tag, .el-tag.el-tag--primary),
-// :deep(.el-tag.el-tag--info) {
-// --el-tag-bg-color: none !important;
-// --el-tag-border-color: none !important;
-// --el-tag-hover-color: none !important;
-// color: #fff !important;
-// }
-::v-deep(.command-select) {
- .el-select__wrapper {
- z-index: 1111;
-
- /* 修改 Tooltip 面板样式 */
- .el-popper.is-light {
- background: #fff !important;
- // box-shadow: none !important;
- // border: none !important;
- border-radius: 4px !important;
-
- .el-select__selection {
- max-width: 240px !important;
-
- .el-select__selected-item {
- .el-tag {
- // background: #fff !important;
- // color: #000;
- }
- }
- }
-
- .el-popper__arrow::before {
- // background: #fff !important;
- // border: 1px solid #8d8b8b !important;
- }
- }
- }
-}
-</style>
diff --git a/applications/drone-command/src/views/job/components/TaskIntermediateContent/AddTask.vue b/applications/drone-command/src/views/job/components/TaskIntermediateContent/AddTask.vue
deleted file mode 100644
index 9fa7ff3..0000000
--- a/applications/drone-command/src/views/job/components/TaskIntermediateContent/AddTask.vue
+++ /dev/null
@@ -1,414 +0,0 @@
-<!-- 新建任务 -->
-<template>
- <el-dialog
- class="command-dialog"
- modal-class="add-task"
- v-model="isShowAddTask"
- title="新建任务"
- :width="1500"
- :close-on-click-modal="false"
- :destroy-on-close="true"
- @close="cancel"
- >
- <!-- <el-divider content-position="left">新建任务</el-divider> -->
- <div class="task-contain">
- <div class="left">
- <div class="search">
- <div class="item">
- <div class="itemchild">任务名称:</div>
- <el-input class="command-input" v-model="searchForm.name" placeholder="请输入"></el-input>
- </div>
- <div class="item">
- <div class="itemchild">日期选择:</div>
- <el-date-picker
- popper-class="custom-date-picker"
- class="command-date-picker tasktimer"
- v-model="taskData"
- type="date"
- placeholder="请选择日期"
- value-format="YYYY-MM-DD"
- :disabled-date="disabledDate"
- />
- </div>
- <div class="item">
- <div class="itemchild">任务时间:</div>
- <el-time-picker
- style="flex: 1"
- popper-class="custom-time-picker"
- class="command-date-picker tasktimer"
- v-model="timeSlot"
- placeholder="请选择"
- format="HH:mm"
- value-format="HH:mm"
- />
- </div>
- <div class="item">
- <div class="itemchild">选择航线:</div>
-
- <el-select
- :teleported="false"
- class="command-select"
- v-model="searchForm.file_id"
- @change="getWayLineFile"
- placeholder="请选择"
- clearable
- >
- <el-option
- v-for="item in routeOptions"
- :key="item.wayline_id"
- :label="item.name"
- :value="item.wayline_id"
- />
- </el-select>
- </div>
- <div class="item">
- <div class="itemchild">关联算法:</div>
- <!-- <TaskAlgorithmBusiness :setWidth="220" :showAlgorithm="true" @algorithmChange="algorithmChange" /> -->
- </div>
- <div class="item">
- <div class="itemchild">任务描述:</div>
- <el-input class="command-input" v-model="searchForm.remark" placeholder="请输入"></el-input>
- </div>
- </div>
- <div class="lines">
- <div class="wayline-type">
- <el-radio-group v-model="waylineType" @change="radioChange">
- <el-radio :value="1" label="单点航线"></el-radio>
- <el-radio :value="2" label="智能规划选区"></el-radio>
- </el-radio-group>
- </div>
- <!-- <TaskMap
- class="wayline-map"
- :wayLineFile="wayLineFile"
- :waylineModel="waylineModel"
- :checkedTableData="checkedTableData"
- :waylineTypeTest="waylineType"
- @clickPosition="clickSignPosition"
- @saveWayline="savePlanerWayline"
- /> -->
- </div>
- </div>
- <div class="right">
- <TaskTable
- ref="taskTableRef"
- :waylineId="waylineId"
- :waylineModel="waylineModel"
- :waylineType="waylineType"
- :singlePoint="singlePoint"
- :planarPoints="planarPoints"
- @update:selected="handleSelected"
- />
- <div class="btn">
- <!-- <img @click="cancel" src="@/assets/images/task/cancel.png" alt="" /> -->
- <img
- @click="!loading && submitClick()"
- :style="{ opacity: loading ? 0.5 : 1, cursor: loading ? 'not-allowed' : 'pointer' }"
- src="@/assets/images/task/publish.png"
- alt=""
- />
- </div>
- </div>
- </div>
- </el-dialog>
-</template>
-
-<script setup>
-import { ElMessage } from 'element-plus'
-import { pxToRem } from '@/utils/rem'
-import { getWaylineList, createTask } from '@/api/job/task'
-// import TaskAlgorithmBusiness from '../components/TaskAlgorithmBusiness.vue'
-// import TaskMap from './TaskMap.vue'
-import TaskTable from './TaskTable.vue'
-import { useRouter } from 'vue-router'
-const router = useRouter()
-
-const emit = defineEmits(['refresh'])
-
-const rangDate = ref([])
-// 航线ID
-const waylineId = ref('')
-// 航线文件
-const wayLineFile = ref('')
-// 航线类型
-const waylineType = ref(3)
-// 添加子组件引用
-const taskTableRef = ref(null)
-const taskData = ref('')
-const timeSlot = ref('')
-const searchForm = reactive({
- name: '',
- ai_types: [],
- file_id: '',
- begin_time: '',
- end_time: '',
- execute_time_arr: '',
- remark: '',
- type: waylineType.value,
- dock_sns: [],
- longitude: '',
- latitude: '',
- polygon: [],
-})
-const isShowAddTask = defineModel('show')
-
-const loading = ref(true)
-
-// 禁用当天之前的日期
-const disabledDate = time => {
- return time.getTime() < Date.now() - 8.64e7 // 86400000 = 24 * 60 * 60 * 1000
-}
-
-// 获取航线列表数据
-const routeOptions = ref([])
-const getRouteList = async () => {
- const res = await getWaylineList()
- if (res.data.code === 0) {
- routeOptions.value = res.data.data
- }
-}
-// 关联算法
-const algorithmChange = val => {
- searchForm.ai_types = val
-}
-
-// 切换航线类型
-const radioChange = val => {
- // 清空选中航线值
- searchForm.file_id = ''
- // 航线ID也清空
- waylineId.value = ''
- wayLineFile.value = ''
- waylineType.value = val
- searchForm.type = val
- // 清空列表数据
- nextTick(() => {
- if (taskTableRef.value) {
- taskTableRef.value.clearTableData()
- }
- })
-}
-// 航线类型
-const waylineModel = ref('')
-// 航线类型
-const judgeWaylineMode = route => {
- if (route.wayline_type == '2' || route.wayline_type == '4') {
- waylineModel.value = 'planar'
- } else {
- waylineModel.value = 'point'
- }
-}
-
-// 获取航线文件
-const getWayLineFile = async val => {
- searchForm.type = 0
- waylineType.value = 0
- waylineId.value = val
- const currentRoute = routeOptions.value.find(item => item.wayline_id === val)
- wayLineFile.value = currentRoute?.object_key || ''
- judgeWaylineMode(currentRoute)
-}
-
-// 获取选中机场列表数据,并且发布
-let checkedTableData = ref([])
-const handleSelected = val => {
- searchForm.dock_sns = val.map(item => item.device_sn)
- checkedTableData.value = val
-}
-
-// 地图点击事件 表格重新刷新数据
-let singlePoint = ref({})
-const clickSignPosition = val => {
- singlePoint.value = val
- searchForm.longitude = val.longitude
- searchForm.latitude = val.latitude
-}
-
-// 保存面状航线
-let planarPoints = ref([])
-const savePlanerWayline = val => {
- planarPoints.value = val
- const polygonArray = val.map(point => [point.longitude, point.latitude])
- searchForm.polygon = polygonArray
-}
-
-// 提交
-const submitClick = () => {
- if (loading.value) return
- if (!taskData.value) {
- ElMessage({
- message: '请选择任务日期',
- type: 'warning',
- })
- return
- }
- if (!searchForm.name) {
- ElMessage({
- message: '请输入任务名称',
- type: 'warning',
- })
- return
- }
- // 检查任务时间
- if (timeSlot.value) {
- const now = new Date()
- const today = now.toDateString()
- const selectedDate = new Date(taskData.value).toDateString()
-
- if (today === selectedDate) {
- const [hours, minutes] = timeSlot.value.split(':')
- const selectedTime = new Date()
- selectedTime.setHours(parseInt(hours), parseInt(minutes))
-
- if (selectedTime < now) {
- ElMessage({
- message: '任务时间不能小于当前时间',
- type: 'warning',
- })
- return
- }
- }
- }
- if (searchForm.dock_sns.length === 0) {
- ElMessage({
- message: '请选择机场',
- type: 'warning',
- })
- return
- }
- loading.value = true
- searchForm.begin_time = `${taskData.value} 00:00:00`
- searchForm.end_time = `${taskData.value} 23:59:59`
- searchForm.execute_time_arr = timeSlot.value ? [timeSlot.value] : []
- createTask(searchForm)
- .then(res => {
- if (res.data.code === 0) {
- ElMessage.success('任务创建成功')
- // 关闭当前窗口,刷新任务管理列表
- isShowAddTask.value = false
- if (searchForm.dock_sns.length === 1) {
- // 清除数据
- cancel()
- emit('refresh')
- } else {
- // 超过两个机场 跳转至集群调度
- store.commit('setSaveTaskId', res?.data.data.id)
- router.push({ path: '/clusterScheduling' })
- }
- }
- })
- .catch(err => {
- loading.value = false
- console.log(err)
- })
- .finally(() => {
- loading.value = false
- })
-}
-const cancel = () => {
- isShowAddTask.value = false
- // 清除搜索数据
- searchForm.name = ''
- searchForm.ai_types = []
- searchForm.file_id = ''
- searchForm.begin_time = ''
- searchForm.end_time = ''
- timeSlot.value = ''
- searchForm.remark = ''
- searchForm.dock_sns = []
- rangDate.value = []
- waylineId.value = ''
- wayLineFile.value = ''
- taskData.value = ''
-}
-
-onMounted(() => {
- getRouteList()
-})
-</script>
-
-<style lang="scss">
-.add-task {
- .el-pagination {
- text-align: left;
- padding: 20px;
- }
-}
-/* 修改日期单元格背景色 */
-// .custom-date-picker .el-picker-panel__body {
-// background: #012350 !important;
-// color: #fff !important;
-// }
-</style>
-
-<style lang="scss" scoped>
-.task-contain {
- display: flex;
- padding: 20px 32px 32px 27px;
- .left {
- width: 1330px;
- margin-right: 34px;
- .search {
- display: grid;
- grid-template-columns: repeat(3, 1fr);
- grid-template-rows: repeat(2, 1fr);
- gap: 30px; // 增加间距使布局更合理
- margin-bottom: 8px; // 添加底部间距
- .item {
- position: relative;
- display: flex;
- align-items: center;
- font-size: 16px;
- color: #ffffff;
- .itemchild {
- width: 68px;
- white-space: nowrap;
- margin-right: 5px;
- }
- // :deep(.el-date-editor.el-input__wrapper) {
- // width: 200px; // 调整日期选择器宽度
- // }
- }
- }
- }
- .right {
- width: 366px;
- display: flex;
- flex-direction: column;
- justify-content: space-between;
-
- .btn {
- display: flex;
- justify-content: center;
- align-items: center;
- img {
- width: 137px;
- height: 32px;
- cursor: pointer;
- }
- }
- }
- :deep(.tasktimer .el-input__wrapper) {
- background: none !important;
- color: #8ac3fd !important;
- }
- :deep(.el-input .el-input__icon) {
- color: #fff !important;
- }
- :deep(.el-input__inner) {
- color: #8ac3fd !important;
- &::placeholder {
- color: #8ac3fd !important;
- }
- }
- :deep(.el-radio__inner) {
- background: none !important;
- border: 1px solid #1b5d9a !important;
- }
- :deep(.el-radio__label) {
- color: #fff !important;
- }
- :deep(.el-radio__inner:after) {
- background: #65b5ff !important;
- }
-}
-</style>
diff --git a/applications/drone-command/src/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue b/applications/drone-command/src/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue
deleted file mode 100644
index 56b1402..0000000
--- a/applications/drone-command/src/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue
+++ /dev/null
@@ -1,382 +0,0 @@
-<!-- 任务统计表格 -->
-<template>
- <div class="task-intermediate-content">
- <SearchBox @search="searchClick" @addTask="handleAddTask"></SearchBox>
- <div class="task-table">
- <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') }}
- </template>
- </el-table-column>
- <el-table-column prop="job_info_num" label="任务编号" width="160">
- <template #default="scope">
- <el-tooltip-copy :content="scope.row.job_info_num" :showCopyText="true">
- {{scope.row.job_info_num}}
- </el-tooltip-copy>
- </template>
- </el-table-column>
- <el-table-column prop="name" label="任务名称" width="160">
- <template #default="scope">
- <el-tooltip-copy :content="scope.row.name" :showCopyText="true">
- {{scope.row.name}}
- </el-tooltip-copy>
- </template>
- </el-table-column>
- <el-table-column prop="device_names" label="所属机巢" show-overflow-tooltip/>
- <el-table-column prop="ai_type_str" label="关联算法" show-overflow-tooltip align="center" />
- <el-table-column label="任务状态" align="center">
- <template #default="scope">
- <div class="base_f_c_c">
- <span :style="{
- color:
- scope.row.status === 1
- ? '#00CDC2'
- : scope.row.status === 2
- ? '#FF720F'
- : scope.row.status === 3
- ? '#00AA2D'
- : scope.row.status === 5
- ? '#FF4848'
- : scope.row.status === 7
- ? '#A6A6A6'
- : '',
- }">
- {{ scope.row.status ? getStatusText(scope.row.status) : '' }}
- </span>
- <el-tooltip
- class="item"
- effect="dark"
- :content="scope.row.reason"
- placement="top"
- popper-class="reasonNotFly command-tooltip-box1"
- >
- <el-icon v-if="scope.row.status === 5 && scope.row.reason" color="#FF4848"><QuestionFilled /></el-icon>
- </el-tooltip>
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="is_circle_job" label="任务类型" align="center">
- <template #default="scope">
- <span>{{ scope.row.rep_rule_type ? '周期任务' : '临时任务' }}</span>
- </template>
- </el-table-column>
- <!-- <el-table-column prop="job_num" label="执行次数" /> -->
- <el-table-column prop="event_number" label="关联事件" align="center">
- <template #default="scope">
- <span>{{ scope.row.event_number ? scope.row.event_number : '/' }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="cycle_time_value" label="任务时间" show-overflow-tooltip />
- <el-table-column prop="dept_name" label="创建部门" width="200" align="center" />
- <el-table-column prop="creator_name" label="创建人" align="center" show-overflow-tooltip />
- <el-table-column label="操作" :width="pxToRemNum(200)" align="center">
- <template #default="scope">
- <!-- <el-button type="text" @click="rejectDetail(row.id)">驳回原因</el-button> -->
- <el-button icon="el-icon-back" v-if="scope.row.status === 2" type="text"
- @click="turnBack(scope.row)">
- 返航
- </el-button>
- <el-button icon="el-icon-close" v-if="scope.row.status === 1" type="text"
- @click="cancelTask(scope.row)">
- 取消任务
- </el-button>
- <el-button icon="el-icon-view" type="text" @click="handleDetail(scope.row)">查看</el-button>
- </template>
- </el-table-column>
- <template #empty>
- <el-empty
- class="custom-empty"
- :image-size="100"
- >
- <template #description>
- <span class="custom-text">暂无数据</span>
- </template>
- </el-empty>
- </template>
- </el-table>
- </div>
- <div class="pagination" v-if="jobListData.length > 0">
- <el-pagination class="command-pagination" popper-class="custom-pagination-dropdown" background
- :page-sizes="[10, 20, 30, 40, 50, 100]" :size="size" v-model:current-page="jobListParams.current"
- v-model:page-size="jobListParams.size" layout="total, sizes, prev, pager, next, jumper" :total="total"
- @size-change="handleSizeChange" @current-change="handleCurrentChange" />
- </div>
- </div>
- <!-- 添加任务 -->
- <AddTask v-model:show="isShowAddTask" @refresh="refreshGetJobList" />
- <!-- 当前任务详情 -->
- <!-- <CurrentTaskDetails
- v-if="isShowCurrentTaskDetails"
- v-model:show="isShowCurrentTaskDetails"
- :wayLineJobInfoId="rowData.id"
- /> -->
- <!-- 历史任务详情 -->
- <DeviceJobDetails v-if="isShowDeviceJobDetails" v-model:show="isShowDeviceJobDetails" :wayLineJobInfoId="rowData.id"
- :batchNo="rowData.batch_no" :jobId="jobId" :waylineJobId="rowData.wayline_job_id"/>
- <CancelTaskDialog ref="cancelTaskDialogRef" v-model:isShowCancelTask="isShowCancelTask" :row-data="rowData"
- @refresh="getJobList" />
-</template>
-
-<script setup>
-import SearchBox from '../SearchBox.vue'
-import AddTask from './AddTask.vue'
-import { pxToRem, pxToRemNum } from '@/utils/rem'
-// import CurrentTaskDetails from '@/components/CurrentTaskDetails/CurrentTaskDetails.vue'
-import { jobList, cancelJobs, taskReturnLines, returnHomeCluster, statusChangedApi } from '@/api/job/task';
-import { ElMessage } from 'element-plus'
-import DeviceJobDetails from '../DeviceJobDetails.vue'
-import CancelTaskDialog from '../CancelTaskDialog.vue'
-import { useStore } from 'vuex'
-import { cloneDeep } from 'lodash'
-import { inject, onBeforeUnmount } from 'vue';
-import ElTooltipCopy from '@/components/ElTooltipCopy.vue';
-const store = useStore()
-const singleUavHome = computed(() => store.state.home.singleUavHome)
-const jobListParams = reactive({
- current: 1,
- size: 10,
- searchParams: {},
-})
-const jobListData = ref([])
-const total = ref(0)
-let isShowDeviceJobDetails = ref(false)
-let isShowCurrentTaskDetails = ref(false)
-let isShowCancelTask = ref(false)
-const cancelTaskDialogRef = ref(null)
-
-// 获取任务列表
-const getJobList = () => {
- const apiParams = {
- ...cloneDeep(jobListParams),
- searchParams: {
- ...cloneDeep(jobListParams.searchParams),
- status_list: jobListParams.searchParams.status || undefined,
- status: undefined,
- },
- }
- jobList(apiParams).then(res => {
- if (res.data.code !== 0) return
- jobListData.value = res.data.data.records
- total.value = res.data.data.total
- })
-}
-// 状态文字
-const getStatusText = status => {
- const statusMap = {
- 1: '待执行',
- 2: '执行中',
- 3: '已执行',
- 4: '已取消',
- 5: '执行失败',
- 7: '取消执行',
- }
- return statusMap[status] || '-'
-}
-
-// 查看当前任务详情 如果是一台机则显示详情 如果是多台机则进入集群调度(暂未开发)
-let rowData = ref({})
-const jobId = ref('')
-const handleDetail = row => {
- if (!row.device_sns.length) return ElMessage.warning('没有device_sns')
- rowData.value = row ? row : {}
- if (row.device_sns.length > 1 && (row.status === 2 || row.status === 1)) {
- const adminUrl = `${import.meta.env.VITE_APP_AREA_NAME}/command-center-dashboard/#/clusterScheduling`
- const targetPath = `taskNo=${encodeURIComponent(rowData.value.job_info_num)}`
- window.open(`${adminUrl}?${targetPath}`, '_blank')
- return
- }
- jobId.value = rowData.value?.job_id
- if (row.status === 2 || row.status === 1) {
- // isShowCurrentTaskDetails.value = true
- // 跳转大屏当前任务详情
- const adminUrl = `${import.meta.env.VITE_APP_AREA_NAME}/command-center-dashboard/#/taskManage`
- const targetPath = `id=${encodeURIComponent(rowData.value.id)}&batchNo=${encodeURIComponent(rowData.value.batch_no)}&dockSn=${encodeURIComponent(rowData.value.device_sns[0])}`
- window.open(`${adminUrl}?${targetPath}`, '_blank')
- } else {
- isShowDeviceJobDetails.value = true
- }
-}
-
-// 分页大小改变
-const handleSizeChange = val => {
- jobListParams.size = val
- getJobList()
-}
-
-// 页码改变
-const handleCurrentChange = val => {
- jobListParams.current = val
- getJobList()
-}
-
-// 传参查询条件
-const searchClick = params => {
- jobListParams.current = 1
- jobListParams.size = 10
- jobListParams.searchParams = params
- getJobList()
-}
-
-const refreshGetJobList = () => {
- jobListParams.current = 1
- jobListParams.size = 10
- getJobList()
-}
-
-// 新建任务
-const isShowAddTask = ref(false)
-const handleAddTask = () => {
- isShowAddTask.value = true
-}
-
-const tableRowClassName = ({ row, rowIndex }) => {
- if (rowIndex % 2 === 1) {
- return 'warning-row'
- } else {
- return 'success-row'
- }
-}
-// 取消任务 0 取消全部 1取消单条
-const cancelTask = row => {
- if (row.execute_time_arr?.length && row.execute_time_arr?.length > 0) {
- rowData.value = row
- isShowCancelTask.value = true
- } else {
- cancelTaskDialogRef.value.submitCancelTask(row, 1)
- }
-}
-// 返航
-const turnBack = row => {
- if (row.returnStatus === '待机') return
-
- if (row.returnStatus === '自动返航') return ElMessage.success('无人机返航中')
-
- if (row.returnStatus === '自动降落') return ElMessage.success('无人机降落中')
- returnHomeCluster(row.device_sns).then(res => {
- if (res.data.code !== 0) return
- ElMessage.success('返航操作成功')
- getJobList()
- })
-}
-
-function handleCellClick(row, column) {
- if (column.no === 1) {
- navigator.clipboard.writeText(row.job_info_num).then(() => {
- ElMessage.success('复制任务编号成功')
- })
- } else if (column.no === 2) {
- navigator.clipboard.writeText(row.name).then(() => {
- ElMessage.success('复制任务名称成功')
- })
- }
-}
-
-
-const changeKey = inject('changeKey')
-const jobUpdateKey = computed(() => store.state.common.jobUpdateKey)
-const deviceUpdateKey = computed(() => store.state.common.deviceUpdateKey)
-
-watch([jobUpdateKey,deviceUpdateKey],()=>{
- getJobList()
- changeKey.value++
-})
-
-onBeforeUnmount(() => {
- // clearInterval(polling)
-})
-
-onMounted(() => {
- // getJobList()
-})
-</script>
-
-<style lang="scss" scoped>
-.task-intermediate-content {
- height: 0;
- flex: 1;
- margin: 0 10px 10px 10px;
- background-color: #ffffff;
- padding: 10px 20px;
- border-radius: 5px;
- display: flex;
- flex-direction: column;
-
- // 表格
- .task-table {
- height: 0;
- flex: 1;
- margin-top: 18px;
- overflow: auto;
- :deep(.el-scrollbar__view) {
- height: 100%;
- }
- :deep(.el-table--fit,.el-scrollbar__view) {
- height: 100%;
- }
- }
-
- .custom-empty {
- //font-family: Source Han Sans CN, Source Han Sans CN;
- //margin-top: 15%;
- //.custom-text {
- // color: #fff;
- // font-size: 16px;
- //}
- }
-
- .btnItem {
- height: 27px;
- line-height: 27px;
- border-radius: 0px 0px 0px 0px;
- border: 1px solid #51a8ff;
- font-family: Segoe UI, Segoe UI;
- font-weight: 400;
- font-size: 14px;
- color: #1C5CFF;
- padding: 0 10px;
- display: inline-block;
- margin-right: 10px;
- cursor: pointer;
-
- &.turnBack {
- border: 1px solid #ffa500;
- color: #ffa500;
- }
-
- &.cancelTask {
- border: 1px solid #00AA2D;
- color: #00AA2D;
- }
- }
-
- // :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);
- // }
- // }
- // 分页
- :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;
- padding: 20px 0;
- justify-content: right;
- }
-
- :deep(.el-pagination button) {
- background: center center no-repeat none !important;
- color: #8eb8ea !important;
- }
-}
-</style>
diff --git a/applications/drone-command/src/views/job/components/TaskIntermediateContent/TaskTable.vue b/applications/drone-command/src/views/job/components/TaskIntermediateContent/TaskTable.vue
deleted file mode 100644
index 2416daf..0000000
--- a/applications/drone-command/src/views/job/components/TaskIntermediateContent/TaskTable.vue
+++ /dev/null
@@ -1,252 +0,0 @@
-<template>
- <div class="table-container command-table">
- <div class="taskTableTitle"><span>可飞行机巢列表</span></div>
- <el-table
- :data="tableData"
- :row-style="{ height: '38px', fontSize: '14px', 'text-align': 'center' }"
- :header-cell-style="{ 'text-align': 'center', height: '36px', fontSize: '14px' }"
- @selection-change="handleSelectionChange"
- >
- <el-table-column type="selection" :selectable="checkSelectable" />
- <el-table-column type="index" label="序号">
- <template #default="{ $index }">
- {{ ($index + 1).toString().padStart(2, '0') }}
- </template>
- </el-table-column>
- <el-table-column prop="nickname" label="机巢名称" />
- <el-table-column prop="estimated_arrival_time" label="预计到达时间(min)" />
- <el-table-column prop="flight_range" label="执行里程(km)" />
- </el-table>
- <div class="pagination">
- <el-pagination
- class="command-pagination"
- v-model:current-page="pagingParams.page"
- v-model:page-size="pagingParams.size"
- layout=" prev, pager, next"
- :total="total"
- @size-change="handleSizeChange"
- @current-change="handleCurrentChange"
- />
- </div>
- </div>
-</template>
-
-<script setup>
-import { getFlyingNestBy } from '@/api/job/task'
-import _ from 'lodash'
-
-const props = defineProps({
- waylineId: {
- type: String,
- default: '',
- },
- waylineType: {
- type: Number,
- default: 3,
- },
- singlePoint: {
- type: Object,
- default: () => ({}),
- },
- waylineModel: {
- type: String,
- default: '',
- },
- planarPoints: {
- type: Array,
- default: () => [],
- },
-})
-
-// 选中的数据
-const selectedRows = ref([])
-// 分页参数
-let pageParams = ref({
- wayline_id: props.waylineId,
- type: props.waylineType,
- longitude: props.singlePoint.longitude,
- latitude: props.singlePoint.latitude,
- polygon: [],
-})
-let pagingParams = ref({
- current: 1,
- size: 10,
-})
-// 获取可用机巢列表数据
-const total = ref(0)
-const tableData = ref([])
-const getNestList = async () => {
- tableData.value = []
- const res = await getFlyingNestBy(pageParams.value, pagingParams.value)
- if (res.data.code === 0) {
- tableData.value = (res.data?.data || []).map((item, index) => ({
- ...item,
- estimated_arrival_time: item.estimated_arrival_time > 0 ? _.round(item.estimated_arrival_time / 60, 0) : 0,
- flight_range: item.flight_range > 0 ? _.round(item.flight_range / 1000, 0) : 0,
- }))
- }
-}
-// 分页大小改变
-const handleSizeChange = val => {
- pagingParams.value.size = val
- getNestList()
-}
-
-// 页码改变
-const handleCurrentChange = val => {
- pagingParams.value.current = val
- getNestList()
-}
-
-// 刷新数据
-const refreshData = () => {
- tableData.value = []
- pagingParams.value.current = 1
- getNestList()
-}
-
-const emit = defineEmits(['update:selected'])
-const handleSelectionChange = val => {
- // 如果不是智能规划选区模式,才更新所有选中数据
- if (props.waylineType !== 2) {
- selectedRows.value = val
- emit('update:selected', val)
- } else {
- // 如果是智能规划选区模式,更新选中数据
- if (val.length > 1) {
- const lastSelected = selection[selection.length - 1]
- selectedRows.value = [lastSelected]
- emit('update:selected', selectedRows.value)
- } else {
- emit('update:selected', val)
- }
- }
-}
-
-// 控制表格行是否可选
-const checkSelectable = row => {
- if (props.waylineType === 2) {
- // 如果已经有选中的行,且当前行未被选中,则不允许选择
- return selectedRows.value.length === 0 || selectedRows.value.some(selected => selected.device_sn === row.device_sn)
- }
- return true
-}
-
-// 处理单行选择
-// const handleSelect = (selection, row) => {
-// console.log(55555, selection)
-// if (props.waylineType === 2) {
-// // 如果是智能规划选区模式,确保只能选中一行
-// if (selection.length === 0) {
-// }
-// if (selection.length > 1 || selection.length === 1) {
-// // 保留最后选中的那一行
-// const lastSelected = selection[selection.length - 1]
-// selectedRows.value = [lastSelected]
-// // 触发更新事件
-// console.log('99999', selectedRows.value)
-// emit('update:selected', selectedRows.value)
-// }
-// }
-// }
-
-// 监听航线ID变化
-watch(
- () => props.waylineId,
- newVal => {
- pageParams.value.type = 0
- if (newVal && props.waylineModel === 'point') {
- pageParams.value.wayline_id = newVal
- refreshData()
- } else {
- // 数据为空时,清除列表数据
- tableData.value = []
- }
- },
- { deep: true }
-)
-
-// 监听单点返回的数据
-watch(
- () => props.singlePoint,
- newVal => {
- pageParams.value.wayline_id = ''
- pageParams.value.type = 1
- if (newVal && newVal.latitude && newVal.longitude) {
- pageParams.value.latitude = newVal.latitude
- pageParams.value.longitude = newVal.longitude
- refreshData()
- }
- },
- { deep: true }
-)
-
-// 监听面状航线返回的数据
-watch(
- () => props.planarPoints,
- newVal => {
- pageParams.value.wayline_id = ''
- pageParams.value.type = 2
- if (newVal && newVal.length > 0) {
- const polygonArray = newVal.map(point => [point.longitude, point.latitude])
- pageParams.value.polygon = polygonArray
- refreshData()
- }
- },
- { deep: true }
-)
-
-// 暴露给父组件的方法
-const clearTableData = () => {
- tableData.value = []
-}
-defineExpose({
- clearTableData,
-})
-// 表格隔行变色
-const tableRowClassName = ({ row, rowIndex }) => {
- if (rowIndex % 2 === 1) {
- return 'warning-row'
- } else {
- return 'success-row'
- }
-}
-onMounted(() => {})
-</script>
-
-<style lang="scss" scoped>
-.table-container {
- .pagination {
- margin-top: 10px;
- }
- :deep(.cell) {
- padding: 0;
- margin: 0;
- // border: 1px solid red;
- // width: 20px;
- }
- // 分页
- :deep(.el-pagination) {
- display: flex;
- justify-content: center;
- }
- .taskTableTitle {
- margin-bottom: 16px;
- background: url('/src/assets/images/signMachineNest/machineRight/detailtitle.png') no-repeat center;
- background-size: 100% 100%;
- span {
- display: inline-block;
- margin-left: 10px;
- font-size: 16px;
- color: #ddf0ff;
- line-height: 20px;
- text-align: left;
- margin-bottom: 8px;
- }
- }
- :deep(.el-checkbox__inner) {
- background: none !important;
- border: 1px solid #3194ef !important;
- }
-}
-</style>
diff --git a/applications/drone-command/src/views/job/components/TaskTop/TaskEvent.vue b/applications/drone-command/src/views/job/components/TaskTop/TaskEvent.vue
deleted file mode 100644
index 9f39ef2..0000000
--- a/applications/drone-command/src/views/job/components/TaskTop/TaskEvent.vue
+++ /dev/null
@@ -1,155 +0,0 @@
-<!-- 任务事件数量比统计 -->
-<template>
- <div class="task-event">
- <div class="title">任务事件数量比统计</div>
- <div class="chart" ref="chartRef"></div>
- </div>
-</template>
-
-<script setup>
-import * as echarts from 'echarts'
-import { jobEventBar } from '@/api/job/task'
-import dayjs from 'dayjs'
-import useEchartsResize from '@/hooks/useEchartsResize'
-import { useStore } from 'vuex'
-
-const store = useStore()
-
-// const setTaskSearchParams = computed(() => store.state.task.setTaskSearchParams);
-
-// 日期
-const currenDate = dayjs().format('YYYY-MM-DD')
-const newTime = ref([currenDate, currenDate])
-// 图表
-const chartRef = ref(null)
-let { chart } = useEchartsResize(chartRef)
-
-const option = {
- tooltip: {
- trigger: 'axis',
- axisPointer: {
- type: 'shadow',
- },
- },
- legend: {
- itemWidth: 8,
- itemHeight: 12,
- data: ['任务', '事件'],
- top: '2%',
- textStyle: {
- color: '#6B90B5',
- fontSize: 12,
- },
- },
- grid: {
- top: '15%',
- left: '3%',
- right: '4%',
- bottom: '2%',
- containLabel: true,
- },
- xAxis: {
- type: 'category',
- // data: ['1月', '2月', '3月', '4月', '5月', '6月'],
- axisLine: {
- show: true, // 隐藏轴线
- },
- axisLabel: {
- color: '#5B5B5D',
- },
- },
- yAxis: {
- type: 'value',
- axisLine: {
- lineStyle: {
- color: '#363636',
- },
- },
- splitLine: {
- lineStyle: {
- color: 'rgba(255, 255, 255, 0.1)',
- },
- },
- axisLabel: {
- color: '#5B5B5D',
- },
- },
- series: [
- {
- name: '任务',
- type: 'bar',
- barWidth: 6,
- itemStyle: {
- color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
- { offset: 0, color: '#FF8902' },
- { offset: 1, color: '#FF8902' },
- ]),
- },
- },
- {
- name: '事件',
- type: 'bar',
- barWidth: 6,
- itemStyle: {
- color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
- { offset: 0, color: '#4363FF' },
- { offset: 1, color: '#4363FF' },
- ]),
- },
- },
- ],
-}
-
-// 获取行业统计数据
-const getJobEventBar = value => {
- const params = {
- ...value,
- status_list:value.status,
- status:undefined
- }
- jobEventBar(params).then(res => {
- if (res.data.code !== 0) return
- option.xAxis.data = res.data.data.map(item => item.name)
- option.series[0].data = res.data?.data.map(item => item.data[0].value)
- option.series[1].data = res.data?.data.map(item => item.data[1].value)
- chart.value.setOption(option)
- })
-}
-
-const changeKey = inject('changeKey')
-// 添加监听
-watch(
- () => [store.state.task.taskSearchParams, changeKey.value],
- () => getJobEventBar(store.state.task.taskSearchParams),
- { deep: true }
-)
-</script>
-
-<style lang="scss" scoped>
-.task-event {
- width: 0;
- flex: 1;
- height: 100%;
- margin-left: 10px;
- background: #FFFFFF;
- border-radius: 8px 8px 8px 8px;
-
- .title {
- padding: 10px 0 10px 16px;
- width: 144px;
- height: 18px;
- font-family: Segoe UI, Segoe UI;
- font-weight: bold;
- font-size: 16px;
- color: #363636;
- line-height: 18px;
- }
-
- .chart {
- padding: 6px 0px;
- width: 100%;
- height: 166px;
-
- }
-}
-</style>
diff --git a/applications/drone-command/src/views/job/components/TaskTop/TaskIndustry.vue b/applications/drone-command/src/views/job/components/TaskTop/TaskIndustry.vue
deleted file mode 100644
index 58c3ed8..0000000
--- a/applications/drone-command/src/views/job/components/TaskTop/TaskIndustry.vue
+++ /dev/null
@@ -1,117 +0,0 @@
-<!-- 任务算法统计 -->
-<template>
- <div class="task-industry">
- <div class="title">机巢事件数量占比</div>
- <div class="chart" ref="chartRef"></div>
- </div>
-</template>
-
-<script setup>
-import * as echarts from 'echarts'
-import { industryJobNumPieChart } from '@/api/job/task'
-import useEchartsResize from '@/hooks/useEchartsResize'
-import { useStore } from 'vuex'
-
-const store = useStore()
-const chartRef = ref(null)
-let { chart } = useEchartsResize(chartRef)
-
-const option = {
- tooltip: {
- trigger: 'item',
- formatter: '{b}: {c} ({d}%)',
- },
- legend: {
- orient: 'vertical',
- top: 'center',
- right: '20',
- textStyle: {
- color: '#363636',
- fontSize: 12,
- },
- itemWidth: 8, // 减小图例标记的宽度
- itemHeight: 8, // 减小图例标记的高度
- itemGap: 8, // 减小图例项之间的间距
- formatter: '{name}', // 简化图例文本
- },
- series: [
- {
- name: '机巢事件统计',
- type: 'pie',
- // roseType: 'radius',
- radius: ['20%', '70%'],
- center: ['30%', '50%'],
- data: [],
- label: {
- show: false,
- position: 'outside',
- formatter: '{c}',
- fontSize: 12,
- color: '#000',
- },
- labelLine: {
- show: false,
- length: 10,
- length2: 10,
- lineStyle: {
- color: '#000',
- },
- },
- },
- ],
-}
-
-// 获取机巢事件数据
-const getIndustryJobNumPieChart = value => {
- industryJobNumPieChart(value).then(res => {
- if (res.data.code !== 0) return
- option.series[0].data = res.data.data
- // forEach(item => {
- // const matchData = res.data.data.find(d => d.name === item.name);
- // if (matchData) {
- // item.value = matchData.value;
- // }
- // });
- chart.value.setOption(option)
- })
-}
-
-const changeKey = inject('changeKey')
-// 添加监听
-watch(
- () => [store.state.task.taskSearchParams,changeKey.value],
- () => getIndustryJobNumPieChart(store.state.task.taskSearchParams),
- { deep: true }
-)
-
-
-onMounted(() => {
- getIndustryJobNumPieChart({ date_enum: 'TODAY' })
-})
-</script>
-
-<style lang="scss" scoped>
-.task-industry {
- width: 300px;
- height: 100%;
- background: #FFFFFF;
- border-radius: 8px 8px 8px 8px;
- margin-left: 10px;
-
- .title {
- font-family: Segoe UI, Segoe UI;
- padding: 10px 0 10px 16px;
- width: 160px;
- height: 18px;
- font-weight: bold;
- font-size: 16px;
- color: #363636;
- line-height: 18px;
- }
-
- .chart {
- width: 100%;
- height: 180px;
- }
-}
-</style>
diff --git a/applications/drone-command/src/views/job/components/TaskTop/TaskTime.vue b/applications/drone-command/src/views/job/components/TaskTop/TaskTime.vue
deleted file mode 100644
index fdacf62..0000000
--- a/applications/drone-command/src/views/job/components/TaskTop/TaskTime.vue
+++ /dev/null
@@ -1,161 +0,0 @@
-<!-- 任务数量统计 -->
-<template>
- <div class="task-time">
- <div class="title">任务数量统计</div>
- <div class="chart" ref="chartRef"></div>
- </div>
-</template>
-
-<script setup>
-import * as echarts from 'echarts'
-import { jobNumBar } from '@/api/job/task'
-import dayjs from 'dayjs'
-import useEchartsResize from '@/hooks/useEchartsResize'
-import { useStore } from 'vuex'
-
-const store = useStore()
-
-// 日期
-const currenDate = dayjs().format('YYYY-MM-DD')
-const newTime = ref([currenDate, currenDate])
-
-const chartRef = ref(null)
-let { chart } = useEchartsResize(chartRef)
-
-const option = {
- tooltip: {
- trigger: 'axis',
- axisPointer: {
- type: 'shadow',
- },
- formatter: '{b}: {c} 件',
- },
- legend: {
- itemWidth: 8,
- itemHeight: 12,
- top: '2%',
- textStyle: {
- color: '#6B90B5',
- fontSize: 12,
- },
- },
- grid: {
- top: '15%',
- left: '3%',
- right: '4%',
- bottom: '2%',
- containLabel: true,
- },
- // xAxis: {
- // type: 'category',
- // axisLine: {
- // lineStyle: {
- // color: '#fff'
- // }
- // },
- // axisLabel: {
- // color: '#fff',
- // interval: 0,
- // rotate: 30
- // }
- // },
- xAxis: {
- type: 'category',
- data: ['1'],
- axisLine: {
- show: true, // 隐藏轴线
- },
- // axisTick: {
- // show: true,
- // length: 2,
- // lineStyle: {
- // color: 'red',
- // },
- // },
- axisLabel: {
- color: '#363636',
- margin: 8,
- },
- },
- yAxis: {
- type: 'value',
- nameTextStyle: {
- color: '#E6F7FF',
- fontSize: 12,
- padding: [0, 0, 0, 0], // 调整单位文字位置
- },
- axisLine: {
- lineStyle: {
- color: '#fff',
- },
- },
- splitLine: {
- lineStyle: {
- color: 'rgba(255, 255, 255, 0.1)',
- },
- },
- axisLabel: {
- color: '#5B5B5D',
- },
- },
- series: [
- {
- name: '任务数量',
- type: 'bar',
- barWidth: 8,
- itemStyle: {
- color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
- { offset: 0, color: '#1C5CFF ' },
- { offset: 1, color: '#1C5CFF ' },
- ]),
- },
- },
- ],
-}
-
-// 获取任务时间统计数据
-const getJobNumBar = value => {
- jobNumBar(value).then(res => {
- if (res.data.code !== 0) return
- option.xAxis.data = res.data.data.map(item => item.name)
- option.series[0].data = res.data.data.map(item => item.value)
- chart.value.setOption(option)
- })
-}
-
-const changeKey = inject('changeKey')
-// 添加监听
-watch(
- () => [store.state.task.taskSearchParams,changeKey.value],
- () => getJobNumBar(store.state.task.taskSearchParams),
- { deep: true }
-)
-</script>
-
-<style lang="scss" scoped>
-.task-time {
- width: 529px;
- height: 100%;
- margin-left: 10px;
- background: #FFFFFF;
- border-radius: 8px 8px 8px 8px;
-
- .title {
- padding: 10px 0 10px 16px;
- width: 96px;
- height: 18px;
- font-family: Segoe UI, Segoe UI;
- font-weight: bold;
- font-size: 16px;
- color: #363636;
- line-height: 18px;
- }
-
- .chart {
- padding: 6px 0px;
- width: 100%;
- height: 166px;
- color: #000;
- }
-}
-</style>
diff --git a/applications/drone-command/src/views/job/components/TaskTop/TaskTop.vue b/applications/drone-command/src/views/job/components/TaskTop/TaskTop.vue
deleted file mode 100644
index 68b8a6b..0000000
--- a/applications/drone-command/src/views/job/components/TaskTop/TaskTop.vue
+++ /dev/null
@@ -1,47 +0,0 @@
-<!--
- * @Author : yuan
- * @Date : 2025-06-14 15:19:16
- * @LastEditors : yuan
- * @LastEditTime : 2025-06-26 11:46:38
- * @FilePath : \src\views\job\components\TaskTop\TaskTop.vue
- * @Description :
- * Copyright 2025 OBKoro1, All Rights Reserved.
- * 2025-06-14 15:19:16
--->
-<template>
- <div class="task-top">
- <!--时间 天气-->
- <div class="statistical-chart">
- <TaskTotal />
- <TaskIndustry />
- <TaskTime />
- <TaskEvent />
- </div>
- </div>
-</template>
-
-<script setup>
-import { pxToRem } from '@/utils/rem'
-import TaskTotal from './TaskTotal.vue'
-import TaskIndustry from './TaskIndustry.vue'
-import TaskTime from './TaskTime.vue'
-import TaskEvent from './TaskEvent.vue'
-</script>
-
-<style lang="scss" scoped>
-.task-top {
- position: relative;
- display: flex;
-
- .statistical-chart {
- margin-left: 10px;
- margin-right: 10px;
- display: flex;
- width: 0;
- flex: 1;
- height: 216px;
- box-sizing: border-box;
- margin-bottom: 10px;
- }
-}
-</style>
diff --git a/applications/drone-command/src/views/job/components/TaskTop/TaskTotal.vue b/applications/drone-command/src/views/job/components/TaskTop/TaskTotal.vue
deleted file mode 100644
index b3bd734..0000000
--- a/applications/drone-command/src/views/job/components/TaskTop/TaskTotal.vue
+++ /dev/null
@@ -1,157 +0,0 @@
-<!-- 任务统计 -->
-<template>
- <div class="task-total">
- <div class="title">任务数据概览</div>
- <div class="contain">
- <div class="left">
- <div class="total-num">
- <img src="@/assets/images/task/total-number.png" alt="" />
- <div class="txt">总执行次数</div>
- <div class="num">{{ numTotal }}</div>
- </div>
- </div>
- <div class="other-total">
- <div class="total" v-for="(item, index) in list" :key="index">
- <div class="total-item">
- <div class="name"><img :src="item.img" alt="" />{{ item.name }}</div>
- <div class="value" :style="{ color: item.color }">{{ item.value }}</div>
- </div>
- </div>
- </div>
- </div>
- </div>
-</template>
-
-<script setup>
-import { pxToRem } from '@/utils/rem'
-import { totalJobNum, jobStatistics } from '@/api/job/task'
-import { useStore } from 'vuex'
-import jhzxpng from '@/assets/images/task/jhzx.png'
-import yzxpng from '@/assets/images/task/yzx.png'
-import zxzpng from '@/assets/images/task/zxz.png'
-import zxsbpng from '@/assets/images/task/zxsb.png'
-import dzxpng from '@/assets/images/task/dzx.png'
-import qxzxpng from '@/assets/images/task/qxzx.png'
-
-const store = useStore()
-
-const total = ref(0)
-const numTotal = ref(0)
-const list = ref([
- { name: '计划执行', value: '0', color: '#1C5CFF', img: jhzxpng },
- { name: '已执行', value: '0', color: '#00AA2D', img: yzxpng },
- { 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: '#373333', img: qxzxpng },
-])
-
-
-// 获取其他任务统计
-const getJobStatistics = value => {
- jobStatistics(value).then(res => {
- if (res.data.code !== 0) returen
- const {executed = 0,failed_executions = 0,go_home_executions = 0} = res.data?.data || {}
- numTotal.value = executed + failed_executions + go_home_executions
- list.value[0].value = res.data.data.planned_executions || 0
- list.value[1].value = res.data.data.executed || 0
- list.value[2].value = res.data.data.running_num || 0
- list.value[3].value = res.data.data.failed_executions || 0
- list.value[4].value = res.data.data.pending_executions || 0
- list.value[5].value = res.data.data.go_home_executions || 0
- })
-}
-
-const changeKey = inject('changeKey')
-// 添加监听
-watch(
- () => [store.state.task.taskSearchParams,changeKey.value],
- () => getJobStatistics(store.state.task.taskSearchParams),
- { deep: true }
-)
-
-</script>
-
-<style scoped lang="scss">
-.task-total {
- width: 300px;
- font-family: Source Han Sans CN, Source Han Sans CN;
- background: #FFFFFF;
- border-radius: 8px 8px 8px 8px;
- .title {
- padding: 10px 0 10px 16px;
- width: 96px;
- height: 18px;
- font-family: Segoe UI, Segoe UI;
- font-weight: bold;
- font-size: 16px;
- color: #363636;
- line-height: 18px;
- text-align: left;
- font-style: normal;
- text-transform: none;
- }
- .contain {
- display: flex;
- }
- .total-num {
- width: 100px;
-
- img {
- width: 84px;
- height: 84px;
- }
- .txt {
- font-weight: 400;
- font-size: 14px;
- color: #363636;
- line-height: 18px;
- text-align: center;
- }
- .num {
- font-weight: bold;
- font-size: 20px;
- color: #363636;
- line-height: 18px;
- text-align: center;
- margin-top: 6px;
- }
- }
- .other-total {
- position: relative;
- top: -10px;
- width: 200px;
- height: 160px;
- display: flex;
- gap: 10px;
-
- flex-wrap: wrap;
- align-content: space-evenly;
- .total {
- display: flex;
- align-items: center;
- margin-bottom: -18px;
- text-align: center;
-
- .name {
- display: flex;
- align-items: center;
- width: 90px;
- font-weight: 400;
- font-size: 14px;
- color: #363636;
- line-height: 18px;
- img {
- width: 18px;
- height: 18px;
- }
- }
- .value {
- font-weight: bold;
- font-size: 16px;
- text-align: center;
- }
- }
- }
-}
-</style>
diff --git a/applications/drone-command/src/views/job/components/executeNestDetails.vue b/applications/drone-command/src/views/job/components/executeNestDetails.vue
deleted file mode 100644
index 6fe3e25..0000000
--- a/applications/drone-command/src/views/job/components/executeNestDetails.vue
+++ /dev/null
@@ -1,225 +0,0 @@
-<template>
- <div class="executeNestDetails">
- <img src="/src/assets/images/task/sign.svg" alt="">
- <div class="title">
- 任务执行详情
- </div>
- </div>
- <div class="command-table">
- <el-table
- v-loading="loading"
- element-loading-background="rgba(22, 43, 74, 0.79)"
- :row-class-name="tableRowClassName"
- :data="taskData"
- style="width: 100%"
- :row-style="{ height: pxToRem(48), fontSize: pxToRem(14), 'text-align': 'center' }"
- :header-cell-style="{ 'text-align': 'center', height: pxToRem(36), fontSize: pxToRem(14) }"
- >
- <el-table-column type="index" label="序号" :width="pxToRemNum(100)" align="center">
- <template #default="{ $index }">
- {{ ($index + 1).toString().padStart(2, '0') }}
- </template>
- </el-table-column>
- <el-table-column show-overflow-tooltip prop="nick_name" label="机巢名称" align="center">
- <template #default="scope">
- <el-tooltip-copy :content="scope.row.nick_name" :showCopyText="true">
- {{ scope.row.nick_name }}
- </el-tooltip-copy>
- </template>
- </el-table-column>
-
- <el-table-column prop="status" label="执行状态" align="center">
- <template #default="scope">
- <div class="base_f_c_c">
- <span
- :style="{
- color:
- scope.row.status === 1
- ? '#ffe17e'
- : scope.row.status === 2
- ? '#ffa768'
- : scope.row.status === 3
- ? 'rgb(6, 217, 87)'
- : scope.row.status === 5
- ? '#FF4848'
- : scope.row.status === 7
- ? '#A6A6A6'
- : '',
- }"
- >
- {{ scope.row.status ? getStatusText(scope.row.status) : '' }}
- </span>
-
- <el-tooltip
- class="item"
- effect="dark"
- :content="scope.row.reason"
- placement="top"
- popper-class="reasonNotFly command-tooltip-box1"
- >
- <el-icon v-if="scope.row.status === 5 && scope.row.reason" color="#FF4848"><QuestionFilled /></el-icon>
- </el-tooltip>
- </div>
- </template>
- </el-table-column>
- </el-table>
- </div>
-</template>
-<script setup>
-import { NestDetailApi } from '@/api/home'
-import { dayjs, ElMessage, ElMessageBox } from 'element-plus'
-import { pxToRem, pxToRemNum } from '@/utils/rem'
-import { QuestionFilled } from '@element-plus/icons-vue'
-import { ContinuingToFly } from '@/const/device'
-const props = defineProps(['wayLineJobInfoId', 'batchNo'])
-const taskData = ref('')
-// 是否展示再次执行弹框
-let isShowDetails = ref(false)
-const singledevice_sns = ref([])
-const deviseName = ref(null)
-const devisejobInfoId = ref('')
-const devisebatchNo = ref(null)
-const loading = ref(true)
-const isShowContinuingToFly = code => {
- return ContinuingToFly.find(i => i.code === code)
-}
-// 状态文字
-const getStatusText = status => {
- const statusMap = {
- 1: '待执行',
- 2: '执行中',
- 3: '已执行',
- 4: '已取消',
- 5: '执行失败',
- 7: '取消执行',
- }
- return statusMap[status] || '-'
-}
-// 表格隔行变色
-const tableRowClassName = ({ row, rowIndex }) => {
- if (rowIndex % 2 === 1) {
- return 'warning-row'
- } else {
- return 'success-row'
- }
-}
-
-const handleSearch = () => {}
-// 执行机巢详情
-const getNestDetailApi = () => {
- const params = {
- jobInfoId: props.wayLineJobInfoId,
- batchNo: props.batchNo,
- }
- NestDetailApi(params).then(res => {
- taskData.value = res.data.data
- })
-}
-onMounted(() => {
- getNestDetailApi()
-})
-</script>
-
-<style scoped lang="scss">
-.executeNestDetails {
- display: flex;
- align-items: center;
- border-bottom: 2px solid #e4e7ed;
- img {
- width: 15px;
- height: 15px;
- }
- margin-bottom: 8px;
-
- .title {
- margin-left: 10px;
- font-size: 16px;
- color: #303133;
- }
-}
-
-.command-table {
- margin-bottom: 16px;
-}
-
-// 待处理
-.pending {
- color: #ff7411;
-}
-// 待审核
-.reviewed {
- color: #ff472f;
-}
-// 处理中
-.processing {
- color: #ffff61;
-}
-// 已完成
-.done {
- color: #06d957;
-}
-// 已完结
-.ended {
- color: #06d957;
-}
-
-.searchBox {
- display: flex;
-}
-.search-btn {
- display: flex;
- img {
- cursor: pointer;
- margin-right: 8px;
- width: 82px;
- height: 32px;
- }
-}
-.btnGroups {
- display: flex;
- align-items: center;
- justify-content: space-around;
- div {
- height: 27px;
- line-height: 27px;
- background: #001f4e !important;
- border-radius: 0px 0px 0px 0px;
- border: 1px solid #51a8ff !important;
- font-size: 14px;
- color: #ffffff;
- text-align: center;
- cursor: pointer;
- padding: 0 8px;
- }
-}
-</style>
-<style lang="scss">
-.machineTableDetailsTitle {
- display: flex;
- border-bottom: 2px solid #e4e7ed;
- background-size: 100% 100%;
-
- img {
- width: 15px;
- height: 15px;
- margin-top: 6px;
- }
-
- .title {
- display: inline-block;
- margin-left: 10px;
- font-size: 16px;
- // color: #0282ff;
- color: #303133;
- line-height: 20px;
- text-align: left;
- margin-bottom: 8px;
- }
-
- .btn {
- margin-left: 16px;
- margin-bottom: 8px;
- line-height: 20px;
- }
-}
-</style>
diff --git a/applications/drone-command/src/views/job/const/drc.js b/applications/drone-command/src/views/job/const/drc.js
deleted file mode 100644
index e15b981..0000000
--- a/applications/drone-command/src/views/job/const/drc.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import startRecordImg from '@/assets/images/task/startRecord.png'
-import hoverImg from '@/assets/images/task/hover.png'
-import takePhotoImg from '@/assets/images/task/takePhoto.png'
-
-export const droneEventList = [
- {name:'开始录像',value:'startRecord',img:startRecordImg},
- {name:'悬停',value:'hover',img:hoverImg},
- {name:'拍照',value:'takePhoto',img:takePhotoImg},
-]
\ No newline at end of file
diff --git a/applications/drone-command/src/views/job/jobinfo.vue b/applications/drone-command/src/views/job/jobinfo.vue
deleted file mode 100644
index 240c22f..0000000
--- a/applications/drone-command/src/views/job/jobinfo.vue
+++ /dev/null
@@ -1,277 +0,0 @@
-<template>
- <basic-container>
- <avue-crud :option="option" v-model:search="search" v-model:page="page" v-model="form" :table-loading="loading"
- :data="data" :permission="permissionList" :before-open="beforeOpen" ref="crud" @row-update="rowUpdate"
- @row-save="rowSave" @row-del="rowDel" @search-change="searchChange" @search-reset="searchReset"
- @selection-change="selectionChange" @current-change="currentChange" @size-change="sizeChange"
- @refresh-change="refreshChange" @on-load="onLoad">
- <template #menu-left>
- <el-button type="danger" icon="el-icon-delete" plain v-if="permission.jobinfo_delete" @click="handleDelete">删 除
- </el-button>
- <el-button type="info" plain icon="el-icon-sort" @click="handleSync">数 据 同 步
- </el-button>
- </template>
- <template #menu="scope">
- <el-button type="primary" text icon="el-icon-video-play" @click="handleRun(scope.row)">运 行
- </el-button>
- </template>
- <template #enable="{ row }">
- <el-switch v-model="row.enable" inline-prompt @change="slotChange(row)" active-text="启用" inactive-text="暂停"
- :active-value="1" :inactive-value="0" />
- </template>
- </avue-crud>
- </basic-container>
-</template>
-
-<script>
-import { getList, getDetail, add, update, remove, change, run, sync } from '@/api/job/jobinfo'
-import option from '@/option/job/jobinfo'
-import { mapGetters } from 'vuex'
-import 'nprogress/nprogress.css'
-import func from '@/utils/func'
-
-export default {
- data () {
- return {
- form: {},
- query: {},
- search: {},
- loading: true,
- page: {
- pageSize: 10,
- currentPage: 1,
- total: 0,
- },
- selectionList: [],
- option: option,
- data: [],
- searchChangeHandler: null,
- }
- },
- created() {
- this.searchChangeHandler = (params) => {
- this.searchChange(params, () => {});
- };
- this.$bus?.on('triggerSearchChange', this.searchChangeHandler);
- },
- beforeDestroy() {
- if (this.searchChangeHandler) {
- this.$bus?.off('triggerSearchChange', this.searchChangeHandler);
- this.searchChangeHandler = null;
- }
- },
- computed: {
- ...mapGetters(['permission']),
- permissionList () {
- return {
- addBtn: this.validData(this.permission.jobinfo_add, false),
- viewBtn: this.validData(this.permission.jobinfo_view, false),
- delBtn: this.validData(this.permission.jobinfo_delete, false),
- editBtn: this.validData(this.permission.jobinfo_edit, false),
- }
- },
- ids () {
- let ids = []
- this.selectionList.forEach(ele => {
- ids.push(ele.id)
- })
- return ids.join(',')
- },
- },
- methods: {
- rowSave (row, done, loading) {
- if (func.isArrayAndNotEmpty(row.lifecycle)) {
- const lifecycleStart = row.lifecycle[0]
- const lifecycleEnd = row.lifecycle[1]
- if (!func.isUndefined(lifecycleStart) && !func.isUndefined(lifecycleEnd)) {
- row.lifecycle = lifecycleStart + ',' + lifecycleEnd
- }
- } else {
- row.lifecycle = ''
- }
- add(row).then(
- () => {
- this.onLoad(this.page)
- this.$message({
- type: 'success',
- message: '操作成功!',
- })
- done()
- },
- error => {
- loading()
- window.console.log(error)
- }
- )
- },
- rowUpdate (row, index, done, loading) {
- if (func.isArrayAndNotEmpty(row.lifecycle)) {
- const lifecycleStart = row.lifecycle[0]
- const lifecycleEnd = row.lifecycle[1]
- if (!func.isUndefined(lifecycleStart) && !func.isUndefined(lifecycleEnd)) {
- row.lifecycle = lifecycleStart + ',' + lifecycleEnd
- }
- } else {
- row.lifecycle = ""
- }
- update(row).then(
- () => {
- this.onLoad(this.page)
- this.$message({
- type: 'success',
- message: '操作成功!',
- })
- done()
- },
- error => {
- loading()
- console.log(error)
- }
- )
- },
- rowDel (row) {
- this.$confirm('确定将选择数据删除?', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning',
- })
- .then(() => {
- return remove(row.id)
- })
- .then(() => {
- this.onLoad(this.page)
- this.$message({
- type: 'success',
- message: '操作成功!',
- })
- })
- },
- handleDelete () {
- if (this.selectionList.length === 0) {
- this.$message.warning('请选择至少一条数据')
- return
- }
- this.$confirm('确定将选择数据删除?', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning',
- })
- .then(() => {
- return remove(this.ids)
- })
- .then(() => {
- this.onLoad(this.page)
- this.$message({
- type: 'success',
- message: '操作成功!',
- })
- this.$refs.crud.toggleSelection()
- })
- },
- handleSync () {
- this.$confirm('确定进行数据双向同步?', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning',
- })
- .then(() => {
- return sync()
- })
- .then(() => {
- this.onLoad(this.page)
- this.$message({
- type: 'success',
- message: '操作成功!',
- })
- })
- },
- handleRun (row) {
- this.$confirm('运行后将创建一个实例执行,是否继续?', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning',
- })
- .then(() => {
- return run(row)
- })
- .then(() => {
- this.$message({
- type: 'success',
- message: '运行成功!',
- })
- })
- },
- slotChange (row) {
- if (!row.id) {
- return
- }
- change(row).then(
- () => {
- this.onLoad(this.page)
- this.$message({
- type: 'success',
- message: '操作成功!',
- })
- },
- error => {
- window.console.log(error)
- }
- )
- },
- beforeOpen (done, type) {
- if (['edit', 'view'].includes(type)) {
- getDetail(this.form.id).then(res => {
- this.form = res.data.data
- })
- }
- done()
- },
- searchReset () {
- this.query = {}
- this.onLoad(this.page)
- },
- searchChange (params, done) {
- this.query = params
- this.page.currentPage = 1
- this.onLoad(this.page, params)
- done()
- },
- selectionChange (list) {
- this.selectionList = list
- },
- selectionClear () {
- this.selectionList = []
- this.$refs.crud.toggleSelection()
- },
- currentChange (currentPage) {
- this.page.currentPage = currentPage
- },
- sizeChange (pageSize) {
- this.page.pageSize = pageSize
- },
- refreshChange () {
- this.onLoad(this.page, this.query)
- },
- onLoad (page) {
- this.loading = true
-
- const { jobServerId, jobName } = this.query
-
- let values = {
- jobServerId_like: jobServerId,
- jobName_like: jobName,
- }
-
- getList(page.currentPage, page.pageSize, values).then(res => {
- const data = res.data.data
- this.page.total = data.total
- this.data = data.records
- this.loading = false
- this.selectionClear()
- })
- },
- },
-}
-</script>
-
-<style scoped lang="scss"></style>
diff --git a/applications/drone-command/src/views/job/jobserver.vue b/applications/drone-command/src/views/job/jobserver.vue
deleted file mode 100644
index 29567a9..0000000
--- a/applications/drone-command/src/views/job/jobserver.vue
+++ /dev/null
@@ -1,213 +0,0 @@
-<template>
- <basic-container>
- <avue-crud :option="option" v-model:search="search" v-model:page="page" v-model="form" :table-loading="loading"
- :data="data" :permission="permissionList" :before-open="beforeOpen" ref="crud" @row-update="rowUpdate"
- @row-save="rowSave" @row-del="rowDel" @search-change="searchChange" @search-reset="searchReset"
- @selection-change="selectionChange" @current-change="currentChange" @size-change="sizeChange"
- @refresh-change="refreshChange" @on-load="onLoad">
- <template #menu-left>
- <el-button type="danger" icon="el-icon-delete" plain v-if="permission.jobserver_delete" @click="handleDelete">删
- 除
- </el-button>
- <el-button type="info" plain icon="el-icon-sort" @click="handleSync">数 据 同 步
- </el-button>
- </template>
- <template #menu="scope">
- <el-button type="primary" text icon="el-icon-video-play" @click="handleLink(scope.row)">服务跳转
- </el-button>
- </template>
- </avue-crud>
- </basic-container>
-</template>
-
-<script>
-import { getList, getDetail, add, update, remove, sync } from '@/api/job/jobserver'
-import option from '@/option/job/jobserver'
-import { mapGetters } from 'vuex'
-import 'nprogress/nprogress.css'
-import func from '@/utils/func'
-
-export default {
- data () {
- return {
- form: {},
- query: {},
- search: {},
- loading: true,
- page: {
- pageSize: 10,
- currentPage: 1,
- total: 0,
- },
- selectionList: [],
- option: option,
- data: [],
- }
- },
- computed: {
- ...mapGetters(['permission']),
- permissionList () {
- return {
- addBtn: this.validData(this.permission.jobserver_add, false),
- viewBtn: this.validData(this.permission.jobserver_view, false),
- delBtn: this.validData(this.permission.jobserver_delete, false),
- editBtn: this.validData(this.permission.jobserver_edit, false),
- }
- },
- ids () {
- let ids = []
- this.selectionList.forEach(ele => {
- ids.push(ele.id)
- })
- return ids.join(',')
- },
- },
- methods: {
- rowSave (row, done, loading) {
- add(row).then(
- () => {
- this.onLoad(this.page)
- this.$message({
- type: 'success',
- message: '操作成功!',
- })
- done()
- },
- error => {
- loading()
- window.console.log(error)
- }
- )
- },
- rowUpdate (row, index, done, loading) {
- update(row).then(
- () => {
- this.onLoad(this.page)
- this.$message({
- type: 'success',
- message: '操作成功!',
- })
- done()
- },
- error => {
- loading()
- console.log(error)
- }
- )
- },
- rowDel (row) {
- this.$confirm('确定将选择数据删除?', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning',
- })
- .then(() => {
- return remove(row.id)
- })
- .then(() => {
- this.onLoad(this.page)
- this.$message({
- type: 'success',
- message: '操作成功!',
- })
- })
- },
- handleDelete () {
- if (this.selectionList.length === 0) {
- this.$message.warning('请选择至少一条数据')
- return
- }
- this.$confirm('确定将选择数据删除?', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning',
- })
- .then(() => {
- return remove(this.ids)
- })
- .then(() => {
- this.onLoad(this.page)
- this.$message({
- type: 'success',
- message: '操作成功!',
- })
- this.$refs.crud.toggleSelection()
- })
- },
- handleLink (row) {
- window.open(func.formatUrl(row.jobServerUrl))
- },
- handleSync () {
- this.$confirm('确定进行数据双向同步?', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning',
- })
- .then(() => {
- return sync()
- })
- .then(() => {
- this.onLoad(this.page)
- this.$message({
- type: 'success',
- message: '操作成功!',
- })
- })
- },
- beforeOpen (done, type) {
- if (['edit', 'view'].includes(type)) {
- getDetail(this.form.id).then(res => {
- this.form = res.data.data
- })
- }
- done()
- },
- searchReset () {
- this.query = {}
- this.onLoad(this.page)
- },
- searchChange (params, done) {
- this.query = params
- this.page.currentPage = 1
- this.onLoad(this.page, params)
- done()
- },
- selectionChange (list) {
- this.selectionList = list
- },
- selectionClear () {
- this.selectionList = []
- this.$refs.crud.toggleSelection()
- },
- currentChange (currentPage) {
- this.page.currentPage = currentPage
- },
- sizeChange (pageSize) {
- this.page.pageSize = pageSize
- },
- refreshChange () {
- this.onLoad(this.page, this.query)
- },
- onLoad (page) {
- this.loading = true
-
- const { jobServerName, jobAppName } = this.query
-
- let values = {
- jobServerName_like: jobServerName,
- jobAppName_like: jobAppName,
- }
-
- getList(page.currentPage, page.pageSize, values).then(res => {
- const data = res.data.data
- this.page.total = data.total
- this.data = data.records
- this.loading = false
- this.selectionClear()
- })
- },
- },
-}
-</script>
-
-<style scoped lang="scss"></style>
diff --git a/applications/drone-command/src/views/job/jobstatistics.vue b/applications/drone-command/src/views/job/jobstatistics.vue
deleted file mode 100644
index 3689297..0000000
--- a/applications/drone-command/src/views/job/jobstatistics.vue
+++ /dev/null
@@ -1,14 +0,0 @@
-
-<template>
- <TaskTop />
- <TaskIntermediateContent />
-</template>
-<script setup>
-import TaskTop from "@/views/job/components/TaskTop/TaskTop.vue"
-import TaskIntermediateContent from "@/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue"
-import { ref } from 'vue';
-const changeKey = ref(0)
-provide('changeKey', changeKey)
-</script>
-
-<style scoped lang="scss"></style>
diff --git a/applications/task-work-order/src/views/flow/deploy.vue b/applications/task-work-order/src/views/flow/deploy.vue
new file mode 100644
index 0000000..9fac645
--- /dev/null
+++ b/applications/task-work-order/src/views/flow/deploy.vue
@@ -0,0 +1,160 @@
+<template>
+ <basic-container>
+ <avue-form
+ ref="form"
+ :option="option"
+ v-model="form"
+ :upload-before="uploadBefore"
+ :upload-after="uploadAfter"
+ />
+ </basic-container>
+</template>
+
+<script>
+import { deployUpload } from '@/api/flow/flow';
+import { flowCategory } from '@/utils/flow';
+
+export default {
+ data() {
+ return {
+ form: {
+ flowCategory: '',
+ tenantId: '',
+ flowFile: [],
+ file: {},
+ },
+ option: {
+ labelWidth: 120,
+ menuBtn: false,
+ column: [
+ {
+ label: '流程类型',
+ prop: 'flowCategory',
+ type: 'select',
+ dicUrl: `/blade-system/dict/dictionary?code=flow`,
+ props: {
+ label: 'dictValue',
+ value: 'dictKey',
+ },
+ row: true,
+ span: 12,
+ dataType: 'number',
+ rules: [
+ {
+ required: true,
+ message: '请选择流程类型',
+ trigger: 'blur',
+ },
+ ],
+ },
+ {
+ label: '流程模式',
+ prop: 'flowMode',
+ type: 'radio',
+ dicData: [
+ {
+ label: '通用流程',
+ value: 1,
+ },
+ {
+ label: '定制流程',
+ value: 2,
+ },
+ ],
+ value: 1,
+ row: true,
+ span: 12,
+ rules: [
+ {
+ required: true,
+ message: '请选择流程模式',
+ trigger: 'blur',
+ },
+ ],
+ },
+ {
+ label: '所属租户',
+ prop: 'tenantId',
+ type: 'tree',
+ multiple: true,
+ dicUrl: '/blade-system/tenant/select',
+ props: {
+ label: 'tenantName',
+ value: 'tenantId',
+ },
+ display: false,
+ row: true,
+ span: 12,
+ rules: [
+ {
+ required: true,
+ message: '请选择所属租户',
+ trigger: 'blur',
+ },
+ ],
+ },
+ {
+ label: '附件上传',
+ prop: 'flowFile',
+ type: 'upload',
+ loadText: '附件上传中,请稍等',
+ span: 24,
+ propsHttp: {
+ res: 'data',
+ },
+ tip: '请上传 bpmn20.xml 标准格式文件',
+ action: '/blade-flow/manager/check-upload',
+ },
+ ],
+ },
+ };
+ },
+ watch: {
+ 'form.flowMode'() {
+ this.$refs.form.option.column.filter(item => {
+ if (item.prop === 'tenantId') {
+ item.display = this.form.flowMode === 2;
+ }
+ });
+ },
+ },
+ methods: {
+ uploadBefore(file, done) {
+ this.$message.success('部署提交');
+ this.file = file;
+ done();
+ },
+ uploadAfter(res, done, loading) {
+ if (!this.form.flowCategory) {
+ this.$message.warning('清先选择流程类型');
+ loading();
+ return false;
+ }
+ if (this.form.flowMode === 2 && !this.form.tenantId) {
+ this.$message.warning('清先选择对应租户');
+ loading();
+ return false;
+ }
+ if (res.success) {
+ deployUpload(
+ flowCategory(this.form.flowCategory),
+ this.form.tenantId ? this.form.tenantId.join(',') : '',
+ [this.file]
+ ).then(res => {
+ const data = res.data;
+ if (data.success) {
+ done();
+ } else {
+ this.$message.error(data.msg);
+ loading();
+ }
+ });
+ } else {
+ this.$message.warning('请上传 bpmn20.xml 标准格式文件');
+ loading();
+ return false;
+ }
+ },
+ },
+};
+</script>
diff --git a/applications/task-work-order/src/views/flow/follow.vue b/applications/task-work-order/src/views/flow/follow.vue
new file mode 100644
index 0000000..f600bce
--- /dev/null
+++ b/applications/task-work-order/src/views/flow/follow.vue
@@ -0,0 +1,199 @@
+<template>
+ <basic-container>
+ <avue-crud
+ :option="option"
+ :table-loading="loading"
+ :data="data"
+ ref="crud"
+ v-model="form"
+ v-model:page="page"
+ :permission="permissionList"
+ @row-del="rowDel"
+ @search-change="searchChange"
+ @search-reset="searchReset"
+ @selection-change="selectionChange"
+ @current-change="currentChange"
+ @size-change="sizeChange"
+ @refresh-change="refreshChange"
+ @on-load="onLoad"
+ >
+ <template #suspensionState="{ row }">
+ <el-tag>{{ row.suspensionState === 1 ? '激活' : '挂起' }}</el-tag>
+ </template>
+ </avue-crud>
+ <el-dialog title="流程删除" append-to-body v-model="followBox" width="20%">
+ <el-form :model="form" ref="form" label-width="80px">
+ <el-form-item label="删除理由">
+ <el-input v-model="deleteReason" placeholder="请输入删除理由" />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="followBox = false">关 闭</el-button>
+ <el-button type="primary" @click="handleDelete">确 定</el-button>
+ </span>
+ </template>
+ </el-dialog>
+ </basic-container>
+</template>
+
+<script>
+import { mapGetters } from 'vuex';
+import { followList, deleteProcessInstance } from '@/api/flow/flow';
+
+export default {
+ data() {
+ return {
+ form: {},
+ selectionId: '',
+ processInstanceId: '',
+ selectionList: [],
+ query: {},
+ loading: true,
+ page: {
+ pageSize: 10,
+ currentPage: 1,
+ total: 0,
+ },
+ followBox: false,
+ deleteReason: '',
+ option: {
+ tip: false,
+ searchShow: true,
+ searchMenuSpan: 6,
+ border: true,
+ index: true,
+ selection: true,
+ editBtn: false,
+ addBtn: false,
+ viewBtn: false,
+ dialogWidth: 900,
+ menuWidth: 100,
+ dialogClickModal: false,
+ column: [
+ {
+ label: '执行id',
+ prop: 'executionId',
+ search: true,
+ width: 320,
+ },
+ {
+ label: '流程key',
+ prop: 'processDefinitionKey',
+ search: true,
+ },
+ {
+ label: '实例id',
+ prop: 'processInstanceId',
+ search: true,
+ width: 320,
+ },
+ {
+ label: '状态',
+ prop: 'suspensionState',
+ slot: true,
+ width: 80,
+ },
+ {
+ label: '发起人',
+ prop: 'startUser',
+ width: 100,
+ },
+ {
+ label: '开始时间',
+ prop: 'startTime',
+ width: 170,
+ },
+ ],
+ },
+ data: [],
+ };
+ },
+ computed: {
+ ...mapGetters(['permission']),
+ permissionList() {
+ return {
+ delBtn: this.validData(this.permission.flow_follow_delete, false),
+ };
+ },
+ ids() {
+ let ids = [];
+ this.selectionList.forEach(ele => {
+ ids.push(ele.id);
+ });
+ return ids.join(',');
+ },
+ },
+ methods: {
+ rowDel(row) {
+ this.followBox = true;
+ this.selectionId = row.id;
+ this.processInstanceId = row.processInstanceId;
+ },
+ handleDelete() {
+ this.$confirm('确定将选择数据删除?', {
+ confirmButtonText: '确定',
+ cancelButtonText: '取消',
+ type: 'warning',
+ })
+ .then(() => {
+ return deleteProcessInstance({
+ deleteReason: this.deleteReason,
+ processInstanceId: this.processInstanceId,
+ });
+ })
+ .then(() => {
+ this.onLoad(this.page);
+ this.followBox = false;
+ this.$message({
+ type: 'success',
+ message: '操作成功!',
+ });
+ });
+ },
+ searchReset() {
+ this.query = {};
+ this.onLoad(this.page);
+ },
+ searchChange(params, done) {
+ this.query = params;
+ this.page.currentPage = 1;
+ this.onLoad(this.page, params);
+ done();
+ },
+ selectionChange(list) {
+ this.selectionList = list;
+ },
+ selectionClear() {
+ this.selectionList = [];
+ this.$refs.crud.toggleSelection();
+ },
+ currentChange(currentPage) {
+ this.page.currentPage = currentPage;
+ },
+ sizeChange(pageSize) {
+ this.page.pageSize = pageSize;
+ },
+ refreshChange() {
+ this.onLoad(this.page, this.query);
+ },
+ onLoad(page, params = {}) {
+ this.loading = true;
+ followList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
+ const data = res.data.data;
+ this.page.total = data.total;
+ this.data = data.records;
+ this.loading = false;
+ this.selectionClear();
+ });
+ },
+ },
+};
+</script>
+
+<style>
+.none-border {
+ border: 0;
+ background-color: transparent !important;
+}
+</style>
diff --git a/applications/task-work-order/src/views/flow/manager.vue b/applications/task-work-order/src/views/flow/manager.vue
new file mode 100644
index 0000000..09ff99a
--- /dev/null
+++ b/applications/task-work-order/src/views/flow/manager.vue
@@ -0,0 +1,369 @@
+<template>
+ <basic-container>
+ <avue-crud
+ :option="option"
+ :table-loading="loading"
+ :data="data"
+ ref="crud"
+ v-model="form"
+ v-model:page="page"
+ :permission="permissionList"
+ @search-change="searchChange"
+ @search-reset="searchReset"
+ @selection-change="selectionChange"
+ @current-change="currentChange"
+ @size-change="sizeChange"
+ @refresh-change="refreshChange"
+ @on-load="onLoad"
+ >
+ <template #menu-left>
+ <el-radio-group v-model="mode">
+ <el-radio-button label="1">通用流程</el-radio-button>
+ <el-radio-button label="2">定制流程</el-radio-button>
+ </el-radio-group>
+ </template>
+ <template #menu="scope">
+ <el-button
+ type="primary"
+ text
+ icon="el-icon-refresh"
+ v-if="permission.flow_manager_state"
+ @click.stop="handleState(scope.row, scope.index)"
+ >变更状态
+ </el-button>
+ <el-button
+ type="primary"
+ text
+ icon="el-icon-search"
+ v-if="permission.flow_manager_image"
+ @click.stop="handleImage(scope.row, scope.index)"
+ >流程图
+ </el-button>
+ <el-button
+ type="primary"
+ text
+ icon="el-icon-delete"
+ v-if="permission.flow_manager_remove"
+ @click.stop="handleSlotDelete(scope.row, scope.index)"
+ >删除
+ </el-button>
+ </template>
+ <template #tenantId="{ row }">
+ <el-tag>{{ row.tenantId === '' ? '通用' : row.tenantId }}</el-tag>
+ </template>
+ <template #version="{ row }">
+ <el-tag>v{{ row.version }}</el-tag>
+ </template>
+ <template #suspensionState="{ row }">
+ <el-tag>{{ row.suspensionState === 1 ? '激活' : '挂起' }}</el-tag>
+ </template>
+ <template #category="{ row }">
+ <el-tag>{{ row.categoryName }}</el-tag>
+ </template>
+ </avue-crud>
+ <flow-design
+ v-if="this.website.design.designMode"
+ is-dialog
+ v-model:is-display="flowBox"
+ :process-definition-id="processDefinitionId"
+ ></flow-design>
+ <el-dialog v-else title="流程图" append-to-body v-model="flowBox" :fullscreen="true">
+ <iframe
+ :src="flowUrl"
+ width="100%"
+ height="700"
+ title="流程图"
+ frameBorder="no"
+ border="0"
+ marginWidth="0"
+ marginHeight="0"
+ scrolling="no"
+ allowTransparency="yes"
+ >
+ </iframe>
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="flowBox = false">关 闭</el-button>
+ </span>
+ </template>
+ </el-dialog>
+ <el-dialog title="流程变更" append-to-body v-model="stateBox" width="20%">
+ <el-form :model="form" ref="form" label-width="80px">
+ <el-form-item label="流程状态">
+ <el-select v-model="flowState" placeholder="请选择" value="">
+ <el-option
+ v-for="item in stateOptions"
+ :key="item.value"
+ :label="item.label"
+ :value="item.value"
+ >
+ </el-option>
+ </el-select>
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="stateBox = false">关 闭</el-button>
+ <el-button type="primary" @click="handleDoState">确 定</el-button>
+ </span>
+ </template>
+ </el-dialog>
+ </basic-container>
+</template>
+
+<script>
+import { mapGetters } from 'vuex';
+import { managerList, changeState, deleteDeployment } from '@/api/flow/flow';
+import { flowCategory } from '@/utils/flow';
+
+export default {
+ data() {
+ return {
+ form: {},
+ mode: '1',
+ selectionId: '',
+ selectionList: [],
+ query: {},
+ loading: true,
+ page: {
+ pageSize: 10,
+ currentPage: 1,
+ total: 0,
+ },
+ processDefinitionId: '',
+ flowBox: false,
+ flowUrl: '',
+ stateBox: false,
+ flowState: '',
+ stateOptions: [
+ {
+ value: 'active',
+ label: '激活',
+ },
+ {
+ value: 'suspend',
+ label: '挂起',
+ },
+ ],
+ option: {
+ tip: false,
+ searchShow: true,
+ searchMenuSpan: 6,
+ border: true,
+ index: true,
+ selection: true,
+ editBtn: false,
+ addBtn: false,
+ viewBtn: false,
+ delBtn: false,
+ dialogWidth: 900,
+ menuWidth: 250,
+ dialogClickModal: false,
+ column: [
+ {
+ label: '租户编号',
+ prop: 'tenantId',
+ slot: true,
+ width: 120,
+ },
+ {
+ label: '流程主键',
+ prop: 'id',
+ },
+ {
+ label: '流程标识',
+ prop: 'key',
+ search: true,
+ width: 150,
+ },
+ {
+ label: '流程名称',
+ prop: 'name',
+ width: 150,
+ },
+ {
+ label: '流程分类',
+ type: 'select',
+ row: true,
+ dicUrl: '/blade-system/dict/dictionary?code=flow',
+ props: {
+ label: 'dictValue',
+ value: 'dictKey',
+ },
+ dataType: 'number',
+ slot: true,
+ prop: 'category',
+ search: true,
+ width: 100,
+ },
+ {
+ label: '流程版本',
+ prop: 'version',
+ slot: true,
+ width: 85,
+ },
+ {
+ label: '状态',
+ prop: 'suspensionState',
+ slot: true,
+ width: 80,
+ },
+ {
+ label: '部署时间',
+ prop: 'deploymentTime',
+ width: 170,
+ },
+ ],
+ },
+ data: [],
+ };
+ },
+ watch: {
+ mode() {
+ this.onLoad(this.page);
+ },
+ },
+ computed: {
+ ...mapGetters(['permission']),
+ permissionList() {
+ return {
+ delBtn: this.validData(this.permission.flow_manager_remove, false),
+ };
+ },
+ ids() {
+ let ids = [];
+ this.selectionList.forEach(ele => {
+ ids.push(ele.id);
+ });
+ return ids.join(',');
+ },
+ deploymentIds() {
+ let ids = [];
+ this.selectionList.forEach(ele => {
+ ids.push(ele.deploymentId);
+ });
+ return ids.join(',');
+ },
+ },
+ methods: {
+ searchReset() {
+ this.query = {};
+ this.onLoad(this.page);
+ },
+ searchChange(params, done) {
+ this.query = params;
+ this.page.currentPage = 1;
+ this.onLoad(this.page, params);
+ done();
+ },
+ selectionChange(list) {
+ this.selectionList = list;
+ },
+ selectionClear() {
+ this.selectionList = [];
+ this.$refs.crud.toggleSelection();
+ },
+ handleDelete() {
+ if (this.selectionList.length === 0) {
+ this.$message.warning('请选择至少一条数据');
+ return;
+ }
+ this.$confirm('确定将选择数据删除?', {
+ confirmButtonText: '确定',
+ cancelButtonText: '取消',
+ type: 'warning',
+ })
+ .then(() => {
+ return deleteDeployment(this.deploymentIds);
+ })
+ .then(() => {
+ this.$message({
+ type: 'success',
+ message: '操作成功!',
+ });
+ this.$refs.crud.toggleSelection();
+ this.onLoad(this.page);
+ });
+ },
+ handleSlotDelete(row) {
+ this.$confirm('确定将选择数据删除?', {
+ confirmButtonText: '确定',
+ cancelButtonText: '取消',
+ type: 'warning',
+ })
+ .then(() => {
+ return deleteDeployment(row.deploymentId);
+ })
+ .then(() => {
+ this.$message({
+ type: 'success',
+ message: '操作成功!',
+ });
+ this.$refs.crud.toggleSelection();
+ this.onLoad(this.page);
+ });
+ },
+ handleState(row) {
+ this.stateBox = true;
+ this.selectionId = row.id;
+ },
+ handleDoState() {
+ if (!this.flowState) {
+ this.$message({
+ type: 'warn',
+ message: '请先选择流程状态!',
+ });
+ return;
+ }
+ changeState({ processId: this.selectionId, state: this.flowState }).then(res => {
+ const data = res.data;
+ if (data.success) {
+ this.$message({
+ type: 'success',
+ message: data.msg,
+ });
+ this.stateBox = false;
+ this.onLoad(this.page);
+ } else {
+ this.$message({
+ type: 'warn',
+ message: data.msg,
+ });
+ }
+ });
+ },
+ handleImage(row) {
+ if (this.website.design.designMode) {
+ this.processDefinitionId = row.id;
+ } else {
+ this.flowUrl = `/blade-flow/process/resource-view?processDefinitionId=${row.id}`;
+ }
+ this.flowBox = true;
+ },
+ currentChange(currentPage) {
+ this.page.currentPage = currentPage;
+ },
+ sizeChange(pageSize) {
+ this.page.pageSize = pageSize;
+ },
+ refreshChange() {
+ this.onLoad(this.page, this.query);
+ },
+ onLoad(page, params = {}) {
+ const values = {
+ ...params,
+ category: params.category ? flowCategory(params.category) : null,
+ mode: this.mode,
+ };
+ this.loading = true;
+ managerList(page.currentPage, page.pageSize, Object.assign(values, this.query)).then(res => {
+ const data = res.data.data;
+ this.page.total = data.total;
+ this.data = data.records;
+ this.loading = false;
+ this.selectionClear();
+ });
+ },
+ },
+};
+</script>
diff --git a/applications/task-work-order/src/views/flow/model.vue b/applications/task-work-order/src/views/flow/model.vue
new file mode 100644
index 0000000..fa06a03
--- /dev/null
+++ b/applications/task-work-order/src/views/flow/model.vue
@@ -0,0 +1,548 @@
+<template>
+ <basic-container>
+ <avue-crud
+ :option="option"
+ :table-loading="loading"
+ :data="data"
+ ref="crud"
+ v-model="form"
+ v-model:page="page"
+ @search-change="searchChange"
+ @search-reset="searchReset"
+ @selection-change="selectionChange"
+ @current-change="currentChange"
+ @size-change="sizeChange"
+ @refresh-change="refreshChange"
+ @on-load="onLoad"
+ >
+ <template #menu-left>
+ <el-button
+ type="primary"
+ size="default"
+ icon="el-icon-circle-plus"
+ v-if="permission.flow_model_create"
+ plain
+ @click="handleCreate"
+ >创 建
+ </el-button>
+ <el-button
+ type="danger"
+ size="default"
+ icon="el-icon-delete"
+ v-if="permission.flow_model_delete"
+ plain
+ @click="handleDelete"
+ >删 除
+ </el-button>
+ </template>
+ <template #menu="scope">
+ <el-button
+ type="primary"
+ text
+ size="default"
+ icon="el-icon-setting"
+ v-if="permission.flow_model_update"
+ @click.stop="handleUpdate(scope.row, scope.index)"
+ >配置
+ </el-button>
+ <el-button
+ type="primary"
+ text
+ size="default"
+ icon="el-icon-upload"
+ v-if="permission.flow_model_deploy"
+ @click.stop="handleDeploy(scope.row, scope.index)"
+ >部署
+ </el-button>
+ <el-button
+ type="primary"
+ text
+ size="default"
+ icon="el-icon-delete"
+ v-if="permission.flow_model_delete"
+ @click.stop="handleSlotDelete(scope.row, scope.index)"
+ >删除
+ </el-button>
+ </template>
+ <template #version="{ row }">
+ <el-tag>v{{ row.version }}</el-tag>
+ </template>
+ </avue-crud>
+ <el-dialog
+ v-if="this.website.design.designMode"
+ title="流程配置"
+ append-to-body
+ destroy-on-close
+ v-model="flowBox"
+ :close-on-press-escape="false"
+ :fullscreen="true"
+ :before-close="handleNutflowClose"
+ class="nf-dialog"
+ >
+ <nf-design-base
+ v-if="nutflowOption.step === 1"
+ class="animated fadeIn"
+ style="height: calc(100vh - 108px)"
+ ref="nf-design"
+ :options="nutflowOption.step1"
+ ></nf-design-base>
+ <nf-design-base
+ v-if="nutflowOption.step === 2"
+ class="animated fadeIn"
+ style="height: calc(100vh - 108px)"
+ ref="nf-design-view"
+ :options="nutflowOption.step2"
+ ></nf-design-base>
+ <template #footer>
+ <span class="avue-dialog__footer">
+ <el-button @click="handleNutflowClose(() => {}, true)">取 消</el-button>
+ <el-button v-if="nutflowOption.step === 1" type="success" @click="handleStep(1)"
+ >下 一 步</el-button
+ >
+ <el-button v-if="nutflowOption.step === 2" type="success" @click="handleStep(-1)"
+ >上 一 步</el-button
+ >
+ <el-button v-if="nutflowOption.step === 2" type="primary" @click="handleSubmitModel"
+ >确 定</el-button
+ >
+ </span>
+ </template>
+ </el-dialog>
+ <el-dialog v-else title="流程配置" append-to-body v-model="flowBox" :fullscreen="true">
+ <iframe
+ :src="flowUrl"
+ width="100%"
+ height="700"
+ title="流程设计器"
+ frameBorder="no"
+ border="0"
+ marginWidth="0"
+ marginHeight="0"
+ scrolling="no"
+ allowTransparency="yes"
+ >
+ </iframe>
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="flowBox = false">取 消</el-button>
+ <el-button type="primary" @click="handleRefresh">确 定</el-button>
+ </span>
+ </template>
+ </el-dialog>
+ <el-dialog title="流程部署" append-to-body v-model="deployBox" width="20%">
+ <avue-form ref="form" :option="optionDeploy" v-model="form" @submit="handleSubmit" />
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="deployBox = false">取 消</el-button>
+ <el-button type="primary" @click="handleDoDeploy" :loading="deployLoading"
+ >确 定</el-button
+ >
+ </span>
+ </template>
+ </el-dialog>
+ </basic-container>
+</template>
+
+<script>
+import { mapGetters } from 'vuex';
+import { modelList, removeModel, deployModel, submitModel, detail } from '@/api/flow/flow';
+import { flowCategory } from '@/utils/flow';
+
+export default {
+ data() {
+ return {
+ form: {},
+ optionDeploy: {
+ menuBtn: false,
+ column: [
+ {
+ label: '流程类型',
+ type: 'select',
+ dicUrl: '/blade-system/dict/dictionary?code=flow',
+ props: {
+ label: 'dictValue',
+ value: 'dictKey',
+ },
+ dataType: 'number',
+ slot: true,
+ prop: 'categoryValue',
+ search: true,
+ span: 24,
+ rules: [
+ {
+ required: true,
+ message: '请选择流程类型',
+ trigger: 'blur',
+ },
+ ],
+ },
+ {
+ label: '流程模式',
+ prop: 'flowMode',
+ type: 'radio',
+ dicData: [
+ {
+ label: '通用流程',
+ value: 1,
+ },
+ {
+ label: '定制流程',
+ value: 2,
+ },
+ ],
+ value: 1,
+ span: 24,
+ rules: [
+ {
+ required: true,
+ message: '请选择流程模式',
+ trigger: 'blur',
+ },
+ ],
+ },
+ {
+ label: '所属租户',
+ prop: 'tenantId',
+ type: 'tree',
+ multiple: true,
+ dicUrl: '/blade-system/tenant/select',
+ props: {
+ label: 'tenantName',
+ value: 'tenantId',
+ },
+ display: false,
+ span: 24,
+ rules: [
+ {
+ required: true,
+ message: '请选择所属租户',
+ trigger: 'blur',
+ },
+ ],
+ },
+ ],
+ },
+ selectionId: '',
+ selectionList: [],
+ query: {},
+ loading: true,
+ deployLoading: false,
+ page: {
+ pageSize: 10,
+ currentPage: 1,
+ total: 0,
+ },
+ deployBox: false,
+ flowBox: false,
+ flowUrl: '',
+ option: {
+ tip: false,
+ searchShow: true,
+ searchMenuSpan: 6,
+ border: true,
+ index: true,
+ selection: true,
+ editBtn: false,
+ addBtn: false,
+ viewBtn: false,
+ delBtn: false,
+ dialogWidth: 900,
+ menuWidth: 220,
+ dialogClickModal: false,
+ size: 'default',
+ searchSize: 'default',
+ column: [
+ {
+ label: '模型主键',
+ prop: 'id',
+ },
+ {
+ label: '模型标识',
+ prop: 'modelKey',
+ search: true,
+ overHidden: true,
+ },
+ {
+ label: '模型名称',
+ prop: 'name',
+ search: true,
+ overHidden: true,
+ },
+ {
+ label: '流程版本',
+ prop: 'version',
+ slot: true,
+ overHidden: true,
+ },
+ {
+ label: '创建时间',
+ prop: 'created',
+ overHidden: true,
+ },
+ {
+ label: '更新时间',
+ prop: 'lastUpdated',
+ overHidden: true,
+ },
+ ],
+ },
+ data: [],
+ nutflowOption: {
+ process: {},
+ step: 1,
+ step1: {
+ toolbar: [
+ 'open',
+ 'create',
+ 'fit',
+ 'zoom-in',
+ 'zoom-out',
+ 'undo',
+ 'redo',
+ 'import',
+ 'preview',
+ ],
+ },
+ step2: {
+ mode: 'view',
+ simulation: true,
+ minimap: true,
+ },
+ },
+ };
+ },
+ watch: {
+ 'form.flowMode'() {
+ this.$refs.form.option.column.filter(item => {
+ if (item.prop === 'tenantId') {
+ item.display = this.form.flowMode === 2;
+ }
+ });
+ },
+ },
+ computed: {
+ ...mapGetters(['permission']),
+ ids() {
+ let ids = [];
+ this.selectionList.forEach(ele => {
+ ids.push(ele.id);
+ });
+ return ids.join(',');
+ },
+ },
+ methods: {
+ handleSubmitModel() {
+ const registry = this.$refs['nf-design-view'].getElementRegistry().getAll();
+ const { businessObject } = registry[0];
+ const { id, name, documentation } = businessObject;
+ const description = documentation && documentation.length > 0 ? documentation[0].text : null;
+ const params = {
+ ...this.nutflowOption.process,
+ modelKey: id,
+ name,
+ description,
+ modelEditorXml: this.nutflowOption.process.xml,
+ };
+ submitModel(params).then(() => {
+ this.$message.success('操作成功');
+ this.handleNutflowClose();
+ this.onLoad(this.page, this.query);
+ });
+ },
+ handleStep(step) {
+ if (step === 1) {
+ // 下一步
+ this.$refs['nf-design'].getData('xml').then(data => {
+ this.nutflowOption.step1.xml = data;
+ this.nutflowOption.step2.xml = data;
+ this.nutflowOption.process.xml = data;
+ this.nutflowOption.step = 2;
+ });
+ } else this.nutflowOption.step = 1;
+ },
+ handleNutflowClose(done, flag) {
+ const initOption = {
+ process: {},
+ step: 1,
+ step1: {
+ toolbar: [
+ 'open',
+ 'create',
+ 'fit',
+ 'zoom-in',
+ 'zoom-out',
+ 'undo',
+ 'redo',
+ 'import',
+ 'preview',
+ ],
+ },
+ step2: {
+ mode: 'view',
+ simulation: true,
+ minimap: true,
+ },
+ };
+ if (done || flag) {
+ this.$confirm('确定要关闭吗?关闭未保存的修改都会丢失。', '警告', {
+ type: 'warning',
+ })
+ .then(() => {
+ this.nutflowOption = initOption;
+ if (typeof done == 'function') done();
+ this.flowBox = false;
+ })
+ .catch(() => {});
+ } else {
+ this.nutflowOption = initOption;
+ this.flowBox = false;
+ }
+ },
+ handleSubmit(form, done) {
+ this.deployLoading = true;
+ deployModel({
+ modelId: this.selectionId,
+ category: flowCategory(form.categoryValue),
+ tenantIds: form.tenantId ? form.tenantId.join(',') : '',
+ }).then(res => {
+ const data = res.data;
+ if (data.success) {
+ this.$message({
+ type: 'success',
+ message: data.msg,
+ });
+ done();
+ this.$refs.form.resetForm();
+ this.deployBox = false;
+ this.deployLoading = false;
+ } else {
+ done();
+ this.deployLoading = false;
+ this.$message({
+ type: 'warn',
+ message: data.msg,
+ });
+ }
+ });
+ },
+ searchReset() {
+ this.query = {};
+ this.onLoad(this.page);
+ },
+ searchChange(params, done) {
+ this.query = params;
+ this.page.currentPage = 1;
+ this.onLoad(this.page, params);
+ done();
+ },
+ selectionChange(list) {
+ this.selectionList = list;
+ },
+ selectionClear() {
+ this.selectionList = [];
+ this.$refs.crud.toggleSelection();
+ },
+ handleDelete() {
+ if (this.selectionList.length === 0) {
+ this.$message.warning('请选择至少一条数据');
+ return;
+ }
+ this.$confirm('确定将选择数据删除?', {
+ confirmButtonText: '确定',
+ cancelButtonText: '取消',
+ type: 'warning',
+ })
+ .then(() => {
+ return removeModel(this.ids);
+ })
+ .then(() => {
+ this.$message({
+ type: 'success',
+ message: '操作成功!',
+ });
+ this.$refs.crud.toggleSelection();
+ this.onLoad(this.page);
+ });
+ },
+ handleCreate() {
+ if (!this.website.design.designMode) {
+ this.flowUrl = `${this.website.design.designUrl}/index.html`;
+ }
+ this.flowBox = true;
+ },
+ handleRefresh() {
+ this.flowBox = false;
+ this.onLoad(this.page);
+ },
+ handleUpdate(row) {
+ if (this.website.design.designMode) {
+ detail({ id: row.id }).then(res => {
+ const data = res.data.data;
+ const { modelEditorXml } = data;
+ this.nutflowOption.step1.xml = modelEditorXml;
+ this.nutflowOption.process = data;
+ });
+ } else {
+ this.flowUrl = `${this.website.design.designUrl}/index.html#/editor/${row.id}`;
+ }
+ this.flowBox = true;
+ },
+ handleDeploy(row) {
+ this.deployBox = true;
+ this.selectionId = row.id;
+ },
+ handleDoDeploy() {
+ this.$refs.form.submit();
+ },
+ handleSlotDelete(row) {
+ this.$confirm('确定将选择数据删除?', {
+ confirmButtonText: '确定',
+ cancelButtonText: '取消',
+ type: 'warning',
+ })
+ .then(() => {
+ return removeModel(row.id);
+ })
+ .then(() => {
+ this.$message({
+ type: 'success',
+ message: '操作成功!',
+ });
+ this.$refs.crud.toggleSelection();
+ this.onLoad(this.page);
+ });
+ },
+ currentChange(currentPage) {
+ this.page.currentPage = currentPage;
+ },
+ sizeChange(pageSize) {
+ this.page.pageSize = pageSize;
+ },
+ refreshChange() {
+ this.onLoad(this.page, this.query);
+ },
+ onLoad(page, params = {}) {
+ this.loading = true;
+ modelList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
+ const data = res.data.data;
+ this.page.total = data.total;
+ this.data = data.records;
+ this.loading = false;
+ this.selectionClear();
+ });
+ },
+ },
+};
+</script>
+
+<style lang="scss">
+.nf-dialog {
+ .el-dialog__body {
+ padding: 5px;
+ }
+
+ .avue-dialog__footer {
+ text-align: center;
+ }
+}
+</style>
diff --git a/applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/FormDiaLog.vue b/applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/FormDiaLog.vue
index 22b4e67..bf04ec8 100644
--- a/applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/FormDiaLog.vue
+++ b/applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/FormDiaLog.vue
@@ -28,30 +28,40 @@
</div>
<!-- 工单信息展示 -->
- <div class="work-order-info" v-if="selectedWorkOrder">
+ <div class="work-order-info">
<el-row class="detail-row-view">
<el-col :span="12">
<div class="info-item">
<span class="label">工单类型</span>
- <span class="val">{{ getDictLabel(selectedWorkOrder.workOrderType, dictObj.workOrderType) }}</span>
+ <span class="val" v-if="selectedWorkOrder">
+ {{ getDictLabel(selectedWorkOrder.workOrderType, dictObj.workOrderType) }}
+ </span>
+ <span class="val grayText" v-else>选择工单后自动带出</span>
</div>
</el-col>
<el-col :span="12">
<div class="info-item">
<span class="label">执行时间范围</span>
- <span class="val">{{ selectedWorkOrder.executeStartTime }} ~ {{ selectedWorkOrder.executeEndTime }}</span>
+ <span class="val" v-if="selectedWorkOrder">
+ {{ selectedWorkOrder.executeStartTime }} ~ {{ selectedWorkOrder.executeEndTime }}
+ </span>
+ <span class="val grayText" v-else>选择工单后自动带出</span>
</div>
</el-col>
<el-col :span="12">
<div class="info-item">
<span class="label">设备需求</span>
- <span class="val">{{ getDictLabel(selectedWorkOrder.deviceLoadDemand, dictObj.deviceLoadDemand) }}</span>
+ <span class="val" v-if="selectedWorkOrder">
+ {{ getDictLabel(selectedWorkOrder.deviceLoadDemand, dictObj.deviceLoadDemand) }}
+ </span>
+ <span class="val grayText" v-else>选择工单后自动带出</span>
</div>
</el-col>
<el-col :span="12">
<div class="info-item">
<span class="label">工单备注</span>
- <span class="val">{{ selectedWorkOrder.remark }}</span>
+ <span class="val" v-if="selectedWorkOrder">{{ selectedWorkOrder.remark }}</span>
+ <span class="val grayText" v-else>选择工单后自动带出</span>
</div>
</el-col>
</el-row>
@@ -65,7 +75,11 @@
<el-table-column width="50" label="">
<template v-slot="{ $index }">{{ $index + 1 }}</template>
</el-table-column>
- <el-table-column prop="patrolTaskName" label="巡查任务名称" min-width="120">
+ <el-table-column prop="patrolTaskName" min-width="120">
+ <template #header>
+ <span class="required-star">*</span>
+ 巡查任务名称
+ </template>
<template v-slot="{ row }">
<el-input
class="gd-input"
@@ -75,14 +89,17 @@
/>
</template>
</el-table-column>
- <el-table-column prop="patrolTaskType" label="巡查任务类型" min-width="140">
+ <el-table-column prop="patrolTaskType" min-width="140">
+ <template #header>
+ <span class="required-star">*</span>
+ 巡查任务类型
+ </template>
<template v-slot="{ row, $index }">
<el-select
class="gd-select"
popper-class="gd-select-popper"
v-model="row.patrolTaskType"
placeholder="请选择"
- :disabled="dialogReadonly"
@change="handlePatrolTaskTypeChange($index, $event)"
>
<el-option
@@ -94,7 +111,11 @@
</el-select>
</template>
</el-table-column>
- <el-table-column prop="executeTime" label="任务执行时间" min-width="180">
+ <el-table-column prop="executeTime" min-width="180">
+ <template #header>
+ <span class="required-star">*</span>
+ 任务执行时间
+ </template>
<template v-slot="{ row }">
<el-date-picker
class="gd-date-picker"
@@ -107,7 +128,11 @@
/>
</template>
</el-table-column>
- <el-table-column prop="patrolRouteUrl" label="巡查任务航线" min-width="120">
+ <el-table-column prop="patrolRouteUrl" min-width="120">
+ <template #header>
+ <span class="required-star">*</span>
+ 巡查任务航线
+ </template>
<template v-slot="{ row }">
<el-select
class="gd-select"
@@ -120,9 +145,14 @@
</el-select>
</template>
</el-table-column>
- <el-table-column prop="recommendFlyerName" label="推荐飞手" min-width="120">
+ <el-table-column prop="recommendFlyerName" min-width="120">
+ <template #header>
+ <span class="required-star">*</span>
+ 推荐飞手
+ </template>
<template v-slot="{ row, $index }">
<el-select
+ no-data-text="暂无推荐飞手"
class="gd-select"
popper-class="gd-select-popper"
v-model="row.recommendFlyerName"
@@ -139,7 +169,11 @@
</el-select>
</template>
</el-table-column>
- <el-table-column prop="deviceId" label="选择设备" min-width="120">
+ <el-table-column prop="deviceId" min-width="120">
+ <template #header>
+ <span class="required-star">*</span>
+ 选择设备
+ </template>
<template v-slot="{ row }">
<el-select
class="gd-select"
@@ -213,9 +247,7 @@
deviceId: null,
taskDesc: '',
})
-// todo 时间线
const dictObj = inject('dictObj')
-
const emit = defineEmits(['success'])
const visible = defineModel() // 弹框显隐
const dialogMode = ref('add') // 弹框模式
@@ -234,12 +266,21 @@
const routeOptions = inject('routeOptions')
// 子任务列表
-const taskList = ref([initTaskRow()])
+const taskList = ref([initTaskRow(), initTaskRow(), initTaskRow(), initTaskRow(), initTaskRow()])
// 处理工单选择变化
function handleWorkOrderChange(val) {
if (val) {
selectedWorkOrder.value = workOrderList.value.find(item => item.id === val)
+ // 将工单类型赋值给所有子任务行
+ const workOrderType = selectedWorkOrder.value?.workOrderType
+ if (workOrderType) {
+ taskList.value.forEach((task, index) => {
+ task.patrolTaskType = workOrderType
+ // 获取对应的飞手列表
+ getFlyerList(index, workOrderType)
+ })
+ }
} else {
selectedWorkOrder.value = null
}
@@ -247,7 +288,17 @@
// 添加任务行
function addTaskRow() {
- taskList.value.push(initTaskRow())
+ const newTask = initTaskRow()
+ // 如果已选择工单,自动填充工单类型
+ if (selectedWorkOrder.value?.workOrderType) {
+ newTask.patrolTaskType = selectedWorkOrder.value.workOrderType
+ }
+ const newIndex = taskList.value.length
+ taskList.value.push(newTask)
+ // 获取对应的飞手列表
+ if (newTask.patrolTaskType) {
+ getFlyerList(newIndex, newTask.patrolTaskType)
+ }
}
// 删除任务行
@@ -270,7 +321,7 @@
// 获取工单列表
async function getWorkOrderList() {
- const res = await gdWorkOrderPageApi({ size: 999, workOrderStatus: dialogMode.value ==='add' ? '20' : '' })
+ const res = await gdWorkOrderPageApi({ size: 999, workOrderStatus: dialogMode.value === 'add' ? '20' : '' })
workOrderList.value = res?.data?.data?.records ?? []
}
@@ -308,19 +359,29 @@
}
)
+// 判断一行是否已填写(至少填写了任务名称)
+function isTaskRowFilled(task) {
+ return !!(task.patrolTaskName || task.executeTime || task.patrolRouteUrl || task.recommendFlyerName || task.deviceId)
+}
+
// 提交新增
async function handleSubmit() {
if (!selectedWorkOrderId.value) {
ElMessage.warning('请选择关联工单')
return
}
- if (!taskList.value.length) {
- ElMessage.warning('请添加至少一个子任务')
+ // 过滤出已填写的任务行
+ const filledTasks = taskList.value.filter(task => isTaskRowFilled(task))
+ if (!filledTasks.length) {
+ ElMessage.warning('请至少填写一个子任务')
return
}
- // 校验子任务
+ // 只校验已填写的子任务
for (let i = 0; i < taskList.value.length; i++) {
const task = taskList.value[i]
+ // 跳过未填写的行
+ if (!isTaskRowFilled(task)) continue
+ // 已填写的行需要校验完整性
if (!task.patrolTaskName) {
ElMessage.warning(`第${i + 1}行: 请输入巡查任务名称`)
return
@@ -348,8 +409,9 @@
}
submitting.value = true
try {
- const preID = taskList.value[0].id
- const dataList = taskList.value.map(item => ({
+ const preID = filledTasks[0]?.id
+ // 只提交已填写的任务行
+ const dataList = filledTasks.map(item => ({
...item,
workOrderId: selectedWorkOrderId.value,
id: undefined,
@@ -371,7 +433,7 @@
if (dialogMode.value === 'add') {
selectedWorkOrderId.value = null
selectedWorkOrder.value = null
- taskList.value = [initTaskRow()]
+ taskList.value = [initTaskRow(), initTaskRow(), initTaskRow(), initTaskRow(), initTaskRow()]
flyerListMap.value = {}
} else {
// 编辑/查看模式 - 加载已有数据
@@ -386,9 +448,7 @@
}
}
-onMounted(() => {
-
-})
+onMounted(() => {})
defineExpose({ open })
</script>
@@ -401,4 +461,12 @@
display: flex;
align-items: center;
}
+.required-star {
+ color: #f56c6c;
+ margin-right: 4px;
+}
+
+.grayText {
+ color: #9f9f9f !important;
+}
</style>
diff --git a/applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/ViewDiaLog.vue b/applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/ViewDiaLog.vue
index 713e5ac..fdee741 100644
--- a/applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/ViewDiaLog.vue
+++ b/applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/ViewDiaLog.vue
@@ -31,7 +31,7 @@
<div class="detail-right">
<!-- 查看模式 -->
<div class="view-container" v-if="dialogReadonly">
- <div class="detail-title">工单详情</div>
+ <div class="detail-title">巡查任务详情</div>
<el-row class="detail-row-view">
<el-col :span="12">
<div class="label">巡查任务名称</div>
diff --git a/applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/index.vue b/applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/index.vue
index e4b90d0..e50d0aa 100644
--- a/applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/index.vue
+++ b/applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/index.vue
@@ -1,8 +1,8 @@
<template>
<basic-container>
<el-tabs class="gd-tabs" v-model="activeName" @tab-click="tabsClick">
- <el-tab-pane label="全部 " name="all"></el-tab-pane>
- <el-tab-pane label="我的巡查任务" name="my"></el-tab-pane>
+ <el-tab-pane :label="`全部(${allTotal})`" name="all"></el-tab-pane>
+ <el-tab-pane :label="`我的巡查任务(${myTotal})`" name="my"></el-tab-pane>
</el-tabs>
<el-form ref="queryParamsRef" :model="searchParams" class="gd-search-form">
<el-form-item label="任务名称" prop="patrolTaskName">
@@ -72,7 +72,9 @@
</el-form>
<div class="gd-table-toolbar">
- <el-button v-if="permission.flyOrder_add" :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">拆分工单</el-button>
+ <el-button v-if="permission.flyOrder_add" :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">
+ 拆分工单
+ </el-button>
</div>
<div class="gd-table-container" v-loading="loading">
@@ -98,7 +100,7 @@
<el-table-column prop="taskDesc" show-overflow-tooltip label="巡查任务描述" />
<el-table-column label="操作" class-name="operation-btns">
<template v-slot="{ row }">
- <el-link type="primary" @click="viewDiaLogView(row)">查看</el-link>
+ <el-link type="primary" @click="viewDiaLogView(row)">查看</el-link>
</template>
</el-table-column>
</el-table>
@@ -154,6 +156,8 @@
const searchParams = ref(initSearchParams()) // 查询参数
const dateRange = ref([]) // 执行时间范围
const total = ref(0) // 总条数
+const allTotal = ref(0) // 全部tab总条数
+const myTotal = ref(0) // 我的巡查任务tab总条数
const loading = ref(true) // 列表加载中
const list = ref([]) // 列表数据
const selectedIds = ref([]) // 勾选的ID列表
@@ -258,7 +262,25 @@
})
}
+// 获取两个tab的总条数
+async function getTabTotals() {
+ const allRes = await gdPatrolTaskPageApi({
+ current: 1,
+ size: 1,
+ createUser: '',
+ })
+ allTotal.value = allRes?.data?.data?.total ?? 0
+
+ const myRes = await gdPatrolTaskPageApi({
+ current: 1,
+ size: 1,
+ createUser: store.state.user.userInfo.user_id,
+ })
+ myTotal.value = myRes?.data?.data?.total ?? 0
+}
+
onMounted(() => {
+ getTabTotals()
getList()
getDictList()
getAirList()
diff --git a/applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue b/applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue
index d7bc9c3..cb97552 100644
--- a/applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue
+++ b/applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue
@@ -57,6 +57,7 @@
</el-row>
<div class="detail-title" :style="{ marginTop: pxToRem(20) }">已选设备</div>
<el-table class="setHeight gd-dialog-table" ref="deviceTableRef" :data="deviceList" row-key="id">
+ <el-table-column type="index" label="序号" />
<el-table-column prop="nickname" label="设备名称" />
<el-table-column prop="deviceName" label="设备型号" />
<el-table-column prop="devicePayload" label="设备负载" />
@@ -90,6 +91,7 @@
:disabled="dialogReadonly"
label-width="90px"
>
+ <div class="detail-title">工单详情</div>
<el-row>
<el-col :span="12">
<el-form-item label="工单名称" prop="workOrderName">
@@ -161,7 +163,10 @@
</el-col>
</el-row>
</el-form>
- <div class="detail-title" :style="{ marginTop: pxToRem(20) }">工单执行范围</div>
+ <div class="detail-title" :style="{ marginTop: pxToRem(20) }">
+ <span class="required-star" v-if="!dialogReadonly">*</span>
+ 工单执行范围
+ </div>
<CommonCesiumMap
ref="mapRef"
class="gd-cesium"
@@ -175,12 +180,17 @@
/>
</div>
<div class="rightBox" v-if="!dialogReadonly">
- <div class="detail-title">推荐设备</div>
+ <div class="detail-title">
+ <span class="required-star" v-if="!dialogReadonly">*</span>
+ 推荐设备
+ </div>
<el-table
class="separateTable"
- :empty-text="(formData.deviceLoadDemand && pointList.length) ? '暂无数据' : '请先选择设备需求并在地图上绘制工单范围'"
+ :empty-text="
+ formData.deviceLoadDemand && pointList.length ? '暂无数据' : '请先选择设备需求并在地图上绘制工单范围'
+ "
ref="deviceTableRef"
- :data="(formData.deviceLoadDemand && pointList.length) ? deviceList : []"
+ :data="formData.deviceLoadDemand && pointList.length ? deviceList : []"
row-key="id"
@selection-change="handleDeviceSelectionChange"
>
@@ -193,12 +203,8 @@
</div>
<template #footer>
- <el-button
- v-if="['11', '21', '23','31', '60'].includes(gdStatus)"
- @click="viewDescription"
- color="#F2F3F5"
- >
- {{ gdStatusObj[gdStatus].reason }}
+ <el-button v-if="['11', '21', '23', '31', '60'].includes(gdStatus)" @click="viewDescription" color="#F2F3F5">
+ {{ gdStatusObj[gdStatus]?.reason }}
</el-button>
<template v-if="!suddenlyEdit">
@@ -212,7 +218,7 @@
<el-button
color="#4C34FF"
@click="requestModification"
- v-if="['20'].includes(gdStatus) && permission.order_applyNegotiation"
+ v-if="['20'].includes(gdStatus) && permission.order_applyEdit"
>
申请修改
</el-button>
@@ -239,7 +245,7 @@
</template>
<template v-if="permission.order_controlCancel">
<el-button v-if="gdStatus === '21'" @click="statusChange(6)" color="#F2F3F5">驳回</el-button>
- <el-button v-if="gdStatus === '21'" @click="statusChange(5)" color="#4C34FF">同意</el-button>
+ <el-button v-if="gdStatus === '21'" @click="statusChange(5)" color="#4C34FF">通过</el-button>
</template>
<el-button v-if="gdStatus === '30' && permission.order_applyNegotiation" @click="addDescription" color="#4C34FF">
协商修改
@@ -326,14 +332,15 @@
const hasPatrolTaskList = computed(() => ['30', '40', '50', '60'].includes(String(formData.value.workOrderStatus)))
const gdStatusObj = {
- '10': { reason: '驳回原因', operationType: '2' },
- '11': { reason: '驳回原因' },
- '20': { reason: '取消原因', operationType: '3' },
+ '10': { reason: '拒单原因', operationType: '2', title: '拒绝接单' },
+ '11': { reason: '拒单原因' },
+ '20': { reason: '原因', operationType: '3',title:'申请取消' },
+ '21': { reason: '取消原因' },
'23': { reason: '取消原因' },
- '30': { reason: '协商修改原因', operationType: '9' },
- '31': { reason: '协商修改原因' },
+ '30': { reason: '修改原因', operationType: '9',title:'协商修改' },
+ '31': { reason: '修改原因' },
'40': { reason: '' },
- '50': { reason: '情况说明', operationType: '11' },
+ '50': { reason: '情况说明', operationType: '11',title:'结算' },
'60': { reason: '情况说明' },
}
@@ -387,6 +394,7 @@
row: formData.value,
type: gdStatusObj[gdStatus.value].operationType,
formLabel: gdStatusObj[gdStatus.value].reason,
+ title: gdStatusObj[gdStatus.value]?.title || '新增'
})
})
}
@@ -636,14 +644,14 @@
</script>
<style lang="scss" scoped>
-.separateTable{
+.separateTable {
height: 0;
flex: 1;
- :deep(){
- .el-scrollbar__view{
+ :deep() {
+ .el-scrollbar__view {
height: 100%;
}
- .el-table__empty-text{
+ .el-table__empty-text {
line-height: normal;
}
}
@@ -701,4 +709,9 @@
height: 200px;
line-height: 200px;
}
+
+.required-star {
+ color: #f56c6c;
+ margin-right: 4px;
+}
</style>
diff --git a/applications/task-work-order/src/views/orderView/orderManage/orderManage/RefuseOrderDialog.vue b/applications/task-work-order/src/views/orderView/orderManage/orderManage/RefuseOrderDialog.vue
index 4a28286..328533f 100644
--- a/applications/task-work-order/src/views/orderView/orderManage/orderManage/RefuseOrderDialog.vue
+++ b/applications/task-work-order/src/views/orderView/orderManage/orderManage/RefuseOrderDialog.vue
@@ -2,7 +2,7 @@
<el-dialog
class="gd-dialog"
v-model="visible"
- :title="titleEnum[dialogMode]"
+ :title="dialogTitle"
@closed="visible = false"
destroy-on-close
:close-on-click-modal="false"
@@ -98,12 +98,14 @@
}
let formLabelStr = ref('原因')
+let dialogTitle = ref('新增')
// 操作类型:1接单,2拒接接单,3申请取消,4申请修改, 5同意取消 6不同意取消 7.同意修改 8.不同意修改 9.协商修改
-async function open({ mode = 'add', row, formLabel, type } = {}) {
+async function open({ mode = 'add', row, formLabel, type,title } = {}) {
dialogMode.value = mode
formLabelStr.value = formLabel
+ dialogTitle.value = dialogMode.value === 'add' ? title : '查看'
formData.value = {
workOrderId: row.id,
operationType: type,
diff --git a/applications/task-work-order/src/views/orderView/orderManage/orderManage/index.vue b/applications/task-work-order/src/views/orderView/orderManage/orderManage/index.vue
index cde7336..7a07a85 100644
--- a/applications/task-work-order/src/views/orderView/orderManage/orderManage/index.vue
+++ b/applications/task-work-order/src/views/orderView/orderManage/orderManage/index.vue
@@ -1,8 +1,8 @@
<template>
<basic-container>
<el-tabs class="gd-tabs" v-model="activeName" @tab-click="tabsClick">
- <el-tab-pane label="全部 " name="all"></el-tab-pane>
- <el-tab-pane label="我的任务工单" name="my"></el-tab-pane>
+ <el-tab-pane :label="`全部(${allTotal})`" name="all"></el-tab-pane>
+ <el-tab-pane :label="`我的任务工单(${myTotal})`" name="my"></el-tab-pane>
</el-tabs>
<el-form ref="queryParamsRef" :model="searchParams" class="gd-search-form">
<el-form-item label="工单名称" prop="workOrderName">
@@ -72,25 +72,9 @@
</el-form>
<div class="gd-table-toolbar">
-
- <el-button
- v-if="permission.order_release"
- :icon="Plus"
- color="#4C34FF"
- type="primary"
- @click="openForm('add')"
- >
+ <el-button v-if="permission.order_release" :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">
新增工单
</el-button>
-<!-- <el-button
- :icon="Delete"
- color="#4C34FF"
- :disabled="!selectedIds.length"
- @click="handleDelete()"
- v-if="permission.order_delete"
- >
- 删除
- </el-button>-->
</div>
<div class="gd-table-container" v-loading="loading">
@@ -126,8 +110,6 @@
<el-table-column label="操作" class-name="operation-btns">
<template v-slot="{ row }">
<el-link type="primary" @click="openFormChange(row)">查看</el-link>
- <!--<el-link type="primary" @click="openForm('edit', row)">编辑</el-link>-->
-<!-- <el-link type="primary" @click="handleDelete(row)" v-if="permission.order_delete">删除</el-link>-->
</template>
</el-table-column>
</el-table>
@@ -182,6 +164,8 @@
const searchParams = ref(initSearchParams()) // 查询参数
const dateRange = ref([]) // 执行时间范围
const total = ref(0) // 总条数
+const allTotal = ref(0) // 全部tab总条数
+const myTotal = ref(0) // 我的任务工单tab总条数
const loading = ref(true) // 列表加载中
const list = ref([]) // 列表数据
const selectedIds = ref([]) // 勾选的ID列表
@@ -281,7 +265,27 @@
})
}
+// 获取两个tab的总条数
+async function getTabTotals() {
+ // 获取全部tab的总数
+ const allRes = await gdWorkOrderPageApi({
+ current: 1,
+ size: 1,
+ createUser: '',
+ })
+ allTotal.value = allRes?.data?.data?.total ?? 0
+
+ // 获取我的任务工单tab的总数
+ const myRes = await gdWorkOrderPageApi({
+ current: 1,
+ size: 1,
+ createUser: store.state.user.userInfo.user_id,
+ })
+ myTotal.value = myRes?.data?.data?.total ?? 0
+}
+
onMounted(() => {
+ getTabTotals()
getList()
getDictList()
})
diff --git a/applications/task-work-order/src/views/orderView/organizational/agenciesManagement/FormDiaLog.vue b/applications/task-work-order/src/views/orderView/organizational/agenciesManagement/FormDiaLog.vue
index 44f488c..6a4411e 100644
--- a/applications/task-work-order/src/views/orderView/organizational/agenciesManagement/FormDiaLog.vue
+++ b/applications/task-work-order/src/views/orderView/organizational/agenciesManagement/FormDiaLog.vue
@@ -245,6 +245,11 @@
// 懒加载获取区域节点数据
async function loadRegionNode(node, resolve) {
try {
+ // 限制只加载两级,当前节点 level >= 1 时不加载子节点
+ if (node.level >= 2) {
+ resolve([])
+ return
+ }
// 初始化加载时传递 code 参数,加载子节点时传递 parentCode 参数
const params = node.data?.id ? { parentCode: node.data.id } : { code: '360800000000' }
const parentCode = node.data?.id || '360800000000'
diff --git a/applications/task-work-order/src/views/orderView/organizational/agenciesManagement/agenciesApi.js b/applications/task-work-order/src/views/orderView/organizational/agenciesManagement/agenciesApi.js
index 53ea264..5ac5b57 100644
--- a/applications/task-work-order/src/views/orderView/organizational/agenciesManagement/agenciesApi.js
+++ b/applications/task-work-order/src/views/orderView/organizational/agenciesManagement/agenciesApi.js
@@ -57,10 +57,11 @@
}
// 导出模板
-export const agenciesExportTemplateApi = () => {
+export const agenciesExportTemplateApi = params => {
return request({
url: `/blade-system/dept/export-template`,
method: 'get',
+ params,
responseType: 'blob',
})
}
diff --git a/applications/task-work-order/src/views/orderView/organizational/agenciesManagement/index.vue b/applications/task-work-order/src/views/orderView/organizational/agenciesManagement/index.vue
index ba5a2c2..9bb277c 100644
--- a/applications/task-work-order/src/views/orderView/organizational/agenciesManagement/index.vue
+++ b/applications/task-work-order/src/views/orderView/organizational/agenciesManagement/index.vue
@@ -47,13 +47,14 @@
<div class="gd-table-toolbar">
<el-button :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">新增机构</el-button>
- <el-button :icon="Upload" @click="handleImport">导入机构</el-button>
- <el-button :icon="Download" :loading="exportLoading" :disabled="exportLoading" @click="handleExport">导出机构</el-button>
+ <el-button :icon="Upload" @click="handleImport">机构导入</el-button>
+ <el-button :icon="Download" :loading="exportLoading" :disabled="exportLoading" @click="handleExport">机构导出</el-button>
</div>
<div class="gd-table-container" v-loading="loading">
<div class="gd-table-content gd-table-content-bg">
- <el-table class="gd-table" :data="list">
+ <el-table class="gd-table" :data="list" @selection-change="handleSelectionChange">
+ <el-table-column type="selection" width="46" />
<el-table-column label="序号" width="80">
<template v-slot="{ $index }">
{{ $index + 1 }}
@@ -98,8 +99,8 @@
class="gd-dialog"
append-to-body
v-model="isShowImportView"
- title="导入机构"
- :width="pxToRem(600)"
+ title="机构导入"
+ :width="pxToRem(500)"
:close-on-click-modal="false"
:destroy-on-close="true"
@close="handleImportClose"
@@ -111,14 +112,17 @@
type="primary"
@click="handleExportTemplate"
>
- 点击导出
+ 下载模板
</el-button>
</el-form-item>
<el-form-item label="上传文件" prop="file">
- {{ importFileName }}
+
<el-upload class="avatar-uploader" action="" :show-file-list="false" :before-upload="onImportFileBefore">
<el-button type="primary">点击上传</el-button>
</el-upload>
+ <div class="upload-tip">
+ {{ importFileName }}
+ </div>
</el-form-item>
</el-form>
<template #footer>
@@ -171,6 +175,7 @@
const dialogVisible = ref(false)
const regionOptions = ref([]) // 区域选项
const dictObj = ref({}) // 字典对象
+const selectedFiles = ref([]) // 勾选的文件列表
// 树形选择器配置
const treeSelectProps = {
@@ -178,8 +183,6 @@
label: 'name',
children: 'children'
}
-
-
// 导入机构相关
const isShowImportView = ref(false)
const importFileName = ref('')
@@ -213,6 +216,12 @@
async function loadRegionNode(node, resolve) {
try {
+ // 限制只加载两级,当前节点 level >= 1 时不加载子节点
+ if (node.level >= 2) {
+ resolve([])
+ return
+ }
+
// 初始化加载时传递 code 参数,加载子节点时传递 parentCode 参数
const params = node.data?.id ? { parentCode: node.data.id } : { code: '360800000000' }
const res = await regionLazyTreeApi(params)
@@ -315,6 +324,11 @@
}
}
+// 勾选值设置
+function handleSelectionChange(rows) {
+ selectedFiles.value = rows
+}
+
// 导出模板
async function handleExportTemplate() {
templateExportLoading.value = true
@@ -338,9 +352,6 @@
// 上传文件前处理
function onImportFileBefore(file) {
- // 提示请先导出模板
- // ElMessage.warning('请先导出模板并按模板格式填写数据')
-
// 检查文件类型
const allowedTypes = ['.xlsx', '.xls']
const fileExtension = file.name.substring(file.name.lastIndexOf('.')).toLowerCase()
@@ -348,21 +359,11 @@
ElMessage.error('请上传Excel文件(.xlsx 或 .xls)')
return false
}
- let data = new FormData()
- data.append('file', file)
-
- agenciesImportApi(data, { isCovered: importParams.value.isCovered }).then(res => {
- if (res.data.code === 200) {
- ElMessage.success('导入成功')
- // 保存文件名
- importFileName.value = file.name
- importParams.value.file = file
- isShowImportView.value = false
- getList()
- } else {
- ElMessage.error(res.msg || '导入失败')
- }
- })
+
+ // 保存文件名和文件对象
+ importFileName.value = file.name
+ importParams.value.file = file
+ ElMessage.success('上传成功')
return false // 阻止组件的默认上传行为
}
@@ -378,8 +379,19 @@
ElMessage.error('请先上传文件')
return
}
- // 触发上传操作
- await onImportFileBefore(importParams.value.file)
+
+ // 构建表单数据并调用导入接口
+ let data = new FormData()
+ data.append('file', importParams.value.file)
+
+ const res = await agenciesImportApi(data, { isCovered: importParams.value.isCovered })
+ if (res.data.code === 200) {
+ ElMessage.success('导入成功')
+ isShowImportView.value = false
+ getList()
+ } else {
+ ElMessage.error(res.msg || '导入失败')
+ }
} finally {
importSubmitting.value = false
}
@@ -389,15 +401,26 @@
// 导出机构
async function handleExport() {
+ // 检查是否有选中的数据
+ if (selectedFiles.value.length === 0) {
+ ElMessage.warning('请选择要导出的机构')
+ return
+ }
+
exportLoading.value = true
try {
- const res = await agenciesExportApi(searchParams.value)
+ const params = {}
+ params.deptIds = selectedFiles.value.map(i => i.id)
+ // 调用导出API
+ const res = await agenciesExportApi(params)
const blob = new Blob([res.data])
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
- // `机构列表${new Date().getTime()}.xlsx`
- a.download = `机构列表.xlsx`
+ // `机构列表${new Date().getTime()}.xlsx`
+ const fileName = `机构列表.xlsx`
+ a.download = fileName
+
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
@@ -416,4 +439,7 @@
</script>
<style scoped lang="scss">
+.upload-tip {
+margin-left: 5px;
+}
</style>
\ No newline at end of file
diff --git a/applications/task-work-order/src/views/orderView/organizational/zoningManagement/FormDiaLog.vue b/applications/task-work-order/src/views/orderView/organizational/zoningManagement/FormDiaLog.vue
index d54e7a0..2781012 100644
--- a/applications/task-work-order/src/views/orderView/organizational/zoningManagement/FormDiaLog.vue
+++ b/applications/task-work-order/src/views/orderView/organizational/zoningManagement/FormDiaLog.vue
@@ -276,6 +276,11 @@
// 懒加载获取区域节点数据
async function loadRegionNode(node, resolve) {
try {
+ // 限制只加载两级,当前节点 level >= 1 时不加载子节点
+ if (node.level >= 2) {
+ resolve([])
+ return
+ }
// 初始化加载时传递 code 参数,加载子节点时传递 parentCode 参数
const params = node.data?.id ? { parentCode: node.data.id } : { code: '360800000000' }
const parentCode = node.data?.id || '360800000000'
diff --git a/applications/task-work-order/src/views/orderView/organizational/zoningManagement/index.vue b/applications/task-work-order/src/views/orderView/organizational/zoningManagement/index.vue
index c1f3dbc..400ce59 100644
--- a/applications/task-work-order/src/views/orderView/organizational/zoningManagement/index.vue
+++ b/applications/task-work-order/src/views/orderView/organizational/zoningManagement/index.vue
@@ -39,13 +39,14 @@
<div class="gd-table-toolbar">
<el-button :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">新增区划</el-button>
- <el-button :icon="Upload" @click="handleImport">导入区划</el-button>
- <el-button :icon="Download" :loading="exportLoading" :disabled="exportLoading" @click="handleExport">导出区划</el-button>
+ <el-button :icon="Upload" @click="handleImport">区划导入</el-button>
+ <el-button :icon="Download" :loading="exportLoading" :disabled="exportLoading" @click="handleExport">区划导出</el-button>
</div>
<div class="gd-table-container" v-loading="loading">
<div class="gd-table-content gd-table-content-bg">
- <el-table class="gd-table" :data="list">
+ <el-table class="gd-table" :data="list" @selection-change="handleSelectionChange">
+ <el-table-column type="selection" width="46" />
<el-table-column label="序号" width="80">
<template v-slot="{ $index }">
{{ $index + 1 }}
@@ -95,29 +96,29 @@
class="gd-dialog"
append-to-body
v-model="isShowImportView"
- title="导入区划"
- :width="pxToRem(600)"
+ title="区划导入"
+ :width="pxToRem(500)"
:close-on-click-modal="false"
:destroy-on-close="true"
@close="handleImportClose"
>
<el-form class="gd-dialog-form" ref="importFormRef" :model="importParams" :rules="importRules" label-width="140px">
<el-form-item label="导出模板">
- <!-- :loading="templateExportLoading"
- :disabled="templateExportLoading" -->
<el-button
type="primary"
color="#F2F3F5"
@click="handleExportTemplate"
>
- 点击导出
+ 下载模板
</el-button>
</el-form-item>
<el-form-item label="上传文件" prop="file">
- {{ importFileName }}
<el-upload class="avatar-uploader" action="" :show-file-list="false" :before-upload="onImportFileBefore">
<el-button type="primary">点击上传</el-button>
</el-upload>
+ <div class="upload-tip">
+ {{ importFileName }}
+ </div>
</el-form-item>
</el-form>
<template #footer>
@@ -169,6 +170,7 @@
const dialogVisible = ref(false)
const regionOptions = ref([]) // 区域选项
const dictObj = ref({}) // 字典对象
+const selectedFiles = ref([]) // 勾选的文件列表
// 导入区划相关
const isShowImportView = ref(false)
@@ -281,6 +283,11 @@
}
}
+// 勾选值设置
+function handleSelectionChange(rows) {
+ selectedFiles.value = rows
+}
+
// 导出模板
async function handleExportTemplate() {
templateExportLoading.value = true
@@ -304,9 +311,6 @@
// 上传文件前处理
function onImportFileBefore(file) {
- // 提示请先导出模板
- // ElMessage.warning('请先导出模板并按模板格式填写数据')
-
// 检查文件类型
const allowedTypes = ['.xlsx', '.xls']
const fileExtension = file.name.substring(file.name.lastIndexOf('.')).toLowerCase()
@@ -314,21 +318,11 @@
ElMessage.error('请上传Excel文件(.xlsx 或 .xls)')
return false
}
- let data = new FormData()
- data.append('file', file)
-
- zoningImportApi(data, { isCovered: importParams.value.isCovered }).then(res => {
- if (res.data.code === 200) {
- ElMessage.success('导入成功')
- // 保存文件名
- importFileName.value = file.name
- importParams.value.file = file
- isShowImportView.value = false
- getList()
- } else {
- ElMessage.error(res.msg || '导入失败')
- }
- })
+
+ // 保存文件名和文件对象
+ importFileName.value = file.name
+ importParams.value.file = file
+ ElMessage.success('上传成功')
return false // 阻止组件的默认上传行为
}
@@ -344,8 +338,18 @@
ElMessage.error('请先上传文件')
return
}
- // 触发上传操作
- await onImportFileBefore(importParams.value.file)
+
+ // 构建表单数据并调用导入接口
+ let data = new FormData()
+ data.append('file', importParams.value.file)
+ const res = await zoningImportApi(data, { isCovered: importParams.value.isCovered })
+ if (res.data.code === 200) {
+ ElMessage.success('导入成功')
+ isShowImportView.value = false
+ getList()
+ } else {
+ ElMessage.error(res.msg || '导入失败')
+ }
} finally {
importSubmitting.value = false
}
@@ -355,14 +359,25 @@
// 导出区划
async function handleExport() {
+ // 检查是否有选中的数据
+ if (selectedFiles.value.length === 0) {
+ ElMessage.warning('请选择要导出的区划')
+ return
+ }
+
exportLoading.value = true
try {
- const res = await zoningExportApi(searchParams.value)
+ const params = {}
+ params.regionIds = selectedFiles.value.map(i => i.id)
+ // 调用导出API
+ const res = await zoningExportApi(params)
const blob = new Blob([res.data])
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
- a.download = `区划列表.xlsx`
+ const fileName = `区划列表.xlsx`
+ a.download = fileName
+
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
@@ -381,4 +396,7 @@
</script>
<style scoped lang="scss">
+.upload-tip {
+margin-left: 5px;
+}
</style>
\ No newline at end of file
diff --git a/applications/task-work-order/src/views/orderView/organizational/zoningManagement/zoningApi.js b/applications/task-work-order/src/views/orderView/organizational/zoningManagement/zoningApi.js
index 737a506..8a2534e 100644
--- a/applications/task-work-order/src/views/orderView/organizational/zoningManagement/zoningApi.js
+++ b/applications/task-work-order/src/views/orderView/organizational/zoningManagement/zoningApi.js
@@ -57,10 +57,11 @@
}
// 导出模板
-export const zoningExportTemplateApi = () => {
+export const zoningExportTemplateApi = params => {
return request({
url: `/blade-system/region/export-template`,
method: 'get',
+ params,
responseType: 'blob',
})
}
diff --git a/packages/constants/package.json b/packages/constants/package.json
index 72630ba..82b4ef2 100644
--- a/packages/constants/package.json
+++ b/packages/constants/package.json
@@ -4,5 +4,11 @@
"private": true,
"description": "常量",
"main": "index.js",
- "license": "ISC"
+ "license": "ISC",
+ "peerDependencies": {
+ "cesium": "catalog:"
+ },
+ "devDependencies": {
+ "cesium": "catalog:"
+ }
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e24baf1..1189736 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -854,7 +854,11 @@
packages/components: {}
- packages/constants: {}
+ packages/constants:
+ devDependencies:
+ cesium:
+ specifier: 'catalog:'
+ version: 1.126.0
packages/hooks:
dependencies:
@@ -13590,30 +13594,30 @@
- '@vue/composition-api'
- vue
- '@wangeditor-next/basic-modules@1.5.47(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)':
+ '@wangeditor-next/basic-modules@1.5.47(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)':
dependencies:
- '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)
- dom7: 3.0.0
+ '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
+ dom7: 4.0.6
is-url: 1.2.4
lodash.throttle: 4.1.1
- nanoid: 3.3.11
- slate: 0.72.8
+ nanoid: 5.1.6
+ slate: 0.82.1
snabbdom: 3.6.3
- '@wangeditor-next/code-highlight@1.3.43(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.82.1)(snabbdom@3.6.3)':
+ '@wangeditor-next/code-highlight@1.3.43(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.82.1)(snabbdom@3.6.3)':
dependencies:
- '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)
+ '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
dom7: 4.0.6
prismjs: 1.30.0
slate: 0.82.1
snabbdom: 3.6.3
- '@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)':
+ '@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)':
dependencies:
'@types/event-emitter': 0.3.5
'@uppy/core': 2.3.4
'@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
- dom7: 3.0.0
+ dom7: 4.0.6
event-emitter: 0.3.5
html-void-elements: 3.0.0
i18next: 23.16.8
@@ -13624,9 +13628,9 @@
lodash.foreach: 4.5.0
lodash.throttle: 4.1.1
lodash.toarray: 4.4.0
- nanoid: 3.3.11
+ nanoid: 5.1.6
scroll-into-view-if-needed: 3.1.0
- slate: 0.72.8
+ slate: 0.82.1
slate-history: 0.109.0(slate@0.82.1)
snabbdom: 3.6.3
@@ -13634,13 +13638,13 @@
dependencies:
'@uppy/core': 2.3.4
'@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
- '@wangeditor-next/basic-modules': 1.5.47(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)
- '@wangeditor-next/code-highlight': 1.3.43(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.82.1)(snabbdom@3.6.3)
- '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)
- '@wangeditor-next/list-module': 1.1.52(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.82.1)(snabbdom@3.6.3)
- '@wangeditor-next/table-module': 1.6.60(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.debounce@4.0.8)(lodash.throttle@4.1.1)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
- '@wangeditor-next/upload-image-module': 1.1.50(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/basic-modules@1.5.47(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.foreach@4.5.0)(slate@0.82.1)(snabbdom@3.6.3)
- '@wangeditor-next/video-module': 1.3.51(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@4.0.6)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
+ '@wangeditor-next/basic-modules': 1.5.47(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
+ '@wangeditor-next/code-highlight': 1.3.43(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.82.1)(snabbdom@3.6.3)
+ '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
+ '@wangeditor-next/list-module': 1.1.52(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.82.1)(snabbdom@3.6.3)
+ '@wangeditor-next/table-module': 1.6.60(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.debounce@4.0.8)(lodash.throttle@4.1.1)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
+ '@wangeditor-next/upload-image-module': 1.1.50(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/basic-modules@1.5.47(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.foreach@4.5.0)(slate@0.82.1)(snabbdom@3.6.3)
+ '@wangeditor-next/video-module': 1.3.51(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
dom7: 4.0.6
is-hotkey: 0.2.0
lodash.camelcase: 4.3.0
@@ -13653,16 +13657,16 @@
slate: 0.82.1
snabbdom: 3.6.3
- '@wangeditor-next/list-module@1.1.52(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.82.1)(snabbdom@3.6.3)':
+ '@wangeditor-next/list-module@1.1.52(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.82.1)(snabbdom@3.6.3)':
dependencies:
- '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)
+ '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
dom7: 4.0.6
slate: 0.82.1
snabbdom: 3.6.3
- '@wangeditor-next/table-module@1.6.60(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.debounce@4.0.8)(lodash.throttle@4.1.1)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)':
+ '@wangeditor-next/table-module@1.6.60(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.debounce@4.0.8)(lodash.throttle@4.1.1)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)':
dependencies:
- '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)
+ '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
dom7: 4.0.6
lodash.debounce: 4.0.8
lodash.throttle: 4.1.1
@@ -13670,22 +13674,22 @@
slate: 0.82.1
snabbdom: 3.6.3
- '@wangeditor-next/upload-image-module@1.1.50(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/basic-modules@1.5.47(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.foreach@4.5.0)(slate@0.82.1)(snabbdom@3.6.3)':
+ '@wangeditor-next/upload-image-module@1.1.50(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/basic-modules@1.5.47(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.foreach@4.5.0)(slate@0.82.1)(snabbdom@3.6.3)':
dependencies:
'@uppy/core': 2.3.4
'@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
- '@wangeditor-next/basic-modules': 1.5.47(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)
- '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)
+ '@wangeditor-next/basic-modules': 1.5.47(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
+ '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
dom7: 4.0.6
lodash.foreach: 4.5.0
slate: 0.82.1
snabbdom: 3.6.3
- '@wangeditor-next/video-module@1.3.51(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3))(dom7@4.0.6)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)':
+ '@wangeditor-next/video-module@1.3.51(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/core@1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@4.0.6)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)':
dependencies:
'@uppy/core': 2.3.4
'@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
- '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)
+ '@wangeditor-next/core': 1.7.45(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.6)(slate@0.82.1)(snabbdom@3.6.3)
dom7: 4.0.6
nanoid: 5.1.6
slate: 0.82.1
--
Gitblit v1.9.3