吉安感知网项目-前端
罗广辉
2026-02-02 d1b42b3ad9cd9c430e522ab629594196f2404316
Merge remote-tracking branch 'origin/master'
15 files modified
1 files added
782 ■■■■ changed files
applications/drone-command/env/.env.development 4 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/api.txt 70 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/partition/1.json patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue 225 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/precinctInfo/precinctInfoApi.js 9 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/fwDevice.js 9 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/styles/common/cockpit.scss 20 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/appConfiguration/materials/FormDiaLog.vue 8 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/appConfiguration/materials/index.vue 6 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderDataManage/evaluate/index.vue 15 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/ViewDiaLog.vue 2 ●●● patch | view | raw | blame | history
uniapps/work-wx/src/api/index.js 16 ●●●●● patch | view | raw | blame | history
uniapps/work-wx/src/subPackages/deviceRegistration/add.vue 96 ●●●●● patch | view | raw | blame | history
uniapps/work-wx/src/subPackages/flightApplication/add.vue 151 ●●●● patch | view | raw | blame | history
uniapps/work-wx/src/subPackages/flightApplication/details.vue 74 ●●●● patch | view | raw | blame | history
uniapps/work-wx/src/subPackages/flightApplication/index.vue 77 ●●●● patch | view | raw | blame | history
applications/drone-command/env/.env.development
@@ -2,7 +2,7 @@
 # @Author       : yuan
 # @Date         : 2026-01-31 14:34:31
 # @LastEditors  : yuan
 # @LastEditTime : 2026-01-31 15:18:17
 # @LastEditTime : 2026-02-02 11:01:46
 # @FilePath     : \applications\drone-command\env\.env.development
 # @Description  : 
 # Copyright 2026 OBKoro1, All Rights Reserved. 
@@ -16,7 +16,7 @@
#开发环境代理地址(推荐本地新建文件 .env.development.local 来进行覆盖)
# VITE_APP_URL=https://wrj.shuixiongit.com/api
VITE_APP_URL=http://192.168.1.33
VITE_APP_URL=http://192.168.1.168
#新大屏地址
VITE_APP_DASHBOARD_URL='https://wrj.shuixiongit.com/command-center-dashboard/'
applications/drone-command/src/views/api.txt
@@ -1,22 +1,34 @@
## 列表
## 根据多边形区域查询派出所
**接口地址**:`/area/fwDefenseSceneManage/list`
**接口地址**:`/area/fwPoliceStation/queryByPolygons`
**请求方式**:`GET`
**请求方式**:`POST`
**请求数据类型**:`application/x-www-form-urlencoded`
**请求数据类型**:`application/json`
**响应数据类型**:`*/*`
**接口描述**:<p>传入time</p>
**接口描述**:<p>传入多边形WKT字符串集合</p>
**请求示例**:
```javascript
{
  "polygons": [
    "POLYGON((114.96898636473107 27.11156483383477,114.97972069576815 27.111564833834766,114.97972069576815 27.120473921808035,114.96898636473107 27.120473921808035,114.96898636473107 27.11156483383477))"
  ]
}
```
**请求参数**:
@@ -27,7 +39,8 @@
| 参数名称 | 参数说明 | 请求类型    | 是否必须 | 数据类型 | schema |
| -------- | -------- | ----- | -------- | -------- | ------ |
|time|时间筛选(yyyy-MM-dd HH:mm:ss)|query|false|string(date-time)||
|dto|派出所多边形区域查询入参|body|true|FwPoliceStationPolygonQueryDTO对象|FwPoliceStationPolygonQueryDTO对象|
|&emsp;&emsp;polygons|多边形WKT字符串集合||true|array|string|
**响应状态**:
@@ -35,7 +48,8 @@
| 状态码 | 说明 | schema |
| -------- | -------- | ----- | 
|200|OK|R«List«FwDefenseSceneManageVO»»|
|200|OK|R«List«FwPoliceStationVO»»|
|201|Created||
|401|Unauthorized||
|403|Forbidden||
|404|Not Found||
@@ -47,18 +61,21 @@
| 参数名称 | 参数说明 | 类型 | schema |
| -------- | -------- | ----- |----- | 
|code|状态码|integer(int32)|integer(int32)|
|data|承载数据|array|FwDefenseSceneManageVO|
|data|承载数据|array|FwPoliceStationVO|
|&emsp;&emsp;address|派出所地址|string||
|&emsp;&emsp;areaCode|区域编码|string||
|&emsp;&emsp;defenseLeader|防控负责人|string||
|&emsp;&emsp;defenseSceneId|关联场景配置ID|integer(int64)||
|&emsp;&emsp;defenseSceneName|场景配置名称|string||
|&emsp;&emsp;effectiveDateEnd|有效时间-结束|string(date-time)||
|&emsp;&emsp;effectiveDateStart|有效时间-开始|string(date-time)||
|&emsp;&emsp;id||integer(int64)||
|&emsp;&emsp;latitude|指挥点纬度|number(double)||
|&emsp;&emsp;leaderPhone|防控负责人电话|string||
|&emsp;&emsp;longitude|指挥点经度|number(double)||
|&emsp;&emsp;sceneName|场景名称|string||
|&emsp;&emsp;contactPerson|联系人|string||
|&emsp;&emsp;contactPhone|联系方式|string||
|&emsp;&emsp;createDept|创建部门|integer(int64)||
|&emsp;&emsp;createTime|创建时间|string(date-time)||
|&emsp;&emsp;createUser|创建人|integer(int64)||
|&emsp;&emsp;id|主键id|integer(int64)||
|&emsp;&emsp;latitude|纬度|number(double)||
|&emsp;&emsp;longitude|经度|number(double)||
|&emsp;&emsp;stationName|派出所名称|string||
|&emsp;&emsp;status|状态(0正常 1停用)|integer(int32)||
|&emsp;&emsp;updateTime|更新时间|string(date-time)||
|&emsp;&emsp;updateUser|更新人|integer(int64)||
|msg|返回消息|string||
|success|是否成功|boolean||
@@ -69,17 +86,20 @@
    "code": 0,
    "data": [
        {
            "address": "",
            "areaCode": "",
            "defenseLeader": "",
            "defenseSceneId": 0,
            "defenseSceneName": "",
            "effectiveDateEnd": "",
            "effectiveDateStart": "",
            "contactPerson": "",
            "contactPhone": "",
            "createDept": 0,
            "createTime": "",
            "createUser": 0,
            "id": 0,
            "latitude": 0,
            "leaderPhone": "",
            "longitude": 0,
            "sceneName": ""
            "stationName": "",
            "status": 0,
            "updateTime": "",
            "updateUser": 0
        }
    ],
    "msg": "",
applications/drone-command/src/views/areaManage/partition/1.json
applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue
@@ -226,7 +226,7 @@
<script setup>
import { Close } from '@element-plus/icons-vue'
import { computed, inject, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { computed, inject, nextTick, onBeforeUnmount, ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { fwAreaDivideDetailApi, fwAreaDivideSubmitApi } from './partitionApi'
import { saveOperationLog } from '@ztzf/apis'
@@ -248,8 +248,8 @@
} from '@/utils/cesium/deviceRange'
import * as Cesium from 'cesium'
import { AREA_TYPE_STYLE_MAP, BUFFER_LEVEL_STYLES, DEFAULT_AREA_STYLE } from '@ztzf/constants'
import { fwPoliceStationListApi } from '@/views/areaManage/precinctInfo/precinctInfoApi'
import { fwDeviceListApi } from '@/views/basicManage/deviceStock/fwDevice'
import { fwPoliceStationQueryByPolygonsApi } from '@/views/areaManage/precinctInfo/precinctInfoApi'
import { fwDeviceListByPolygonsApi } from '@/views/basicManage/deviceStock/fwDevice'
import polygonIcon from '@/assets/images/areaMap/polygon.png'
import rectIcon from '@/assets/images/areaMap/rect.png'
import ellipseIcon from '@/assets/images/areaMap/ellipse.png'
@@ -307,8 +307,13 @@
const deviceRangePrimitiveMap = new Map()
const activeToolMode = ref('')
const suppressDeviceFly = ref(false)
const isDialogInitializing = ref(false)
let lastSelectedDeviceIds = []
let deviceRangeRenderToken = 0
let deviceListTimer = null
let deviceListRequestToken = 0
let policeStationListTimer = null
let policeStationRequestToken = 0
const rules = {
    areaName: fieldRules(true, 50),
@@ -411,6 +416,14 @@
    () => visible.value,
    val => {
        if (!val) {
            if (deviceListTimer) {
                clearTimeout(deviceListTimer)
                deviceListTimer = null
            }
            if (policeStationListTimer) {
                clearTimeout(policeStationListTimer)
                policeStationListTimer = null
            }
            viewEntity && viewer?.entities?.remove(viewEntity)
            viewEntity = null
            clearActiveTool()
@@ -425,6 +438,15 @@
    val => {
        updateActiveShapeAreaType(val)
    }
)
watch(
    () => shapeList.value,
    () => {
        if (isDialogInitializing.value) return
        queueDeviceListRefresh()
        queuePoliceStationRefresh()
    },
    { deep: true }
)
// 加载详情
@@ -474,6 +496,103 @@
        viewer.scene.primitives.remove(primitive)
    }
    deviceRangePrimitiveMap.clear()
}
function clearDeviceListState () {
    deviceOptions.value = []
    selectedDeviceRows.value = []
    formData.value.deviceIds = ''
    if (deviceTableRef.value) {
        deviceTableRef.value.clearSelection()
    }
}
function clearPoliceStationState () {
    policeStationOptions.value = []
    formData.value.policeStationId = ''
}
function formatWktNumber (value) {
    if (!Number.isFinite(Number(value))) return null
    return Number(value).toFixed(6)
}
function isSameWktPoint (left, right) {
    if (!left || !right) return false
    return Math.abs(left.lat - right.lat) < 0.000001 && Math.abs(left.lng - right.lng) < 0.000001
}
function buildPolygonWktFromPoints (points) {
    if (!Array.isArray(points) || points.length < 3) return null
    const ring = points
        .map(resolveLngLatPoint)
        .filter(Boolean)
        .map(point => ({ lng: point.lng, lat: point.lat }))
    if (ring.length < 3) return null
    const first = ring[0]
    const last = ring[ring.length - 1]
    if (!isSameWktPoint(first, last)) {
        ring.push({ ...first })
    }
    const coordText = ring
        .map(point => {
            const lng = formatWktNumber(point.lng)
            const lat = formatWktNumber(point.lat)
            if (lat === null || lng === null) return null
            return `${lng} ${lat}`
        })
        .filter(Boolean)
        .join(', ')
    if (!coordText) return null
    return `POLYGON((${coordText}))`
}
function buildEllipseWktFromMeta (center, semiMajor, semiMinor) {
    if (!viewer) return null
    const resolvedCenter = resolveLngLatPoint(center)
    if (!resolvedCenter) return null
    if (!Number.isFinite(Number(semiMajor)) || !Number.isFinite(Number(semiMinor))) return null
    const centerCartesian = Cesium.Cartesian3.fromDegrees(
        resolvedCenter.lng,
        resolvedCenter.lat,
        resolvedCenter.height || 0
    )
    const positions = buildEllipsePositions(centerCartesian, Number(semiMajor), Number(semiMinor))
    if (!positions.length) return null
    const points = positions
        .map(item => {
            const converted = cartesian3Convert(item, viewer)
            return { lng: converted.longitude, lat: converted.latitude }
        })
        .filter(Boolean)
    return buildPolygonWktFromPoints(points)
}
function resolveShapePolygonsWkt () {
    const polygons = []
    shapeList.value.forEach(shape => {
        if (!shape) return
        if (shape.drawType === 'buffer') {
            const radii = Array.isArray(shape?.meta?.bufferRadii)
                ? shape.meta.bufferRadii.filter(item => Number.isFinite(item) && item > 0)
                : []
            const maxRadius = radii.length ? Math.max(...radii) : null
            const wkt = buildEllipseWktFromMeta(shape?.meta?.center, maxRadius, maxRadius)
            if (wkt) polygons.push(wkt)
            return
        }
        if (shape.drawType === 'ellipse') {
            const wkt = buildEllipseWktFromMeta(shape?.meta?.center, shape?.meta?.semiMajor, shape?.meta?.semiMinor)
            if (wkt) {
                polygons.push(wkt)
                return
            }
        }
        const points = getShapeDisplayPoints(shape)
        const wkt = buildPolygonWktFromPoints(points)
        if (wkt) polygons.push(wkt)
    })
    return polygons
}
async function renderDeviceRangeSingle (device) {
@@ -1069,18 +1188,77 @@
}
// 获取派出所列表
async function getPoliceStationList () {
    const res = await fwPoliceStationListApi()
function queuePoliceStationRefresh () {
    if (!visible.value) return
    if (policeStationListTimer) {
        clearTimeout(policeStationListTimer)
    }
    policeStationListTimer = setTimeout(() => {
        policeStationListTimer = null
        void getPoliceStationListByPolygons()
    }, 200)
}
async function getPoliceStationListByPolygons () {
    if (!visible.value) return
    const polygons = resolveShapePolygonsWkt()
    if (!polygons.length) {
        clearPoliceStationState()
        return
    }
    policeStationRequestToken += 1
    const requestToken = policeStationRequestToken
    const res = await fwPoliceStationQueryByPolygonsApi({ polygons })
    if (requestToken !== policeStationRequestToken) return
    policeStationOptions.value = res?.data?.data ?? []
    await nextTick()
    const matched = policeStationOptions.value.find(
        item => String(item.id) === String(formData.value.policeStationId)
    )
    if (!matched) {
        formData.value.policeStationId = ''
    }
}
// 获取设备列表
async function getDeviceList () {
    if (deviceOptions.value.length) return
    const res = await fwDeviceListApi({ isAreaSelect: 1, areaId: formData.value.id, isTrack: 1 })
function queueDeviceListRefresh () {
    if (!visible.value) return
    if (deviceListTimer) {
        clearTimeout(deviceListTimer)
    }
    deviceListTimer = setTimeout(() => {
        deviceListTimer = null
        void getDeviceListByPolygons()
    }, 200)
}
async function getDeviceListByPolygons () {
    if (!visible.value) return
    const polygons = resolveShapePolygonsWkt()
    if (!polygons.length) {
        clearDeviceListState()
        return
    }
    deviceListRequestToken += 1
    const requestToken = deviceListRequestToken
    const res = await fwDeviceListByPolygonsApi({
        isAreaSelect: 1,
        isTrack: 1,
        polygons,
    })
    if (requestToken !== deviceListRequestToken) return
    deviceOptions.value = res?.data?.data ?? []
    if (dialogMode.value === 'view') {
        deviceOptions.value = deviceOptions.value.filter(item => formData.value.deviceIds.includes(item.id))
    if (dialogMode.value === 'view' && formData.value.deviceIds) {
        const targetIds = formData.value.deviceIds.split(',')
        deviceOptions.value = deviceOptions.value.filter(item => targetIds.includes(String(item.id)))
    }
    await nextTick()
    const prevSuppressDeviceFly = suppressDeviceFly.value
    suppressDeviceFly.value = true
    syncDeviceSelection()
    suppressDeviceFly.value = prevSuppressDeviceFly
    if (!readonly.value) {
        renderDeviceRanges(selectedDeviceRows.value)
    }
}
@@ -1114,14 +1292,16 @@
// 同步选择状态
function syncDeviceSelection () {
    if (isDialogInitializing.value && !deviceOptions.value.length) return
    const rows = []
    const arr = formData.value.deviceIds.split(',')
    const arr = formData.value.deviceIds ? formData.value.deviceIds.split(',') : []
    deviceOptions.value.forEach(row => {
        if (arr.includes(row.id)) {
        if (arr.includes(String(row.id))) {
            rows.push(row)
        }
    })
    selectedDeviceRows.value = rows
    formData.value.deviceIds = rows.map(item => item.id).join(',')
    if (readonly.value) {
        renderDeviceRanges(selectedDeviceRows.value)
        return
@@ -1135,9 +1315,12 @@
// 打开弹框
async function open ({ mode, row } = {}) {
    isDialogInitializing.value = true
    dialogMode.value = mode || 'add'
    formData.value = dialogMode.value === 'add' ? initForm() : row
    selectedDeviceRows.value = []
    deviceOptions.value = []
    policeStationOptions.value = []
    shapeList.value = []
    activeShapeId.value = null
    activeAreaType.value = ''
@@ -1149,7 +1332,6 @@
    viewEntity && viewer?.entities?.remove(viewEntity)
    viewEntity = null
    clearActiveTool()
    await getDeviceList()
    if (dialogMode.value === 'add') {
        mapRef.value?.zoomToAdminBoundary?.()
        // default no draw mode
@@ -1169,13 +1351,20 @@
    if (!readonly.value) {
        renderDeviceRanges(selectedDeviceRows.value)
    }
    isDialogInitializing.value = false
    queueDeviceListRefresh()
    queuePoliceStationRefresh()
}
onMounted(() => {
    getPoliceStationList()
})
onBeforeUnmount(() => {
    if (deviceListTimer) {
        clearTimeout(deviceListTimer)
        deviceListTimer = null
    }
    if (policeStationListTimer) {
        clearTimeout(policeStationListTimer)
        policeStationListTimer = null
    }
    hideTypePanel()
})
@@ -1260,10 +1449,10 @@
    cursor: pointer;
    transition: all 0.2s ease;
    overflow: hidden;
    box-sizing: border-box;
}
.tool-btn:hover {
    background: rgba(255, 255, 255, 0.16);
    border-color: rgba(255, 255, 255, 0.35);
    transform: translateY(-1px);
}
applications/drone-command/src/views/areaManage/precinctInfo/precinctInfoApi.js
@@ -18,6 +18,15 @@
    })
}
// 多边形区域查询派出所
export const fwPoliceStationQueryByPolygonsApi = data => {
    return request({
        url: `/drone-fw/area/fwPoliceStation/queryByPolygons`,
        method: 'post',
        data,
    })
}
// 增加或更新
export const fwPoliceStationSubmitApi = data => {
    return request({
applications/drone-command/src/views/basicManage/deviceStock/fwDevice.js
@@ -18,6 +18,15 @@
    })
}
// 多边形过滤设备列表
export const fwDeviceListByPolygonsApi = data => {
    return request({
        url: `/drone-fw/device/fwDevice/listByPolygons`,
        method: 'post',
        data,
    })
}
// 增加或更新
export const fwDeviceSubmitApi = data => {
    return request({
applications/task-work-order/src/styles/common/cockpit.scss
@@ -31,6 +31,7 @@
    &::placeholder {
      color: var(--tips);
    }
    color: #383874;
  }
@@ -123,6 +124,7 @@
    &::placeholder {
      color: var(--tips);
    }
    color: #383874;
  }
@@ -1209,5 +1211,21 @@
  //  .el-form-item:nth-last-child(1) {
  //   margin-bottom: 0;
  // }
  .el-input,
  .el-input-number {
    width: 0;
    flex: 1;
    .el-input__wrapper {
      padding: 0 8px;
    }
    .el-input__inner {
      text-align: left;
      &::placeholder {
        text-align: left;
      }
    }
  }
}
applications/task-work-order/src/views/orderView/appConfiguration/materials/FormDiaLog.vue
@@ -24,10 +24,10 @@
                <div class="label">是否适用全部情形</div>
                <div class="val">{{ getDictLabel(String(formData.isAllScenario), dictObj.isAllScenario) }}</div>
            </el-col>
            <el-col :span="12">
            <!-- <el-col :span="12">
                <div class="label">所属区域</div>
                <div class="val">{{ formData.areaName }}</div>
            </el-col>
            </el-col> -->
        </el-row>
        <el-form
@@ -86,7 +86,7 @@
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                <!-- <el-col :span="12">
                    <el-form-item label="所属区域" prop="areaCode">
                        <el-tree-select
                            class="gd-select"
@@ -104,7 +104,7 @@
                            :render-after-expand="false"
                        />
                    </el-form-item>
                </el-col>
                </el-col> -->
            </el-row>
        </el-form>
        <template #footer v-if="!dialogReadonly">
applications/task-work-order/src/views/orderView/appConfiguration/materials/index.vue
@@ -11,7 +11,7 @@
                />
            </el-form-item>
            <el-form-item label="所属区划" prop="areaCode">
            <!-- <el-form-item label="所属区划" prop="areaCode">
                <el-tree-select
                    class="gd-select gray"
                    popper-class="gd-tree-select-popper"
@@ -27,7 +27,7 @@
                    :load="loadRegionNode"
                    :render-after-expand="false"
                />
            </el-form-item>
            </el-form-item> -->
            <el-form-item class="gd-search-actions">
                <el-button :icon="RefreshRight" @click="resetForm"></el-button>
@@ -59,7 +59,7 @@
                            {{ getDictLabel(String(row.isAllScenario), dictObj.isAllScenario) }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="areaName" show-overflow-tooltip label="所属区域" />
                    <!-- <el-table-column prop="areaName" show-overflow-tooltip label="所属区域" /> -->
                    <el-table-column label="操作" class-name="operation-btns" width="200">
                        <template v-slot="{ row }">
                            <el-link type="primary" @click="openForm('view', row)">查看</el-link>
applications/task-work-order/src/views/orderView/orderDataManage/evaluate/index.vue
@@ -80,7 +80,6 @@
</template>
<script setup>
import { Search, RefreshRight, Plus, Delete } from '@element-plus/icons-vue'
import { onMounted, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getDeptTree } from '@/api/system/dept'
import FormDiaLog from './FormDiaLog.vue'
@@ -91,12 +90,15 @@
import { useStore } from 'vuex'
const store = useStore()
const userDepartment = store.state.user.userInfo?.dept_id
const permission = computed(() => store.state.user.permission);
console.log('ppppppp', permission.value.sjyy_shtg)
// 初始化查询参数
const initSearchParams = () => ({
    title: '', // 标题
    isResolved: '', // 是否解决
    current: 1, // 当前页
    size: 10, // 每页大小
    isQueryAll:''//是否查询所有
})
const searchParams = ref(initSearchParams()) // 查询参数
const total = ref(0) // 总条数
@@ -122,7 +124,16 @@
async function getList() {
    loading.value = true
    try {
        const res = await gdDataEvaluationPageApi(searchParams.value)
        // 根据权限设置查询参数
        const params = { ...searchParams.value }
        if (permission.value.sjyy_shtg) {
            params.isQueryAll = true
            params.isResolved = '0'
        } else {
            params.isQueryAll = false
            delete params.isResolved
        }
        const res = await gdDataEvaluationPageApi(params)
        res?.data?.data?.records.forEach(item => {    
            item.isTheSameDepartment = String(item.createDept) === String(userDepartment)
        })
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/ViewDiaLog.vue
@@ -538,7 +538,7 @@
    .item-content {
        position: relative;
        .flowName {
            width: 80px;
            width: 72px;
            position: absolute;
            left: -120px;
            top: 0px;
uniapps/work-wx/src/api/index.js
@@ -61,6 +61,22 @@
    method: 'get'
  })
}
export const takeoffOperationApi = data => {
  return request({
    url: `/webservice/flightPlan/takeoffOperation`,
    method: 'post',
    data,
  })
}
export const flightApprovalApi = data => {
  return request({
    url: `/webservice/flightPlan/flightApproval`,
    method: 'post',
    data,
  })
}
// 航空器注册
export const aircraftInfoSaveApi = data => {
uniapps/work-wx/src/subPackages/deviceRegistration/add.vue
@@ -199,7 +199,6 @@
            <template #right>
                <u-upload
                    @afterRead="afterReadImage"
                    @delete="deletePic"
                    name="6"
                    multiple
                    :maxCount="1"
@@ -231,13 +230,50 @@
            @cancel="showType = false"
        />
        <!-- 飞行器厂商 -->
        <u-picker
        <!-- <u-picker
            :show="showManufacturer"
            :columns="[manufacturerList]"
            keyName="label"
            @confirm="onPickerConfirmManufacturer"
            @cancel="showManufacturer = false"
        />
        /> -->
        <u-popup
            :show="showManufacturer"
            mode="bottom"
            round="20"
            @close="showManufacturer = false"
        >
        <scroll-view scroll-y>
            <view class="select-header">
                <u-search
                    v-model="searchFacturerName"
                    placeholder="请输入飞行器厂商名称关键字"
                    @custom="getManufacturerInfoApi"
                ></u-search>
            </view>
            <view class="select-content">
                <!-- 加载状态 -->
            <view v-if="loadingManufacturer" class="loading-container">
                <u-loading-icon></u-loading-icon>
            </view>
                <u-radio-group
                v-if="!loadingManufacturer"
                    v-model="formParams.spacecraftId"
                    placement="column"
                    @change="radioChange"
                >
                    <u-radio
                        :customStyle="{marginBottom: '8px'}"
                        v-for="(item, index) in manufacturerList"
                        :key="item.id"
                        :label="item.label"
                        :name="item.value"
                    >
                </u-radio>
            </u-radio-group>
            </view>
            </scroll-view>
        </u-popup>
        <u-picker
            :show="showIsModified"
            :columns="[actionsModified]"
@@ -335,6 +371,10 @@
    ],
})
// 搜索飞行器厂商
const searchFacturerName = ref('')
// 是否改装
const actionsModified = ref([
    {
@@ -411,12 +451,17 @@
// 飞行器厂商
const showManufacturer = ref(false);
const manufacturerList = ref([])
const loadingManufacturer = ref(false)
// 选择飞行器厂商
const onPickerConfirmManufacturer = (e) => {
  const selected = e.value[0]
  formParams.value.manufacturer = selected.value
  formParams.value.manufacturerText = selected.label
  showManufacturer.value = false
}
function radioChange(value) {
    formParams.value.manufacturer = value
    formParams.value.manufacturerText = manufacturerList.value.find(item => item.value === value)?.label || ''
}
// 飞行器类型
const showType = ref(false);
@@ -589,10 +634,20 @@
// }
// 获取无人机厂商
function getManufacturerInfoApi() {
    if (searchFacturerName.value === '') {
        uni.showToast({
            title: '请输入搜索内容',
            icon: 'none'
        })
        return
    }
    loadingManufacturer.value = true
    manufacturerInfoApi({
        unitName: searchFacturerName.value,
        current: 1,
        size: 1000,
    }).then(res => {
        loadingManufacturer.value = false
        manufacturerList.value = res.data.data.records.map(item => ({
            ...item,
            label: item.unitName,
@@ -601,7 +656,7 @@
    })
}
onMounted(async () => {
    getManufacturerInfoApi()
    // getManufacturerInfoApi()
    // 动态从外部URL导入xzqhData,确保在使用前已初始化
    // 注意:直接动态导入外部URL可能会有跨域或构建问题,建议检查服务器CORS设置
    const response = await fetch('https://wrj.shuixiongit.com/aiskyminio/cloud-bucket/ja-app-wx/xzqh/index.json')
@@ -711,5 +766,38 @@
            margin-right: 8rpx;
        }
    }
    .uni-scroll-view {
       display: flex;
       flex-direction: column;
    }
    .select-header {
        display: flex;
        justify-content: space-between;
        padding: 20rpx 20rpx;
        font-size: 32rpx;
        color: #222324;
        .select-header-btn {
            display: flex;
            font-size: 28rpx;
            color: #1D6FE9;
            .cancel-btn {
                color: #86909C;
                margin-right: 20rpx;
            }
        }
    }
    .select-content {
        height: 400rpx;
        text-align: center;
        padding: 20rpx 20rpx;
        .loading-container {
            height: 400rpx;
            display: flex;
            align-items: center;
            justify-content: center;
        }
    }
}
</style>
uniapps/work-wx/src/subPackages/flightApplication/add.vue
@@ -209,14 +209,14 @@
        <u-form-item
                label="飞行器"
                labelWidth="230rpx"
                prop="aircraftId"
                prop="spacecraftText"
                :borderBottom="true"
                required
                ref="item2"
                @click="isShowAircraftInfo = true"
        >
            <u-input
                v-model="formParams.aircraftText"
                v-model="formParams.spacecraftText"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择"
@@ -231,7 +231,7 @@
        <u-form-item
                label="飞手"
                labelWidth="230rpx"
                prop="pilotId"
                prop="pilotText"
                :borderBottom="true"
                ref="item2"
                required
@@ -276,7 +276,7 @@
        <u-form-item
                label="航线"
                labelWidth="230rpx"
                prop="routeId"
                prop="selectedRouteId"
                :borderBottom="true"
                ref="item2"
                required
@@ -350,21 +350,85 @@
    <!-- <u-calendar :show="isShowTimeRange" mode="date" @confirm="onConfirmTimeRange" @close="isShowTimeRange = false" :close-on-click-overlay="true"></u-calendar> -->
    <!-- <u-calendar :show="isShowTimeRange" mode="range" @confirm="onConfirmTimeRange" @close="isShowTimeRange = false" :close-on-click-overlay="true" :allow-same-day="true" :show-time="true" time-format="HH:mm:ss"></u-calendar> -->
    <!-- 飞行器 -->
     <u-picker
     <!-- <u-picker
        :show="isShowAircraftInfo"
        :columns="[aircraftInfoList]"
        keyName="label"
        @confirm="onPickerAircraftInfo"
        @cancel="isShowAircraftInfo = false"
    />
    /> -->
    <u-popup
    :show="isShowAircraftInfo"
    mode="bottom"
    round="20"
    @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 class="select-content">
          <up-checkbox-group
            v-model="formParams.spacecraftId"
            placement="column"
            @change="checkboxChange"
        >
            <up-checkbox
                :customStyle="{marginBottom: '8px'}"
                v-for="(item, index) in aircraftInfoList"
                :key="item.id"
                :label="item.label"
                :name="item.value"
            >
            </up-checkbox>
        </up-checkbox-group>
    </view>
    </scroll-view>
  </u-popup>
    <!-- 飞手 -->
    <u-picker
    <!-- <u-picker
        :show="isShowPilotInfo"
        :columns="[pilotInfoList]"
        keyName="label"
        @confirm="onPickerPilotInfo"
        @cancel="isShowPilotInfo = false"
    />
    /> -->
    <u-popup
    :show="isShowPilotInfo"
    mode="bottom"
    round="20"
    @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 class="select-content">
          <up-checkbox-group
            v-model="formParams.pilotId"
            placement="column"
            @change="checkboxPilotChange"
        >
            <up-checkbox
                :customStyle="{marginBottom: '8px'}"
                v-for="(item, index) in pilotInfoList"
                :key="item.id"
                :label="item.label"
                :name="item.value"
            >
            </up-checkbox>
        </up-checkbox-group>
    </view>
    </scroll-view>
  </u-popup>
    <!-- 空域 -->
    <u-picker
        :show="isShowAirspaceInfo"
@@ -427,13 +491,13 @@
    flightStartTime: '', // 飞行活动开始时间
    flightEndTime: '', // 飞行活动结束时间
    weatherCondition: '', // 气象条件
    spacecraftId: '', // 航天器id
    spacecraftId: [], // 航天器id
    spacecraftText: '', // 航天器文本
    pilotId: '', // 飞手id
    pilotId: [], // 飞手id
    pilotText: '', // 飞手文本
    airspaceId: '', // 空域id
    airspaceText: '', // 空域文本
    routeId: '', // 航线id
    selectedRouteId: '', // 航线id
    routeText: '', // 航线文本
})
const rules = ref({
@@ -491,16 +555,16 @@
    endTime: [
        { required: true, message: '请选择飞行活动结束时间', trigger: 'change' }
    ],
    spacecraftId: [
    spacecraftText: [
        { required: true, message: '请选择飞行器', trigger: 'change' }
    ],
    pilotId: [
    pilotText: [
        { required: true, message: '请选择飞手', trigger: 'change' }
    ],
    airspaceId: [
        { required: true, message: '请选择空域', trigger: 'change' }
    ],
    routeId: [
    selectedRouteId: [
        { required: true, message: '请选择航线', trigger: 'change' }
    ],
})
@@ -550,11 +614,11 @@
const endTime = ref(Date.now()); // 设置默认值为当前时间戳
const onConfirmStartTime = (e) => {
    formParams.value.flightStartTime = dayjs(e.value).format('YYYY-MM-DD HH:mm')
    formParams.value.flightStartTime = dayjs(e.value).format('YYYY-MM-DD HH:mm:ss')
    isShowStartTime.value = false
}
const onConfirmEndTime = (e) => {
    formParams.value.flightEndTime = dayjs(e.value).format('YYYY-MM-DD HH:mm')
    formParams.value.flightEndTime = dayjs(e.value).format('YYYY-MM-DD HH:mm:ss')
    isShowEndTime.value = false
}
@@ -574,10 +638,17 @@
        }))
    })
}
const onPickerAircraftInfo = (e) => {
  const selected = e.value[0]
  formParams.value.aircraftId = selected.value
  formParams.value.aircraftText = selected.label
// const onPickerAircraftInfo = (e) => {
//   const selected = e.value[0]
//   formParams.value.spacecraftId = selected.value
//   formParams.value.aircraftText = selected.label
//   isShowAircraftInfo.value = false
// }
function checkboxChange(val) {
    formParams.value.spacecraftId = val.join(',')
    formParams.value.spacecraftText = aircraftInfoList.value.filter(item => val.includes(item.value)).map(item => item.label).join(',')
}
const onPickerAircraftInfo = () => {
  isShowAircraftInfo.value = false
}
// 获取飞手信息
@@ -594,10 +665,14 @@
        }))
    })
}
function checkboxPilotChange(val) {
    formParams.value.pilotId = val.join(',')
    formParams.value.pilotText = pilotInfoList.value.filter(item => val.includes(item.value)).map(item => item.label).join(',')
}
const onPickerPilotInfo = (e) => {
  const selected = e.value[0]
  formParams.value.pilotId = selected.value
  formParams.value.pilotText = selected.label
//   const selected = e.value[0]
//   formParams.value.pilotId = selected.value
//   formParams.value.pilotText = selected.label
  isShowPilotInfo.value = false
}
// 获取空域信息
@@ -636,7 +711,7 @@
}
const onPickerFlightRouteInfo = (e) => {
  const selected = e.value[0]
  formParams.value.routeId = selected.value
  formParams.value.selectedRouteId = selected.value
  formParams.value.routeText = selected.label
  isShowFlightRouteInfo.value = false
}
@@ -866,5 +941,33 @@
            margin-right: 8rpx;
        }
    }
    .uni-scroll-view {
       display: flex;
       flex-direction: column;
    }
    .select-header {
        display: flex;
        justify-content: space-between;
        padding: 20rpx 20rpx;
        font-size: 32rpx;
        color: #222324;
        .select-header-btn {
            display: flex;
            font-size: 28rpx;
            color: #1D6FE9;
            .cancel-btn {
                color: #86909C;
                margin-right: 20rpx;
            }
        }
    }
    .select-content {
        height: 400rpx;
        text-align: center;
        padding: 20rpx 20rpx;
    }
}
</style>
uniapps/work-wx/src/subPackages/flightApplication/details.vue
@@ -136,7 +136,7 @@
                ref="item1"
        >
            <u-input
                    v-model="formParams.weatherCondition"
                    v-model="formParams.weatherCondition"
                    readonly
                    border="none"
            ></u-input>
@@ -185,39 +185,39 @@
        <u-form-item
                label="航线"
                labelWidth="230rpx"
                prop="routeId"
                prop="selectedRouteId"
                :borderBottom="true"
                ref="item2"
        >
            <u-input
                v-model="formParams.routeId"
                v-model="formParams.selectedRouteId"
                readonly
                border="none"
            ></u-input>
        </u-form-item>
        <u-popup :show="applyShow" :round="10" mode="center" @close="close" :closeable="true" :mask-close-able="true">
         <u-form labelPosition="top" :model="formParams" :rules="rules" ref="formRef">
         <u-form labelPosition="top" :model="formParamsTime" :rules="rulesTime" ref="formTimeRef">
            <u-form-item
                label="计划申请起飞时间"
                labelWidth="260rpx"
                prop="actualTakeoffTime"
                prop="takeOffStartTime"
                :borderBottom="true"
                ref="item1"
                @click="isShowStartTime = true"
                required
        >
            <u-input v-model="formParams.actualTakeoffTime" border="none" placeholder="请选择" suffixIcon="calendar"></u-input>
            <u-input v-model="formParamsTime.takeOffStartTime" border="none" placeholder="请选择" suffixIcon="calendar"></u-input>
        </u-form-item>
         <u-form-item
                label="计划申请结束时间"
                labelWidth="260rpx"
                prop="actualLandingTime"
                prop="takeOffEndTime"
                :borderBottom="true"
                ref="item1"
                @click="isShowEndTime = true"
                required
        >
            <u-input v-model="formParams.actualLandingTime" border="none" placeholder="请选择" suffixIcon="calendar"></u-input>
            <u-input v-model="formParamsTime.takeOffEndTime" border="none" placeholder="请选择" suffixIcon="calendar"></u-input>
        </u-form-item>
        <u-datetime-picker
@@ -239,10 +239,10 @@
            @cancel="isShowEndTime = false">
        </u-datetime-picker>
         </u-form>
         <u-button @click="submitApply" color="#1D6FE9">确定</u-button>
         <u-button @click="submitSure" color="#1D6FE9">确定</u-button>
    </u-popup>
    </u-form>
    <u-button v-if="formParams.planApprovalStatus === '99'" @click="submitApply" color="#1D6FE9">申请</u-button>
    <u-button v-if="formParams.planApprovalStatus === '99' && (formParams.planStatus === '0')" @click="submitApply" color="#1D6FE9">申请</u-button>
</view>
</template>
<script setup>
@@ -252,6 +252,7 @@
    aircraftInfoPageInfoOfMyApi,
    pilotInfoPageInfoOfMyApi,
    flightAirspacePageInfoApi,
    flightApprovalApi
 } from '@/api/index'
import { ref, computed, onMounted } from 'vue'
import dayjs from 'dayjs'
@@ -261,8 +262,20 @@
const appStore = useAppStore()
const formRef = ref(null);
const formTimeRef = ref(null);
const formParams = ref({})
const rules = ref({})
const formParamsTime = ref({
    id: '',
    takeOffStartTime: '',
    takeOffEndTime: '',
    planStatus: '1',
})
const rulesTime = ref({
    takeOffStartTime: [{ required: true, message: '请选择计划申请起飞时间', trigger: ['blur'] }],
    takeOffEndTime: [{ required: true, message: '请选择计划申请结束时间', trigger: ['blur'] }],
})
// 任务类型
const actionsFlightTaskType = ref(appStore.getTaskType)
@@ -284,11 +297,11 @@
const endTime = ref(Date.now()); // 设置默认值为当前时间戳
const onConfirmStartTime = (e) => {
    formParams.value.actualTakeoffTime = dayjs(e.value).format('YYYY-MM-DD HH:mm')
    formParamsTime.value.takeOffStartTime = dayjs(e.value).format('YYYY-MM-DD HH:mm:ss')
    isShowStartTime.value = false
}
const onConfirmEndTime = (e) => {
    formParams.value.actualLandingTime = dayjs(e.value).format('YYYY-MM-DD HH:mm')
    formParamsTime.value.takeOffEndTime = dayjs(e.value).format('YYYY-MM-DD HH:mm:ss')
    isShowEndTime.value = false
}
@@ -370,10 +383,28 @@
   applyShow.value = true
}
async function submitSure() {
    // 校验时间
    await formTimeRef.value.validate()
    formParamsTime.value.id = formParams.value.id
    flightApprovalApi(formParamsTime.value).then(res => {
        if (res.data.code === 200) {
            uni.showToast({
                title: '操作成功',
                icon: 'success',
            })
            // 跳转申请列表页
            uni.navigateTo({
                url: '/subPackages/flightApplication/index',
            })
        }
    })
}
function close() {
    // 清空时间
    formParams.value.actualTakeoffTime = ''
    formParams.value.actualLandingTime = ''
    formParamsTime.value.takeOffStartTime = ''
    formParamsTime.value.takeOffEndTime = ''
    applyShow.value = false
}
@@ -381,7 +412,6 @@
     flightPlanDetailsApi(options.id).then(res => {
        // return
        const result = res.data.data
        console.log(result, '7777')
        formParams.value = result
        // 字典
        formParams.value.flightRule = actionsFlightRule.value.find(item => item.dictValue === result.flightRule)?.dictLabel || '无'
@@ -389,11 +419,19 @@
        formParams.value.planType = actionsPlanType.value.find(item => item.dictValue === result.planType)?.dictLabel || '无'
        formParams.value.flightTaskType = actionsFlightTaskType.value.find(item => item.dictValue === result.flightTaskType)?.dictLabel || '无'
        // 数据
        formParams.value.routeId = result?.routeInfo?.name || '无'
        // 航线
        formParams.value.selectedRouteId = result?.routeInfo?.name || '无'
        // 空域
        formParams.value.airspaceId = result?.airspaceInfo?.taskName || '无'
        formParams.value.aircraftId = result?.airspaceInfo?.aircraftInfoList?.map(item => item.name).join(',') || '无'
        // 飞行器
        formParams.value.aircraftId = result?.aircraftInfoList?.map(item => item.name).join(',') || '无'
        // 飞手
        formParams.value.pilotId = result?.operatorInfoList?.map(item => item.name).join(',') || '无'
        // 日期赋值
        formParamsTime.value.takeOffStartTime = dayjs(result.flightStartTime).format('YYYY-MM-DD HH:mm:ss')
        formParamsTime.value.takeOffEndTime = dayjs(result.flightEndTime).format('YYYY-MM-DD HH:mm:ss')
    })
})
onMounted(() => {
uniapps/work-wx/src/subPackages/flightApplication/index.vue
@@ -25,11 +25,9 @@
                            <u-image :src="spSvg" class="icon" width="16" height="16"></u-image>
                            <view class="name">{{approvalRecord.flightPlanName}}</view>
                        </view>
                        <view class="status" :class="getStatusClass(approvalRecord.planApprovalStatus)">
                            {{ approvalRecord.planApprovalStatusLabel || ''}}
                        </view>
                    </view>
                    <view class="txt">计划起飞状态:{{approvalRecord.fly_activity_takeoff_status || ''}}</view>
                     <view class="txt">审批状态: <span :class="getStatusClass(approvalRecord.planApprovalStatus)">{{approvalRecord.planApprovalStatusLabel || ''}}</span></view>
                    <view class="txt">计划起飞状态:<span :class="getPlanStatusClass(approvalRecord.planStatus)">{{approvalRecord.flyActivityTakeoffStatusLabel || ''}}</span></view>
                    <view class="txt">飞行器:{{approvalRecord.spacecraftLabel || '无'}}</view>
                    <view class="txt">飞行任务:{{approvalRecord.flightTaskTypeLabel || ''}}</view>
                    <view class="txt">飞行起飞时间:{{approvalRecord.flightStartTime || ''}}</view>
@@ -81,6 +79,27 @@
    }
}
const getPlanStatusClass = (status) => {
    switch (status) {
        case '0':
            return 'Sred'
        case '1':
            return 'Ssqz'
        case '2':
            return 'Sjz'
        case '3':
            return 'SOrange'
            case '99':
            return 'Syzz'
            case '-1':
            return 'Ssqbh'
            case '-2':
            return 'Sysx'
        default:
            return ''
    }
}
// 获取飞行器信息
const aircraftInfoList = ref([])
async function getAircraftInfo() {
@@ -114,9 +133,12 @@
        const newData = res.data.data.records || []
        // 处理字典数据
        newData.forEach(item => {
            item.spacecraftLabel = aircraftInfoList.value.find(info => info.value === item.spacecraftId)?.label || '无'
            item.spacecraftLabel = aircraftInfoList?.value.filter(info => item.spacecraftId?.split(',').includes(info.value)).map(info => info.label).join('、') || '无'
            // 审批状态
            item.planApprovalStatusLabel = appStore.flyActivityStatus.find(status => status.dictValue === item.planApprovalStatus)?.dictLabel || '无'
            // 计划起飞状态
            item.flyActivityTakeoffStatusLabel = appStore.flyActivityTakeoffStatus.find(status => status.dictValue === item.planStatus)?.dictLabel || '无'
            // 飞行任务
            item.flightTaskTypeLabel = appStore.taskType.find(type => type.dictValue === item.flightTaskType)?.dictLabel || '无'
        })
        // 如果是第一页,直接替换数据;否则追加数据
@@ -275,18 +297,7 @@
            color: #FFFFFF;
            padding: 0 6rpx;
            &.Sorange {
                background: #FF9604;
            }
            &.Sred {
                background: #F8422A;
            }
            &.Sgreen {
                background: #17A836;
            }
            &.Sblue {
                background: #1D6FE9;
            }
        }
    }
@@ -295,6 +306,38 @@
        font-size: 12px;
        color: #86909C;
        margin-bottom: 14rpx;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    .Sorange {
        color: #FF9604;
    }
    .Sred {
        color: #F8422A;
    }
    .Sgreen {
        color: #17A836;
    }
    .Sblue {
        color: #1D6FE9;
    }
    .Sjz {
        color: #0088FF;
    }
    .Syzz {
        color: #002BFF;
    }
    .Ssqbh {
        color: #FF383C;
    }
    .Sysx {
        color:#414141;
    }
    .Ssqz {
        color: #FF9500;
    }
}
</style>