Merge remote-tracking branch 'origin/master'
| | |
| | | /* |
| | | * @Author : yuan |
| | | * @Date : 2026-01-07 14:58:30 |
| | | * @LastEditors : yuan |
| | | * @LastEditTime : 2026-02-03 15:43:33 |
| | | * @FilePath : \applications\drone-command\env\.env |
| | | * @Description : |
| | | * Copyright 2026 OBKoro1, All Rights Reserved. |
| | | * 2026-01-07 14:58:30 |
| | | */ |
| | | #接口地址 |
| | | VITE_APP_API=/api |
| | | |
| | |
| | | |
| | | # 是否在打包时开启压缩,支持 gzip 和 brotli |
| | | VITE_BUILD_COMPRESS=gzip |
| | | |
| | | VITE_APP_TERRAIN_URL=https://wrj.shuixiongit.com/aiskyminio/cloud-bucket/ztzf_c_uas/ |
| | |
| | | # @Author : yuan |
| | | # @Date : 2026-01-31 14:34:31 |
| | | # @LastEditors : yuan |
| | | # @LastEditTime : 2026-02-03 09:34:48 |
| | | # @LastEditTime : 2026-02-03 15:52:27 |
| | | # @FilePath : \applications\drone-command\env\.env.development |
| | | # @Description : |
| | | # Copyright 2026 OBKoro1, All Rights Reserved. |
| | |
| | | # 航线文件地址 |
| | | VITE_APP_AIRLINE_URL=https://wrj.shuixiongit.com/minio/cloud-bucket |
| | | |
| | | # 地形存放 |
| | | VITE_APP_TERRAIN_URL=https://wrj.shuixiongit.com/aiskyminio/cloud-bucket/ztzf_c_uas/ |
| | | # 行政区划存放地址 |
| | | VITE_APP_REGION_URL=https://wrj.shuixiongit.com/aiskyminio/cloud-bucket/ztzf_region |
| | | # 算法仓库图片地址 |
| | |
| | | # 航线文件地址 |
| | | VITE_APP_AIRLINE_URL=https://wrj.shuixiongit.com/aiskyminio/cloud-bucket |
| | | |
| | | # 图片存放地址 |
| | | VITE_APP_TERRAIN_URL=https://wrj.shuixiongit.com/aiskyminio/cloud-bucket/ztzf_terrain/ |
| | | |
| | | # 算法仓库图片地址 |
| | | VITE_APP_PICTURE_URL=https://wrj.shuixiongit.com/aiskyminio/cloud-bucket |
| | | |
| | |
| | | # 航线文件地址 |
| | | VITE_APP_AIRLINE_URL = https://wrj.shuixiongit.com/minio/cloud-bucket |
| | | |
| | | # 图片存放地址 |
| | | VITE_APP_TERRAIN_URL = https://wrj.shuixiongit.com/aiskyminio/cloud-bucket/ztzf_terrain/ |
| | | |
| | | # 算法仓库图片地址 |
| | | VITE_APP_PICTURE_URL = https://wrj.shuixiongit.com/aiskyminio/cloud-bucket |
| | | |
| | |
| | | align-items: center; |
| | | |
| | | .label { |
| | | width: 88px; |
| | | min-width: 88px; |
| | | font-family: Source Han Sans CN, Source Han Sans CN; |
| | | font-weight: 500; |
| | | font-size: 14px; |
| | |
| | | ## 列表 |
| | | |
| | | |
| | | **接口地址**:`/area/fwPoliceStation/list` |
| | | **接口地址**:`/device/fwDevice/list` |
| | | |
| | | |
| | | **请求方式**:`GET` |
| | |
| | | **响应数据类型**:`*/*` |
| | | |
| | | |
| | | **接口描述**:<p>获取全部派出所</p> |
| | | **接口描述**:<p>传入isAreaSelect、areaId、areaIds(多个用,隔开获取对应区域绑定设备数据)、status</p> |
| | | |
| | | |
| | | |
| | |
| | | **请求参数**: |
| | | |
| | | |
| | | 暂无 |
| | | | 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | schema | |
| | | | -------- | -------- | ----- | -------- | -------- | ------ | |
| | | |areaId|areaId|query|false|integer(int64)|| |
| | | |areaIds|areaIds(多个用,隔开)|query|false|string|| |
| | | |isAreaSelect|isAreaSelect|query|false|integer(int32)|| |
| | | |isTrack|isTrack|query|false|integer(int32)|| |
| | | |status|status|query|false|integer(int32)|| |
| | | |
| | | |
| | | **响应状态**: |
| | |
| | | |
| | | | 状态码 | 说明 | schema | |
| | | | -------- | -------- | ----- | |
| | | |200|OK|R«List«FwPoliceStationVO»»| |
| | | |200|OK|R«List«FwDeviceVO»»| |
| | | |401|Unauthorized|| |
| | | |403|Forbidden|| |
| | | |404|Not Found|| |
| | |
| | | | 参数名称 | 参数说明 | 类型 | schema | |
| | | | -------- | -------- | ----- |----- | |
| | | |code|状态码|integer(int32)|integer(int32)| |
| | | |data|承载数据|array|FwPoliceStationVO| |
| | | |  address|派出所地址|string|| |
| | | |  areaCode|区域编码|string|| |
| | | |  contactPerson|联系人|string|| |
| | | |  contactPhone|联系方式|string|| |
| | | |data|承载数据|array|FwDeviceVO| |
| | | |  azimuth|方位角 0-360°|integer(int32)|| |
| | | |  batteryPct|电量百分比 0-100|integer(int32)|| |
| | | |  belongDept|所属部门id|integer(int64)|| |
| | | |  belongDeptName|所属部门名称|string|| |
| | | |  charger|负责人|string|| |
| | | |  contactPhone|联系电话|string|| |
| | | |  createDept|创建部门|integer(int64)|| |
| | | |  createTime|创建时间|string(date-time)|| |
| | | |  createUser|创建人|integer(int64)|| |
| | | |  id|主键id|integer(int64)|| |
| | | |  latitude|纬度|number(double)|| |
| | | |  longitude|经度|number(double)|| |
| | | |  stationName|派出所名称|string|| |
| | | |  status|状态(0正常 1停用)|integer(int32)|| |
| | | |  detectTargetCnt|侦测目标数量|integer(int32)|| |
| | | |  deviceAtt|设备属性(无线电/光电/雷达)|string|| |
| | | |  deviceModel|型号|string|| |
| | | |  deviceName|设备名称|string|| |
| | | |  deviceSn|设备sn|string|| |
| | | |  deviceSpecification|规格|string|| |
| | | |  deviceType|设备类型(察打一体/便捷侦测箱/反制枪)|string|| |
| | | |  effectiveRangeKm|有效范围 km|number|| |
| | | |  elevation|俯仰角 -90-+90°|integer(int32)|| |
| | | |  id|主键|integer(int64)|| |
| | | |  isShared|是否被共享给当前机构|boolean|| |
| | | |  latitude|纬度|string|| |
| | | |  longitude|经度|string|| |
| | | |  manufacturer|生产厂商|string|| |
| | | |  outTarget|出库去向|string|| |
| | | |  purpose|用途|string|| |
| | | |  remark|备注|string|| |
| | | |  sceneName|场景名称|string|| |
| | | |  shareId|共享记录ID|integer(int64)|| |
| | | |  source|来源|string|| |
| | | |  status|运行状态(0:在线/1:离线/2:故障/3:报废)|integer(int32)|| |
| | | |  trackStatus|设备出入库状态 0未出库1已出库|integer(int32)|| |
| | | |  updateTime|更新时间|string(date-time)|| |
| | | |  updateUser|更新人|integer(int64)|| |
| | | |  workMode|工作模式:1.侦测中/2.信号干扰中/3.诱导驱离中/4.待机|string|| |
| | | |msg|返回消息|string|| |
| | | |success|是否成功|boolean|| |
| | | |
| | |
| | | "code": 0, |
| | | "data": [ |
| | | { |
| | | "address": "", |
| | | "areaCode": "", |
| | | "contactPerson": "", |
| | | "azimuth": 0, |
| | | "batteryPct": 0, |
| | | "belongDept": 0, |
| | | "belongDeptName": "", |
| | | "charger": "", |
| | | "contactPhone": "", |
| | | "createDept": 0, |
| | | "createTime": "", |
| | | "createUser": 0, |
| | | "detectTargetCnt": 0, |
| | | "deviceAtt": "", |
| | | "deviceModel": "", |
| | | "deviceName": "", |
| | | "deviceSn": "", |
| | | "deviceSpecification": "", |
| | | "deviceType": "", |
| | | "effectiveRangeKm": 0, |
| | | "elevation": 0, |
| | | "id": 0, |
| | | "latitude": 0, |
| | | "longitude": 0, |
| | | "stationName": "", |
| | | "isShared": true, |
| | | "latitude": "", |
| | | "longitude": "", |
| | | "manufacturer": "", |
| | | "outTarget": "", |
| | | "purpose": "", |
| | | "remark": "", |
| | | "sceneName": "", |
| | | "shareId": 0, |
| | | "source": "", |
| | | "status": 0, |
| | | "trackStatus": 0, |
| | | "updateTime": "", |
| | | "updateUser": 0 |
| | | "updateUser": 0, |
| | | "workMode": "" |
| | | } |
| | | ], |
| | | "msg": "", |
| | | "success": true |
| | | } |
| | | ``` |
| | | ``` |
| | |
| | | import ellipseIcon from '@/assets/images/areaMap/ellipse.png' |
| | | import bufferIcon from '@/assets/images/areaMap/buffer.png' |
| | | import trashIcon from '@/assets/images/areaMap/trash.png' |
| | | import equipmentIcon from '@/assets/images/dataCockpit/map/equipment.png' |
| | | |
| | | const initForm = () => ({ |
| | | areaName: '', // 区域名称 |
| | |
| | | let viewEntity |
| | | let shapeDisplaySource |
| | | const deviceRangePrimitiveMap = new Map() |
| | | const deviceIconEntityMap = new Map() |
| | | const activeToolMode = ref('') |
| | | const suppressDeviceFly = ref(false) |
| | | const isDialogInitializing = ref(false) |
| | |
| | | hideTypePanel() |
| | | clearShapeDisplay() |
| | | clearDeviceRangePrimitives() |
| | | clearDeviceIconEntities() |
| | | } |
| | | } |
| | | ) |
| | |
| | | deviceRangePrimitiveMap.clear() |
| | | } |
| | | |
| | | function clearDeviceIconEntities () { |
| | | if (!viewer || !deviceIconEntityMap.size) return |
| | | for (const entity of deviceIconEntityMap.values()) { |
| | | viewer.entities.remove(entity) |
| | | } |
| | | deviceIconEntityMap.clear() |
| | | } |
| | | |
| | | function clearDeviceListState () { |
| | | deviceOptions.value = [] |
| | | selectedDeviceRows.value = [] |
| | | formData.value.deviceIds = '' |
| | | clearDeviceRangePrimitives() |
| | | clearDeviceIconEntities() |
| | | if (deviceTableRef.value) { |
| | | deviceTableRef.value.clearSelection() |
| | | } |
| | |
| | | async function renderDeviceRangeSingle (device) { |
| | | if (!viewer) return |
| | | clearDeviceRangePrimitives() |
| | | clearDeviceIconEntities() |
| | | const position = getDeviceLngLat(device) |
| | | if (!position) return |
| | | addDeviceIconEntity(device, position) |
| | | const rangeMeters = normalizeDeviceRange(device.effectiveRangeKm) |
| | | const primitive = await createDeviceRangePrimitive(viewer, position, rangeMeters) |
| | | if (!primitive) return |
| | |
| | | deviceRangePrimitiveMap.delete(id) |
| | | } |
| | | } |
| | | for (const [id, entity] of deviceIconEntityMap.entries()) { |
| | | if (!nextIds.has(id)) { |
| | | viewer.entities.remove(entity) |
| | | deviceIconEntityMap.delete(id) |
| | | } |
| | | } |
| | | const pendingRows = selectedRows.filter(row => { |
| | | const id = String(row?.id) |
| | | return id && !deviceRangePrimitiveMap.has(id) |
| | |
| | | const primitives = await Promise.all( |
| | | pendingRows.map(async row => { |
| | | const position = getDeviceLngLat(row) |
| | | if (!position) return { row, primitive: null } |
| | | if (!position) return { row, primitive: null, position: null } |
| | | const rangeMeters = normalizeDeviceRange(row.effectiveRangeKm) |
| | | const primitive = await createDeviceRangePrimitive(viewer, position, rangeMeters) |
| | | return { row, primitive } |
| | | return { row, primitive, position } |
| | | }) |
| | | ) |
| | | if (renderToken !== deviceRangeRenderToken) return |
| | | primitives.forEach(({ row, primitive }) => { |
| | | primitives.forEach(({ row, primitive, position }) => { |
| | | if (position) { |
| | | addDeviceIconEntity(row, position) |
| | | } |
| | | if (!primitive) return |
| | | const id = String(row?.id) |
| | | if (!nextIds.has(id)) return |
| | |
| | | function clearShapeDisplay () { |
| | | if (!shapeDisplaySource) return |
| | | shapeDisplaySource.entities.removeAll() |
| | | } |
| | | |
| | | function addDeviceIconEntity (device, position) { |
| | | if (!viewer || !position) return |
| | | const id = String(device?.id ?? '') |
| | | if (!id || deviceIconEntityMap.has(id)) return |
| | | const entity = viewer.entities.add({ |
| | | position: Cesium.Cartesian3.fromDegrees(position.lng, position.lat, position.height || 0), |
| | | billboard: { |
| | | image: equipmentIcon, |
| | | width: 28, |
| | | height: 38, |
| | | verticalOrigin: Cesium.VerticalOrigin.BOTTOM, |
| | | heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, |
| | | }, |
| | | }) |
| | | deviceIconEntityMap.set(id, entity) |
| | | } |
| | | |
| | | function normalizeShapePoint (point) { |
| | |
| | | initMap() |
| | | clearShapeDisplay() |
| | | clearDeviceRangePrimitives() |
| | | clearDeviceIconEntities() |
| | | viewEntity && viewer?.entities?.remove(viewEntity) |
| | | viewEntity = null |
| | | clearActiveTool() |
| | |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | | <div class="detail-title">设备列表</div> |
| | | <div class="command-table-container"> |
| | | <div class="command-table-content"> |
| | | <el-table |
| | | class="command-table" |
| | | :data="deviceList" |
| | | :loading="deviceLoading" |
| | | row-key="id" |
| | | > |
| | | <el-table-column prop="deviceName" show-overflow-tooltip label="设备名称" /> |
| | | <el-table-column prop="deviceType" show-overflow-tooltip label="设备类型" /> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <el-form |
| | |
| | | </el-table> |
| | | </el-form-item> |
| | | </div> |
| | | |
| | | <div class="detail-title">设备列表</div> |
| | | <div class="command-table-container"> |
| | | <div class="command-table-content"> |
| | | <el-table |
| | | class="command-table" |
| | | :data="deviceList" |
| | | :loading="deviceLoading" |
| | | row-key="id" |
| | | > |
| | | <el-table-column prop="deviceName" show-overflow-tooltip label="设备名称" /> |
| | | <el-table-column prop="deviceType" show-overflow-tooltip label="设备类型" /> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | | </el-form> |
| | | |
| | | <div class="footer"> |
| | |
| | | import CommonCesiumMap from '@/components/map-container/common-cesium-map.vue' |
| | | import * as Cesium from 'cesium' |
| | | import { fwAreaDivideDetailApi, fwAreaDivideListApi } from '../partition/partitionApi' |
| | | import { fwDeviceListApi } from '@/views/basicManage/deviceStock/fwDevice' |
| | | import { |
| | | addBufferRadiusLabelEntity, |
| | | buildEllipsePositions, |
| | |
| | | const searchName = ref('') |
| | | const areaList = ref([]) // 关联区域列表 |
| | | const selectedAreaRows = ref([]) //选中列表 |
| | | const deviceList = ref([]) // 设备列表 |
| | | const deviceLoading = ref(false) |
| | | const dictObj = inject('dictObj') |
| | | const mapRef = ref(null) |
| | | const route = useRoute() |
| | |
| | | if (areaList.value.length) return |
| | | const res = await fwAreaDivideListApi({ filterSelected: 1, sceneId: formData.value.id }) |
| | | areaList.value = res?.data?.data ?? [] |
| | | } |
| | | |
| | | async function loadDeviceListByAreaIds(areaIdsKey) { |
| | | if (!areaIdsKey) { |
| | | deviceList.value = [] |
| | | return |
| | | } |
| | | deviceLoading.value = true |
| | | try { |
| | | const res = await fwDeviceListApi({ areaIds: areaIdsKey }) |
| | | deviceList.value = res?.data?.data ?? [] |
| | | } finally { |
| | | deviceLoading.value = false |
| | | } |
| | | } |
| | | |
| | | // 关联区域变更 |
| | |
| | | } |
| | | |
| | | watch(() => selectedAreaRows.value, renderSelectedAreas) |
| | | const deviceAreaIdsKey = computed(() => |
| | | selectedAreaRows.value.map(item => item?.id).filter(Boolean).join(',') |
| | | ) |
| | | watch( |
| | | () => deviceAreaIdsKey.value, |
| | | val => { |
| | | loadDeviceListByAreaIds(val) |
| | | } |
| | | ) |
| | | watch( |
| | | () => visible.value, |
| | | val => { |
| | |
| | | </el-table-column> |
| | | <el-table-column prop="areaCount" show-overflow-tooltip label="区域数量" /> |
| | | <el-table-column prop="counterDeviceCount" show-overflow-tooltip label="反制设备数量" /> |
| | | <el-table-column prop="detectDeviceCount" show-overflow-tooltip label="探测设备数量" /> |
| | | <el-table-column prop="detectDeviceCount" show-overflow-tooltip label="侦测设备数量" /> |
| | | <el-table-column label="操作" class-name="operation-btns"> |
| | | <template v-slot="{ row }"> |
| | | <el-link @click="openForm('view', row)">查看</el-link> |
| | |
| | | placeholder="请选择" |
| | | clearable |
| | | > |
| | | <el-option v-for="item in areaOptions" :key="item.value" :label="item.label" :value="item.label" /> |
| | | <el-option v-for="item in areaOptions" :key="item.value" :label="item.label" :value="item.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | submitting.value = true |
| | | try { |
| | | const selected = areaOptions.value.find(item => item.value === formData.value.outTarget) |
| | | |
| | | const payload = { |
| | | ...formData.value, |
| | | outTarget: selected?.label ?? formData.value.outTarget, |
| | | outTargetCode: formData.value.outTarget |
| | | } |
| | | await fwDeviceTrackSubmitApi(payload) |
| | | saveOperationLog({ |
| | |
| | | }) |
| | | ElMessage.success(dialogMode.value === 'add' ? '新增成功' : '更新成功') |
| | | visible.value = false |
| | | emit('success') |
| | | emit('success', { parentId: formData.value.parentId || null, mode: dialogMode.value, isAddChild: isAddChild.value }) |
| | | } finally { |
| | | submitting.value = false |
| | | } |
| | |
| | | </div> |
| | | </div> |
| | | |
| | | <FormDiaLog ref="dialogRef" @success="getList" v-model="visible" v-if="visible" /> |
| | | <FormDiaLog ref="dialogRef" @success="handleFormSuccess" v-model="visible" v-if="visible" /> |
| | | <ShareDiaLog ref="shareDialogRef" @success="getList" v-model="shareVisible" v-if="shareVisible" /> |
| | | </basic-container> |
| | | </template> |
| | |
| | | const list = ref([]) |
| | | const total = ref(0) |
| | | const selectedIds = ref([]) |
| | | const selectedRows = ref([]) |
| | | const lastActionParentRow = ref(null) |
| | | const queryParamsRef = ref(null) |
| | | const dialogRef = ref(null) |
| | | const shareDialogRef = ref(null) |
| | |
| | | }) |
| | | const deptTree = ref([]) |
| | | const route = useRoute() |
| | | const treeResolveMap = new Map() |
| | | |
| | | provide('dictObj', dictObj) |
| | | provide('deptTree', deptTree) |
| | |
| | | } |
| | | |
| | | function handleAddChild(row) { |
| | | lastActionParentRow.value = row |
| | | visible.value = true |
| | | nextTick(() => { |
| | | dialogRef.value?.open({ mode: 'add', parent: row }) |
| | |
| | | cancelButtonClass: 'command-message-box-cancel', |
| | | }) |
| | | const ids = row ? row.id : selectedIds.value.join(',') |
| | | const rows = row ? [row] : selectedRows.value |
| | | await remove(ids) |
| | | saveOperationLog({ |
| | | requestUri: route.path, |
| | |
| | | }) |
| | | ElMessage.success('删除成功') |
| | | selectedIds.value = [] |
| | | getList() |
| | | selectedRows.value = [] |
| | | const parentIds = Array.from(new Set(rows.map(item => item.parentId).filter(Boolean))) |
| | | const hasRoot = rows.some(item => !item.parentId) |
| | | if (hasRoot) { |
| | | getList() |
| | | } |
| | | parentIds.forEach(parentId => refreshChildNodes(parentId)) |
| | | } |
| | | |
| | | function handleSelectionChange(rows) { |
| | | selectedIds.value = rows.map(item => item.id) |
| | | selectedRows.value = rows |
| | | } |
| | | |
| | | function getDictList() { |
| | |
| | | } |
| | | |
| | | function loadChildren(row, treeNode, resolve) { |
| | | treeResolveMap.set(row.id, { resolve, row }) |
| | | getChildLazyTree({ parentId: row.id }) |
| | | .then(res => { |
| | | resolve(res?.data?.data ?? []) |
| | | const children = res?.data?.data ?? [] |
| | | row.hasChildren = children.length > 0 |
| | | resolve(children) |
| | | }) |
| | | .catch(() => resolve([])) |
| | | .catch(() => { |
| | | row.hasChildren = false |
| | | resolve([]) |
| | | }) |
| | | } |
| | | |
| | | function refreshChildNodes(parentId) { |
| | | const cache = treeResolveMap.get(parentId) |
| | | if (!cache?.resolve) return |
| | | getChildLazyTree({ parentId }) |
| | | .then(res => { |
| | | const children = res?.data?.data ?? [] |
| | | if (cache.row) { |
| | | cache.row.hasChildren = children.length > 0 |
| | | } |
| | | cache.resolve(children) |
| | | }) |
| | | .catch(() => { |
| | | if (cache.row) { |
| | | cache.row.hasChildren = false |
| | | } |
| | | cache.resolve([]) |
| | | }) |
| | | } |
| | | |
| | | function handleFormSuccess(payload = {}) { |
| | | if (payload?.parentId) { |
| | | refreshChildNodes(payload.parentId) |
| | | if (payload.isAddChild && lastActionParentRow.value?.id === payload.parentId) { |
| | | lastActionParentRow.value.hasChildren = true |
| | | } |
| | | lastActionParentRow.value = null |
| | | return |
| | | } |
| | | lastActionParentRow.value = null |
| | | getList() |
| | | } |
| | | |
| | | onMounted(() => { |
| | |
| | | > |
| | | <div class="dialog-container"> |
| | | <div class="left-container"> |
| | | <div class="leftMap command-cesium" id="leftMapContainer"></div> |
| | | <CommonCesiumMap |
| | | ref="mapRef" |
| | | class="leftMap command-cesium" |
| | | :active="visible" |
| | | :flat-mode="false" |
| | | :terrain="true" |
| | | :layer-mode="4" |
| | | :boundary="false" |
| | | /> |
| | | </div> |
| | | <div class="right-container"> |
| | | <div class="header"> |
| | |
| | | import { Close } from '@element-plus/icons-vue' |
| | | |
| | | import { computed, nextTick, ref } from 'vue' |
| | | import CommonCesiumMap from '@/components/map-container/common-cesium-map.vue' |
| | | import { fwDroneFlightRecordDetailApi } from '@/views/recordManage/historyTracks/fwDroneFlightRecord' |
| | | import { fwDroneFlightRecordDetailPageApi } from '@/views/recordManage/historyTracks/flyTrajectory' |
| | | import { PublicCesium } from '@/utils/cesium/publicCesium' |
| | | import * as Cesium from 'cesium' |
| | | import { flyVisual } from '@ztzf/utils' |
| | | import { ArrowLineMaterialProperty } from '@/utils/cesium/Material' |
| | | import droneIcon from '@/assets/images/dataCockpit/map/drone.png' |
| | | |
| | |
| | | stayDuration: '', |
| | | }) |
| | | let list = ref([]) |
| | | const emit = defineEmits(['success']) |
| | | const formData = ref(initForm()) // 表单数据 |
| | | const visible = ref(false) // 弹框显隐 |
| | | const dialogMode = ref('add') // 弹框模式 |
| | | const dialogReadonly = computed(() => dialogMode.value === 'view') |
| | | const mapRef = ref(null) |
| | | const dialogTitle = computed(() => { |
| | | if (dialogMode.value === 'edit') { |
| | | return '编辑' |
| | |
| | | let viewer, geometricSource, droneLineEntity, startDroneEntity |
| | | function initMap() { |
| | | if (viewer) return |
| | | const publicCesiumInstance = new PublicCesium({ |
| | | dom: 'leftMapContainer', |
| | | flatMode: false, |
| | | terrain: true, |
| | | layerMode: 4, |
| | | boundary: false, |
| | | }) |
| | | viewer = publicCesiumInstance.getViewer() |
| | | const map = mapRef.value?.getMap?.() |
| | | if (!map?.viewer) return |
| | | viewer = map.viewer |
| | | geometricSource = new Cesium.CustomDataSource('geometricSource') |
| | | viewer.dataSources.add(geometricSource) |
| | | } |
| | |
| | | VITE_BUILD_COMPRESS=gzip |
| | | |
| | | # 预览地址 previewURL |
| | | VITE_APP_PREVIEW_URL=http://192.168.1.204:8012 |
| | | VITE_APP_PREVIEW_URL=http://192.168.1.36:8012 |
| | | |
| | | VITE_APP_TERRAIN_URL=https://wrj.shuixiongit.com/aiskyminio/cloud-bucket/ztzf_c_uas/ |
| | | VITE_APP_TERRAIN_URL=https://wrj.shuixiongit.com/aiskyminio/cloud-bucket/ztzf_c_uas/ |
| | |
| | | # 航线文件地址 |
| | | VITE_APP_AIRLINE_URL=https://wrj.shuixiongit.com/aiskyminio/cloud-bucket |
| | | |
| | | # 图片存放地址 |
| | | VITE_APP_TERRAIN_URL=https://wrj.shuixiongit.com/aiskyminio/cloud-bucket/ztzf_terrain/ |
| | | |
| | | # 行政区划存放地址 |
| | | VITE_APP_REGION_URL=https://wrj.shuixiongit.com/aiskyminio/cloud-bucket/ztzf_region |
| | |
| | | ### |
| | | # @Author : yuan |
| | | # @Date : 2026-02-03 13:46:08 |
| | | # @LastEditors : yuan |
| | | # @LastEditTime : 2026-02-03 15:44:11 |
| | | # @FilePath : \applications\task-work-order\env\.env.test |
| | | # @Description : |
| | | # Copyright 2026 OBKoro1, All Rights Reserved. |
| | | # 2026-02-03 13:46:08 |
| | | ### |
| | | ### |
| | | # @Author : yuan |
| | | # @Date : 2026-02-03 13:46:08 |
| | | # @LastEditors : yuan |
| | | # @LastEditTime : 2026-02-03 15:44:01 |
| | | # @FilePath : \applications\task-work-order\env\.env.test |
| | | # @Description : |
| | | # Copyright 2026 OBKoro1, All Rights Reserved. |
| | | # 2026-02-03 13:46:08 |
| | | ### |
| | | NODE_ENV='test' |
| | | |
| | | #开发环境配置 |
| | |
| | | |
| | | # 航线文件地址 |
| | | VITE_APP_AIRLINE_URL=https://wrj.shuixiongit.com/minio/cloud-bucket |
| | | |
| | | # 图片存放地址 |
| | | VITE_APP_TERRAIN_URL=https://wrj.shuixiongit.com/aiskyminio/cloud-bucket/ztzf_terrain/ |
| | | |
| | | |
| | | # 行政区划存放地址 |
| | | VITE_APP_REGION_URL=https://wrj.shuixiongit.com/aiskyminio/cloud-bucket/ztzf_region |
| | |
| | | color: #666 |
| | | } |
| | | |
| | | .iconfont{ |
| | | font-size: 14px; |
| | | } |
| | | |
| | | &:hover { |
| | | background: transparent; |
| | | |
| | |
| | | }) |
| | | // 获取航线详情 |
| | | function getAirDetails() { |
| | | console.log(formData.value) |
| | | const dockHeight = formData.value.height |
| | | queryAirById(formData.value.patrolRouteUrl).then(res => { |
| | | const list = res.data.data.airlineWaypoints |
| | | const {airlineWaypoints,airlineSetting:{globalAirlineHeight,airlineHeightMode}} = res.data.data |
| | | const relative = airlineHeightMode === 'relativeToStartPoint' |
| | | const list = airlineWaypoints |
| | | if (!list.length) return mapRef.value?.flyBoundary() |
| | | const result = list.map(item => [Number(item.longitude), Number(item.latitude)]).flat() |
| | | const result = list.map(item => { |
| | | let height = item?.globalHeight || globalAirlineHeight |
| | | if (relative){ |
| | | height = height + Number(dockHeight) |
| | | } |
| | | return [Number(item.longitude), Number(item.latitude),height || 0] |
| | | }) |
| | | viewer.entities.add({ |
| | | polyline: { |
| | | positions: Cesium.Cartesian3.fromDegreesArray(result), |
| | | clampToGround: true, |
| | | positions: Cesium.Cartesian3.fromDegreesArrayHeights(result.flat()), |
| | | width: 4, |
| | | material: arrowLineMaterialProperty, |
| | | }, |
| | | }) |
| | | // 渲染起点(蓝色) |
| | | const startPoint = list[0] |
| | | const startPoint = result[0] |
| | | viewer.entities.add({ |
| | | position: Cesium.Cartesian3.fromDegrees( |
| | | Number(startPoint.longitude), |
| | | Number(startPoint.latitude), |
| | | startPoint.height || 0 |
| | | ), |
| | | position: Cesium.Cartesian3.fromDegrees(Number(startPoint[0]), Number(startPoint[1]), startPoint[2]), |
| | | point: { |
| | | pixelSize: 12, |
| | | color: Cesium.Color.BLUE, |
| | | outlineColor: Cesium.Color.WHITE, |
| | | outlineWidth: 2, |
| | | heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, |
| | | }, |
| | | }) |
| | | // 渲染终点(红色) |
| | | const endPoint = list[list.length - 1] |
| | | const endPoint = result[result.length - 1] |
| | | viewer.entities.add({ |
| | | position: Cesium.Cartesian3.fromDegrees( |
| | | Number(endPoint.longitude), |
| | | Number(endPoint.latitude), |
| | | endPoint.height || 0 |
| | | ), |
| | | position: Cesium.Cartesian3.fromDegrees(Number(endPoint[0]), Number(endPoint[1]), endPoint[2]), |
| | | point: { |
| | | pixelSize: 12, |
| | | color: Cesium.Color.RED, |
| | | outlineColor: Cesium.Color.WHITE, |
| | | outlineWidth: 2, |
| | | heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, |
| | | }, |
| | | }) |
| | | flyVisual({ |
| | |
| | | </view> |
| | | <u-radio-group |
| | | v-if="!loadingManufacturer" |
| | | v-model="formParams.spacecraftId" |
| | | v-model="formParams.manufacturer" |
| | | placement="column" |
| | | @change="radioChange" |
| | | > |
| | |
| | | formParams.value.region = e[2] || '' // 用逗号分隔的区域码 |
| | | // 通过arrRegion的数组值为[36,3608,360824] 从areaData里面反查出名称 |
| | | const regionNames = findRegionNamesByCodes(e, areaData.value) |
| | | formParams.value.regionText = regionNames.join('-') || '' // 用连字符连接的区域名称 |
| | | formParams.value.regionText = regionNames.join(',') || '' // 用连字符连接的区域名称 |
| | | isShowRegion.value = false |
| | | } |
| | | // 是否有保险 |
| | |
| | | // 检查响应 |
| | | if (response.statusCode === 200) { |
| | | areaData.value = response.data |
| | | console.log('请求成功:', data) |
| | | // console.log('请求成功:', data) |
| | | // 处理数据 |
| | | } else { |
| | | console.error('请求失败:', response) |
| | |
| | | current: 1, |
| | | size: 1000, |
| | | }).then(res => { |
| | | // manufacturerList.value = res.data.data.records.map(item => ({ |
| | | // ...item, |
| | | // label: item.unitName, |
| | | // value: item.id, |
| | | // })) |
| | | console.log(formParams.value.manufacturer, res.data.data.records.find(item => item.id === formParams.value.manufacturer)) |
| | | formParams.value.manufacturerText = res.data.data.records.find(item => item.id === formParams.value.manufacturer)?.unitName || '' |
| | | }) |
| | | } |
| | |
| | | formParams.value.hasInsurance = res.data.data.hasInsurance === '1' ? '有' : '无' |
| | | // formParams.value.manufacturer = manufacturerList.value.find(item => item.value === res.data.data.manufacturer)?.label || '' |
| | | formParams.value.type = typeList.value.find(item => item.value === res.data.data.type)?.label || '' |
| | | formParams.value.region = res.data.data.region |
| | | // formParams.value.region = res.data.data.region |
| | | // 360824 截取留前两位 3608 截取留前四位 |
| | | formParams.value.region = findRegionNamesByCodes([res.data.data.region.slice(0,2),res.data.data.region.slice(0,4),res.data.data.region], areaData.value) |
| | | getManufacturerInfoApi() |
| | | }) |
| | | |
| | |
| | | // getManufacturerInfoApi() |
| | | getProTypeApi() |
| | | // 动态导入xzqhData,确保在使用前已初始化 |
| | | const { xzqhData } = await import('@/static/xzqh/index') |
| | | areaData.value = xzqhData |
| | | // const { xzqhData } = await import('@/static/xzqh/index') |
| | | // areaData.value = xzqhData |
| | | const response = await uni.request({ |
| | | url: 'https://wrj.shuixiongit.com/aiskyminio/cloud-bucket/ja-app-wx/xzqh/index.json', |
| | | method: 'GET', |
| | | timeout: 10000, // 10秒超时 |
| | | dataType: 'json' |
| | | }) |
| | | |
| | | // 检查响应 |
| | | if (response.statusCode === 200) { |
| | | |
| | | areaData.value = response.data |
| | | // console.log('请求成功:', data) |
| | | // 处理数据 |
| | | } else { |
| | | console.error('请求失败:', response) |
| | | throw new Error(`请求失败: ${response.statusCode}`) |
| | | } |
| | | }) |
| | | </script> |
| | | <style scoped lang="scss"> |
| | |
| | | <u-popup |
| | | :show="isShowAircraftInfo" |
| | | mode="bottom" |
| | | round="20" |
| | | round="0" |
| | | @close="isShowAircraftInfo = false" |
| | | > |
| | | <scroll-view scroll-y> |
| | | <view class="select-header"> |
| | | <view>请选择飞行器</view> |
| | | <view class="select-header-btn"> |
| | | <view class="cancel-btn" @click="isShowAircraftInfo = false">取消</view> |
| | | <view @click="onPickerAircraftInfo">确定</view> |
| | | </view> |
| | | <!-- <view>请选择飞行器</view> --> |
| | | <view class="cancel-btn" @click="isShowAircraftInfo = false">取消</view> |
| | | <view class="confirm-btn" @click="onPickerAircraftInfo">确定</view> |
| | | </view> |
| | | <view class="select-content"> |
| | | <up-checkbox-group |
| | | <u-checkbox-group |
| | | v-model="formParams.spacecraftId" |
| | | placement="column" |
| | | @change="checkboxChange" |
| | | > |
| | | <up-checkbox |
| | | <u-checkbox |
| | | :customStyle="{marginBottom: '8px'}" |
| | | v-for="(item, index) in aircraftInfoList" |
| | | :key="item.id" |
| | | :label="item.label" |
| | | :name="item.value" |
| | | > |
| | | </up-checkbox> |
| | | </up-checkbox-group> |
| | | </u-checkbox> |
| | | </u-checkbox-group> |
| | | </view> |
| | | </scroll-view> |
| | | </u-popup> |
| | |
| | | <u-popup |
| | | :show="isShowPilotInfo" |
| | | mode="bottom" |
| | | round="20" |
| | | round="0" |
| | | @close="isShowPilotInfo = false" |
| | | > |
| | | <scroll-view scroll-y> |
| | | <view class="select-header"> |
| | | <view>请选择飞手</view> |
| | | <view class="select-header-btn"> |
| | | <view class="cancel-btn" @click="isShowPilotInfo = false">取消</view> |
| | | <view @click="onPickerPilotInfo">确定</view> |
| | | </view> |
| | | <!-- <view>请选择飞手</view> --> |
| | | <view class="cancel-btn" @click="isShowPilotInfo = false">取消</view> |
| | | <view class="confirm-btn" @click="onPickerPilotInfo">确定</view> |
| | | </view> |
| | | <view class="select-content"> |
| | | <up-checkbox-group |
| | | <u-checkbox-group |
| | | v-model="formParams.pilotId" |
| | | placement="column" |
| | | @change="checkboxPilotChange" |
| | | > |
| | | <up-checkbox |
| | | <u-checkbox |
| | | :customStyle="{marginBottom: '8px'}" |
| | | v-for="(item, index) in pilotInfoList" |
| | | :key="item.id" |
| | | :label="item.label" |
| | | :name="item.value" |
| | | > |
| | | </up-checkbox> |
| | | </up-checkbox-group> |
| | | </u-checkbox> |
| | | </u-checkbox-group> |
| | | </view> |
| | | </scroll-view> |
| | | </u-popup> |
| | |
| | | // isShowAircraftInfo.value = false |
| | | // } |
| | | function checkboxChange(val) { |
| | | formParams.value.spacecraftId = val.join(',') |
| | | formParams.value.spacecraftId = val |
| | | formParams.value.spacecraftText = aircraftInfoList.value.filter(item => val.includes(item.value)).map(item => item.label).join(',') |
| | | } |
| | | const onPickerAircraftInfo = () => { |
| | |
| | | }) |
| | | } |
| | | function checkboxPilotChange(val) { |
| | | formParams.value.pilotId = val.join(',') |
| | | formParams.value.pilotId = val |
| | | formParams.value.pilotText = pilotInfoList.value.filter(item => val.includes(item.value)).map(item => item.label).join(',') |
| | | } |
| | | const onPickerPilotInfo = (e) => { |
| | |
| | | const submitForm = async () => { |
| | | await formRef.value.validate() |
| | | isSubmitDisabled.value = true |
| | | formParams.value.pilotId = formParams.value.pilotId.join(',') |
| | | formParams.value.spacecraftId = formParams.value.spacecraftId.join(',') |
| | | flightPlanSaveApi(formParams.value).then(res => { |
| | | if (res.data.code === 200) { |
| | | isSubmitDisabled.value = true |
| | |
| | | padding: 20rpx 20rpx; |
| | | font-size: 32rpx; |
| | | color: #222324; |
| | | .select-header-btn { |
| | | display: flex; |
| | | font-size: 28rpx; |
| | | .cancel-btn { |
| | | color: #86909C; |
| | | margin-right: 20rpx; |
| | | } |
| | | .confirm-btn { |
| | | color: #1D6FE9; |
| | | .cancel-btn { |
| | | color: #86909C; |
| | | margin-right: 20rpx; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .select-content { |
| | | height: 600rpx; |
| | | height: 480rpx; |
| | | text-align: center; |
| | | padding: 20rpx 20rpx; |
| | | } |
| | |
| | | |
| | | |
| | | .rowTitle { |
| | | width: 160rpx; |
| | | font-family: Source Han Sans CN, Source Han Sans CN; |
| | | font-weight: 400; |
| | | font-size: 30rpx; |