吉安感知网项目-前端
罗广辉
2026-01-17 9cc36560506cdbb3428aceefe1aaeb1ece7b6f49
Merge remote-tracking branch 'origin/master'
24 files modified
5 files added
2707 ■■■■ changed files
applications/drone-command/src/api/dataCockpit/index.js 9 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/styles/common/cockpit.scss 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/api.txt 343 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/DeviceChart2.vue 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/DeviceTrackDiaLog.vue 33 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/DeviceHistoryDialog.vue 67 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/map/address.js 9 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/appComponents/LeafletMap/index.vue 88 ●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/appDataSource/leafletMapIcon/search-normal.svg 14 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/appPages/AirMap/index.vue 2 ●●● patch | view | raw | blame | history
applications/mobile-web-view/src/appPages/AirMap/searchBar.vue 20 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/App.vue 17 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/hooks/useGlobalWS.js 104 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/pages/login/index.vue 22 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/store/index.js 3 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/store/modules/location.js 137 ●●●●● patch | view | raw | blame | history
uniapps/work-app/src/utils/location.js 260 ●●●●● patch | view | raw | blame | history
uniapps/work-app/src/utils/websocket.js 30 ●●●● patch | view | raw | blame | history
uniapps/work-wx/src/api/index.js 85 ●●●●● patch | view | raw | blame | history
uniapps/work-wx/src/pages.json 12 ●●●●● patch | view | raw | blame | history
uniapps/work-wx/src/pages/equipmentManagement/index.vue 30 ●●●● patch | view | raw | blame | history
uniapps/work-wx/src/pages/page/index.vue 8 ●●●● patch | view | raw | blame | history
uniapps/work-wx/src/subPackages/airspaceInformation/index.vue 60 ●●●● patch | view | raw | blame | history
uniapps/work-wx/src/subPackages/deviceRegistration/add.vue 81 ●●●●● patch | view | raw | blame | history
uniapps/work-wx/src/subPackages/deviceRegistration/details.vue 443 ●●●●● patch | view | raw | blame | history
uniapps/work-wx/src/subPackages/flightApplication/add.vue 153 ●●●●● patch | view | raw | blame | history
uniapps/work-wx/src/subPackages/flightApplication/details.vue 509 ●●●●● patch | view | raw | blame | history
uniapps/work-wx/src/subPackages/flightApplication/index.vue 8 ●●●● patch | view | raw | blame | history
uniapps/work-wx/src/subPackages/userDetail/infos/index.vue 156 ●●●● patch | view | raw | blame | history
applications/drone-command/src/api/dataCockpit/index.js
@@ -52,3 +52,12 @@
        method: 'get'
    })
}
// 区域列表
export const areaDivideListApi = (params) => {
    return request({
        url: '/drone-fw/area/fwAreaDivide/list',
        method: 'get',
        params
    })
}
applications/drone-command/src/styles/common/cockpit.scss
@@ -299,7 +299,7 @@
.command-data-cockpit-select {
    .el-select__input {
        color: #8ac3fd !important;
        color: #ffffff !important;
    }
    .el-select__wrapper {
applications/drone-command/src/views/api.txt
@@ -1,9 +1,9 @@
## 分页
## 列表
**接口地址**:`/device/fwDevice/page`
**接口地址**:`/drone-fw/area/fwAreaDivide/list`
**请求方式**:`GET`
@@ -15,7 +15,7 @@
**响应数据类型**:`*/*`
**接口描述**:<p>传入fwDevice</p>
**接口描述**:<p>传入filterSelected或sceneId</p>
@@ -27,12 +27,8 @@
| 参数名称 | 参数说明 | 请求类型    | 是否必须 | 数据类型 | schema |
| -------- | -------- | ----- | -------- | -------- | ------ |
|belongDept|所属部门|query|false|integer(int64)||
|current|当前页|query|false|integer(int32)||
|deviceName|设备名称|query|false|string||
|deviceType|设备类型(察打一体/便捷侦测箱/反制枪)|query|false|string||
|effectiveRangeKmIsNotNull|有效范围是否为空:0-不处理 1-不为空 2-为空|query|false|integer(int32)||
|size|每页的数量|query|false|integer(int32)||
|filterSelected|是否过滤已被选择的数据(1过滤 0不过滤)|query|false|integer(int32)||
|sceneId|场景id|query|false|integer(int64)||
**响应状态**:
@@ -40,7 +36,7 @@
| 状态码 | 说明 | schema |
| -------- | -------- | ----- | 
|200|OK|R«IPage«FwDeviceVO»»|
|200|OK|R«List«FwAreaDivide对象»»|
|401|Unauthorized||
|403|Forbidden||
|404|Not Found||
@@ -52,152 +48,32 @@
| 参数名称 | 参数说明 | 类型 | schema |
| -------- | -------- | ----- |----- | 
|code|状态码|integer(int32)|integer(int32)|
|data|承载数据|IPage«FwDeviceVO»|IPage«FwDeviceVO»|
|&emsp;&emsp;current||integer(int64)||
|&emsp;&emsp;pages||integer(int64)||
|&emsp;&emsp;records||array|FwDeviceVO|
|&emsp;&emsp;&emsp;&emsp;azimuth|方位角 0-360°|integer||
|&emsp;&emsp;&emsp;&emsp;batteryPct|电量百分比 0-100|integer||
|&emsp;&emsp;&emsp;&emsp;belongDept|所属部门id|integer||
|&emsp;&emsp;&emsp;&emsp;belongDeptName|所属部门名称|string||
|&emsp;&emsp;&emsp;&emsp;charger|负责人|string||
|&emsp;&emsp;&emsp;&emsp;contactPhone|联系电话|string||
|&emsp;&emsp;&emsp;&emsp;createTime|创建时间|string||
|&emsp;&emsp;&emsp;&emsp;detectTargetCnt|侦测目标数量|integer||
|&emsp;&emsp;&emsp;&emsp;deviceAtt|设备属性(无线电/光电/雷达)|string||
|&emsp;&emsp;&emsp;&emsp;deviceModel|型号|string||
|&emsp;&emsp;&emsp;&emsp;deviceName|设备名称|string||
|&emsp;&emsp;&emsp;&emsp;deviceSpecification|规格|string||
|&emsp;&emsp;&emsp;&emsp;deviceType|设备类型(察打一体/便捷侦测箱/反制枪)|string||
|&emsp;&emsp;&emsp;&emsp;effectiveRangeKm|有效范围 km|number||
|&emsp;&emsp;&emsp;&emsp;elevation|俯仰角 -90-+90°|integer||
|&emsp;&emsp;&emsp;&emsp;id|主键|integer||
|&emsp;&emsp;&emsp;&emsp;manufacturer|生产厂商|string||
|&emsp;&emsp;&emsp;&emsp;purpose|用途|string||
|&emsp;&emsp;&emsp;&emsp;remark|备注|string||
|&emsp;&emsp;&emsp;&emsp;source|来源|string||
|&emsp;&emsp;&emsp;&emsp;status|运行状态(0:在线/1:离线/2:故障/3:报废)|integer||
|&emsp;&emsp;&emsp;&emsp;workMode|工作模式:1.侦测中/2.信号干扰中/3.诱导驱离中/4.待机|string||
|&emsp;&emsp;size||integer(int64)||
|&emsp;&emsp;total||integer(int64)||
|msg|返回消息|string||
|success|是否成功|boolean||
**响应示例**:
```javascript
{
    "code": 0,
    "data": {
        "current": 0,
        "pages": 0,
        "records": [
            {
                "azimuth": 0,
                "batteryPct": 0,
                "belongDept": 0,
                "belongDeptName": "",
                "charger": "",
                "contactPhone": "",
                "createTime": "",
                "detectTargetCnt": 0,
                "deviceAtt": "",
                "deviceModel": "",
                "deviceName": "",
                "deviceSpecification": "",
                "deviceType": "",
                "effectiveRangeKm": 0,
                "elevation": 0,
                "id": 0,
                "manufacturer": "",
                "purpose": "",
                "remark": "",
                "source": "",
                "status": 0,
                "workMode": ""
            }
        ],
        "size": 0,
        "total": 0
    },
    "msg": "",
    "success": true
}
```
## 详情
**接口地址**:`/device/fwDevice/detail`
**请求方式**:`GET`
**请求数据类型**:`application/x-www-form-urlencoded`
**响应数据类型**:`*/*`
**接口描述**:<p>传入fwDevice</p>
**请求参数**:
**请求参数**:
| 参数名称 | 参数说明 | 请求类型    | 是否必须 | 数据类型 | schema |
| -------- | -------- | ----- | -------- | -------- | ------ |
|id|主键|query|true|integer(int64)||
**响应状态**:
| 状态码 | 说明 | schema |
| -------- | -------- | ----- |
|200|OK|R«FwDeviceVO»|
|401|Unauthorized||
|403|Forbidden||
|404|Not Found||
**响应参数**:
| 参数名称 | 参数说明 | 类型 | schema |
| -------- | -------- | ----- |----- |
|code|状态码|integer(int32)|integer(int32)|
|data|承载数据|FwDeviceVO|FwDeviceVO|
|&emsp;&emsp;azimuth|方位角 0-360°|integer(int32)||
|&emsp;&emsp;batteryPct|电量百分比 0-100|integer(int32)||
|&emsp;&emsp;belongDept|所属部门id|integer(int64)||
|&emsp;&emsp;belongDeptName|所属部门名称|string||
|&emsp;&emsp;charger|负责人|string||
|&emsp;&emsp;contactPhone|联系电话|string||
|data|承载数据|array|FwAreaDivide对象|
|&emsp;&emsp;areaCode|区域编码|string||
|&emsp;&emsp;areaName|区域名称|string||
|&emsp;&emsp;areaSize|区域面积(km²)|number||
|&emsp;&emsp;areaType|区域类型:报警区/重点管制区|string||
|&emsp;&emsp;controlLevel|管控级别:日常/重点/严密|string||
|&emsp;&emsp;createDept|创建部门|integer(int64)||
|&emsp;&emsp;createTime|创建时间|string(date-time)||
|&emsp;&emsp;detectTargetCnt|侦测目标数量|integer(int32)||
|&emsp;&emsp;deviceAtt|设备属性(无线电/光电/雷达)|string||
|&emsp;&emsp;deviceModel|型号|string||
|&emsp;&emsp;deviceName|设备名称|string||
|&emsp;&emsp;deviceSpecification|规格|string||
|&emsp;&emsp;deviceType|设备类型(察打一体/便捷侦测箱/反制枪)|string||
|&emsp;&emsp;effectiveRangeKm|有效范围 km|number||
|&emsp;&emsp;elevation|俯仰角 -90-+90°|integer(int32)||
|&emsp;&emsp;id|主键|integer(int64)||
|&emsp;&emsp;manufacturer|生产厂商|string||
|&emsp;&emsp;purpose|用途|string||
|&emsp;&emsp;remark|备注|string||
|&emsp;&emsp;source|来源|string||
|&emsp;&emsp;status|运行状态(0:在线/1:离线/2:故障/3:报废)|integer(int32)||
|&emsp;&emsp;workMode|工作模式:1.侦测中/2.信号干扰中/3.诱导驱离中/4.待机|string||
|&emsp;&emsp;createUser|创建人|integer(int64)||
|&emsp;&emsp;deviceIds|关联设备ID,逗号分隔|string||
|&emsp;&emsp;flyDateEnd|可飞行日期时间-结束|string(date-time)||
|&emsp;&emsp;flyDateStart|可飞行日期时间-开始|string(date-time)||
|&emsp;&emsp;geom|经纬度面(存储地理面数据)|string||
|&emsp;&emsp;id|主键id|integer(int64)||
|&emsp;&emsp;isDeleted|是否已删除|integer(int32)||
|&emsp;&emsp;latitude|区域中心纬度|number(double)||
|&emsp;&emsp;longitude|区域中心经度|number(double)||
|&emsp;&emsp;policeStationContactPerson|派出所联系人|string||
|&emsp;&emsp;policeStationContactPhone|派出所联系电话|string||
|&emsp;&emsp;policeStationId|关联派出所id|integer(int64)||
|&emsp;&emsp;policeStationName|派出所名称|string||
|&emsp;&emsp;responseMechanism|响应机制内容|string||
|&emsp;&emsp;status|业务状态|integer(int32)||
|&emsp;&emsp;triggerCondition|触发条件|string||
|&emsp;&emsp;updateTime|更新时间|string(date-time)||
|&emsp;&emsp;updateUser|更新人|integer(int64)||
|msg|返回消息|string||
|success|是否成功|boolean||
@@ -206,134 +82,35 @@
```javascript
{
    "code": 0,
    "data": {
        "azimuth": 0,
        "batteryPct": 0,
        "belongDept": 0,
        "belongDeptName": "",
        "charger": "",
        "contactPhone": "",
        "createTime": "",
        "detectTargetCnt": 0,
        "deviceAtt": "",
        "deviceModel": "",
        "deviceName": "",
        "deviceSpecification": "",
        "deviceType": "",
        "effectiveRangeKm": 0,
        "elevation": 0,
        "id": 0,
        "manufacturer": "",
        "purpose": "",
        "remark": "",
        "source": "",
        "status": 0,
        "workMode": ""
    },
    "msg": "",
    "success": true
}
```
## 新增或修改
**接口地址**:`/device/fwDevice/submit`
**请求方式**:`POST`
**请求数据类型**:`application/json`
**响应数据类型**:`*/*`
**接口描述**:<p>传入fwDevice</p>
**请求示例**:
```javascript
{
  "belongDept": 0,
  "charger": "",
  "contactPhone": "",
  "deviceAtt": "",
  "deviceModel": "",
  "deviceName": "",
  "deviceSpecification": "",
  "deviceStatusList": [],
  "deviceType": "",
  "effectiveRangeKm": 0,
  "effectiveRangeKmIsNotNull": 0,
  "id": 0,
  "manufacturer": "",
  "purpose": "",
  "source": ""
}
```
**请求参数**:
**请求参数**:
| 参数名称 | 参数说明 | 请求类型    | 是否必须 | 数据类型 | schema |
| -------- | -------- | ----- | -------- | -------- | ------ |
|fwDevice|fwDevice|body|true|FwDeviceDTO|FwDeviceDTO|
|&emsp;&emsp;belongDept|所属部门||false|integer(int64)||
|&emsp;&emsp;charger|负责人||false|string||
|&emsp;&emsp;contactPhone|联系电话||false|string||
|&emsp;&emsp;deviceAtt|设备属性(无线电/光电/雷达)||false|string||
|&emsp;&emsp;deviceModel|型号||false|string||
|&emsp;&emsp;deviceName|设备名称||true|string||
|&emsp;&emsp;deviceSpecification|规格||false|string||
|&emsp;&emsp;deviceStatusList|设备状态,多个状态用逗号隔开||false|array|string|
|&emsp;&emsp;deviceType|设备类型(察打一体/便捷侦测箱/反制枪)||false|string||
|&emsp;&emsp;effectiveRangeKm|有效范围 km||false|number||
|&emsp;&emsp;effectiveRangeKmIsNotNull|是否有范围 0不过滤 1有 2没有||false|integer(int32)||
|&emsp;&emsp;id|主键||false|integer(int64)||
|&emsp;&emsp;manufacturer|生产厂商||false|string||
|&emsp;&emsp;purpose|用途||false|string||
|&emsp;&emsp;source|来源||false|string||
**响应状态**:
| 状态码 | 说明 | schema |
| -------- | -------- | ----- |
|200|OK|R|
|201|Created||
|401|Unauthorized||
|403|Forbidden||
|404|Not Found||
**响应参数**:
| 参数名称 | 参数说明 | 类型 | schema |
| -------- | -------- | ----- |----- |
|code|状态码|integer(int32)|integer(int32)|
|data|承载数据|object||
|msg|返回消息|string||
|success|是否成功|boolean||
**响应示例**:
```javascript
{
    "code": 0,
    "data": {},
    "data": [
        {
            "areaCode": "",
            "areaName": "",
            "areaSize": 0,
            "areaType": "",
            "controlLevel": "",
            "createDept": 0,
            "createTime": "",
            "createUser": 0,
            "deviceIds": "",
            "flyDateEnd": "",
            "flyDateStart": "",
            "geom": "",
            "id": 0,
            "isDeleted": 0,
            "latitude": 0,
            "longitude": 0,
            "policeStationContactPerson": "",
            "policeStationContactPhone": "",
            "policeStationId": 0,
            "policeStationName": "",
            "responseMechanism": "",
            "status": 0,
            "triggerCondition": "",
            "updateTime": "",
            "updateUser": 0
        }
    ],
    "msg": "",
    "success": true
}
applications/drone-command/src/views/basicManage/deviceStock/DeviceChart2.vue
@@ -1,7 +1,7 @@
<template>
    <div class="deviceChart">
        <div class="titleBg">
            <span>设备类型分析</span>
            <span>出库去向统计</span>
        </div>
        <div class="chartBox" ref="chartRef"></div>
    </div>
applications/drone-command/src/views/basicManage/deviceStock/DeviceTrackDiaLog.vue
@@ -16,7 +16,15 @@
                </el-col>
                <el-col :span="12">
                    <el-form-item label="出库去向" prop="outTarget">
                        <el-input class="command-data-cockpit-search-input" v-model="formData.outTarget" maxlength="50" placeholder="请输入" clearable />
                        <el-select
                            class="command-data-cockpit-select"
                            popper-class="command-data-cockpit-select-popper"
                            v-model="formData.outTarget"
                            placeholder="请选择"
                            clearable
                        >
                            <el-option v-for="item in areaOptions" :key="item.value" :label="item.label" :value="item.label" />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
@@ -54,9 +62,10 @@
</template>
<script setup>
import { computed, ref } from 'vue'
import { computed, onMounted, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { getUserListApi } from '@/api/system/user'
import { getLazyTree } from '@/api/base/region'
import { fieldRules } from '@ztzf/utils'
import { fwDeviceTrackSubmitApi } from '@/views/basicManage/deviceStock/fwDeviceTrackApi'
@@ -84,6 +93,8 @@
    }
})
const areaOptions = ref([])
const rules = {
    charger: fieldRules(true), // 负责人
    contactPhone: fieldRules(true, 50), // 联系电话
@@ -93,6 +104,16 @@
}
const userList = ref([])
function getAreaOptions() {
    getLazyTree('360800000000').then(res => {
        const list = res?.data?.data ?? []
        areaOptions.value = list.map(item => ({
            label: item.title,
            value: item.value,
        }))
    })
}
function getUserList() {
    formData.value.charger = ''
    getUserListApi().then(res => {
@@ -111,7 +132,12 @@
    if (!isValid) return
    submitting.value = true
    try {
        await fwDeviceTrackSubmitApi(formData.value)
        const selected = areaOptions.value.find(item => item.value === formData.value.outTarget)
        const payload = {
            ...formData.value,
            outTarget: selected?.label ?? formData.value.outTarget,
        }
        await fwDeviceTrackSubmitApi(payload)
        ElMessage.success('出库成功')
        visible.value = false
        emit('success')
@@ -134,6 +160,7 @@
onMounted(() => {
    getUserList()
    getAreaOptions()
})
defineExpose({ open })
applications/drone-command/src/views/dataCockpit/components/DeviceHistoryDialog.vue
@@ -16,15 +16,14 @@
            <el-form-item label="区域" prop="area">
                <el-tree-select class="command-data-cockpit-select" popper-class="command-data-cockpit-tree-select-popper"
                    v-model="searchParams.area" :data="areaTree" :props="areaTreeProps" node-key="value" check-strictly
                    clearable placeholder="请选择" @change="handleSearch" />
                    v-model="searchParams.area" :data="areaTree" :props="areaTreeProps" node-key="value" check-strictly filterable :filter-node-method="filterAreaNode" clearable placeholder="请选择" @change="handleSearch" />
            </el-form-item>
            <el-form-item label="无人机类型" prop="droneType">
                <el-select class="command-data-cockpit-select" popper-class="command-data-cockpit-select-popper"
                    v-model="searchParams.droneType" placeholder="请选择" clearable @change="handleSearch">
                    <el-option v-for="item in droneTypeOptions" :key="item.value" :label="item.label"
                        :value="item.value" />
                    <el-option v-for="item in dictObj.droneType" :key="item.dictKey" :label="item.dictValue"
                        :value="item.dictKey" />
                </el-select>
            </el-form-item>
@@ -74,7 +73,9 @@
<script setup>
import { Search, RefreshRight } from '@element-plus/icons-vue'
import { computed, ref, watch } from 'vue'
import { alarmLogApi } from '@/api/dataCockpit'
import { getDictionaryByCode } from '@/api/system/dictbiz'
import { alarmLogApi, areaDivideListApi } from '@/api/dataCockpit'
import { getDictLabel } from '@ztzf/utils'
const props = defineProps({
    modelValue: {
@@ -111,29 +112,41 @@
const loading = ref(true)
const list = ref([])
const total = ref(0)
const areaTree = ref([
    {
        label: '古村区',
        value: '古村区'
    },
    {
        label: '新区',
        value: '新区'
    },
    {
        label: '产业园',
        value: '产业园'
    }
])
const dictObj = ref({
    droneType: []
})
const areaTree = ref([])
const areaTreeProps = {
    label: 'label',
    children: 'children'
}
const droneTypeOptions = [
    { label: '微型机', value: '1' },
    { label: '植保机', value: '2' }
]
const fetchDictList = async () => {
    const res = await getDictionaryByCode('droneType')
    dictObj.value = res?.data?.data ?? { droneType: [] }
}
const filterAreaNode = (value, data) => {
    if (!value) return true
    return (data?.label || '').includes(value)
}
const fetchAreaTree = async () => {
    try {
        const res = await areaDivideListApi()
        const list = res?.data?.data ?? []
        areaTree.value = list
            .filter((item) => item?.areaName)
            .map((item) => ({
                label: item.areaName,
                value: item.areaName
            }))
    } catch (error) {
        areaTree.value = []
    }
}
const getList = async () => {
    if (!props.device?.id) {
@@ -188,9 +201,7 @@
}
const getDroneTypeText = (value) => {
    if (value === 1 || value === '1') return '微型机'
    if (value === 2 || value === '2') return '植保机'
    return value || '-'
    return getDictLabel(value, dictObj.value.droneType) || value || '-'
}
watch(
@@ -198,7 +209,11 @@
    (visible) => {
        if (visible) {
            searchParams.value.current = 1
            fetchDictList()
            fetchAreaTree()
            getList()
        } else {
            searchParams.value = initSearchParams()
        }
    }
)
applications/mobile-web-view/src/api/map/address.js
@@ -28,3 +28,12 @@
    data
  })
}
// 我的空域查询
export const droneFlightTaskApi = data => {
  return request({
    url: `/webservice/droneFlightTask/pageInfoOfMy`,
    method: 'post',
    data,
  })
}
applications/mobile-web-view/src/appComponents/LeafletMap/index.vue
@@ -156,9 +156,9 @@
let locationFlag = false
let watchId = null
let userLocationMarker = null
const setMapLocation = () => {
    locationFlag = true
// 初始化实时位置监听
const initLocationWatch = () => {
    // 在WebView加载的网页中
    // 检查浏览器是否支持 geolocation
    if (navigator.geolocation) {
@@ -166,7 +166,6 @@
            // 停止监听位置
            navigator.geolocation.clearWatch(watchId)
        }
        // 开始持续获取用户的位置
        watchId = navigator.geolocation.watchPosition(
            function (position) {
@@ -174,31 +173,26 @@
                const lat = position.coords.latitude // 纬度
                const lng = position.coords.longitude // 经度
                if (locationFlag) {
                    if (userLocationMarker) {
                        userLocationMarker.setLatLng([lat, lng])
                    } else {
                        userLocationMarker = L.marker([lat, lng], {
                            icon: L.icon({
                // 确保位置标记存在并更新位置
                if (userLocationMarker) {
                    userLocationMarker.setLatLng([lat, lng])
                } else {
                    userLocationMarker = L.marker([lat, lng], {
                        icon: L.icon({
                                iconUrl: userLocationIcon, // 图片路径
                                iconSize: [24, 24], // 图标尺寸
                                iconAnchor: [12, 12], // 锚点位置
                            }),
                        }).addTo(map)
                    }
                                    iconSize: [24, 24], // 图标尺寸
                                    iconAnchor: [12, 12], // 锚点位置
                                }),
                    }).addTo(map)
                }
                // 如果是点击定位按钮触发的,将地图视图定位到当前位置
                if (locationFlag) {
                    mapSetView({
                        lat,
                        lng,
                    })
                    locationFlag = false
                    return
                }
                if (userLocationMarker) {
                    userLocationMarker.setLatLng([lat, lng])
                }
            },
            function (error) {
@@ -213,6 +207,24 @@
        )
    } else {
        console.log('该浏览器不支持地理定位功能。')
    }
}
const setMapLocation = () => {
    locationFlag = true
    // 如果还没有开始监听位置,先初始化监听
    if (!watchId) {
        initLocationWatch()
    } else {
        // 已经在监听位置,会自动定位到当前位置
        // 可以添加一个视觉反馈,比如位置标记闪烁效果
        if (userLocationMarker) {
            // 添加闪烁效果
            userLocationMarker.getElement().classList.add('location-blink')
            setTimeout(() => {
                userLocationMarker.getElement().classList.remove('location-blink')
            }, 1000)
        }
    }
}
let lastLocationMarker = null
@@ -450,6 +462,9 @@
    initMap()
    map.addLayer(layers[0].map)
    // 初始化实时位置监听
    initLocationWatch()
    EventBus.on('mapSetView', mapSetView)
    EventBus.on('mapAddMarker', mapAddMarker)
    EventBus.on('mapAddAirMarker', mapAddAirMarker)
@@ -466,6 +481,17 @@
})
onUnmounted(() => {
    // 清理位置监听
    if (watchId) {
        navigator.geolocation.clearWatch(watchId)
        watchId = null
    }
    // 移除位置标记
    if (userLocationMarker) {
        map.removeLayer(userLocationMarker)
        userLocationMarker = null
    }
    EventBus.off('mapSetView', mapSetView)
    EventBus.off('mapAddMarker', mapAddMarker)
    EventBus.off('mapAddAirMarker', mapAddAirMarker)
@@ -606,4 +632,24 @@
        }
    }
}
// 位置标记闪烁效果
.location-blink {
    animation: blink 1s ease-in-out;
}
@keyframes blink {
    0% {
        transform: scale(1);
        opacity: 1;
    }
    50% {
        transform: scale(1.2);
        opacity: 0.8;
    }
    100% {
        transform: scale(1);
        opacity: 1;
    }
}
</style>
applications/mobile-web-view/src/appDataSource/leafletMapIcon/search-normal.svg
New file
@@ -0,0 +1,14 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="search-normal">
<g id="search-normal_2">
<g id="search-normal_3">
<g id="vuesax/linear/search-normal">
<g id="search-normal_4">
<path id="Vector" d="M7.66659 14C11.1644 14 13.9999 11.1644 13.9999 7.66665C13.9999 4.16884 11.1644 1.33331 7.66659 1.33331C4.16878 1.33331 1.33325 4.16884 1.33325 7.66665C1.33325 11.1644 4.16878 14 7.66659 14Z" stroke="#191919" stroke-linecap="round" stroke-linejoin="round"/>
<path id="Vector_2" d="M14.6666 14.6666L13.3333 13.3333" stroke="#191919" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</g>
</g>
</g>
</g>
</svg>
applications/mobile-web-view/src/appPages/AirMap/index.vue
@@ -10,7 +10,7 @@
</template>
<script setup>
import LeafletMap from '@/appComponents/LeafletMap/index.vue'
import LeafletMap from '@/appComponents/LeafletMap/indexWX.vue'
import SearchBar from './searchBar.vue'
import { useRoute } from 'vue-router'
const route = useRoute()
applications/mobile-web-view/src/appPages/AirMap/searchBar.vue
@@ -32,10 +32,13 @@
        <van-list v-else :finished="finished" :finished-text="finishedText">
            <div class="list-item" v-for="item in list" :key="item" @click="flyToAddress(item)">
                <div class="name">{{ item.airspaceName }}</div>
                <div class="name">{{ item.taskName }}</div>
                <div class="item">
                    <div class="laber">空域管制时间</div>
                    <div class="value">{{item.createTime}}</div>
                    <div class="value">
                        <div>{{item.plannedStartTime}}</div>
                        <div>{{item.plannedEndTime}}</div>
                    </div>
                </div>
                <div class="item">
                    <div class="laber">空域管制类型</div>
@@ -51,7 +54,7 @@
import { showToast } from 'vant'
import _ from 'lodash'
import { searchWithPlugin } from '@/utils/util'
import { getAirspaceDetail } from '@/api/map/address'
import { droneFlightTaskApi } from '@/api/map/address'
import { useStore } from 'vuex'
import EventBus from '@/utils/eventBus'
import { gcj02ToWgs84 } from '@/utils/coordinateTransformation'
@@ -89,16 +92,21 @@
// 使用新接口搜索地址
const searchWithNewAPI = () => {
    // 获取微信小程序带过来的头部参数
    console.log(route.query, '9999')
    // 构建请求参数,搜索框为空时不传递airspaceName
    const params = {
        current: 1,
        size: 9999
        size: 10,
        taskName: route.query.taskName || '',
        status: '',
        // id: route.query.id || '',
    }
    if (searchVal.value.trim() !== '') {
        params.airspaceName = searchVal.value
        params.taskName = searchVal.value
    }
    
    getAirspaceDetail(params).then(res => {
    droneFlightTaskApi(params).then(res => {
        const data = res.data.data.records || []
        if (data && data.length > 0) {
            list.value = data
uniapps/work-app/src/App.vue
@@ -1,19 +1,32 @@
<script setup>
import { onHide, onLaunch, onShow } from "@dcloudio/uni-app";
import { useAppStore, useUserStore } from "@/store";
import { useAppStore, useUserStore, useLocationStore } from "@/store";
import { useGlobalWS } from "@/hooks/useGlobalWS.js";
const appStore = useAppStore();
const userStore = useUserStore();
const locationStore = useLocationStore();
useGlobalWS();
onShow(() => {
onShow(async () => {
  console.log("App Show");
  // 应用从后台返回前台时,重新初始化位置服务
  if (userStore.userInfo && locationStore) {
    try {
      await locationStore.initLocationService();
    } catch (error) {
      console.error('重新初始化位置服务失败:', error);
    }
  }
});
onHide(() => {
  console.log("App Hide");
  // 应用进入后台时,停止位置监听以节省资源
  if (locationStore) {
    locationStore.stopLocationWatch();
  }
});
onLaunch(() => {
uniapps/work-app/src/hooks/useGlobalWS.js
@@ -5,6 +5,7 @@
export function useGlobalWS() {
    const userStore = useUserStore();
  const appStore = useAppStore();
  const callStatus = ref(null)
  // 设置默认用户ID为3
  const defaultUserId = '3';
@@ -16,10 +17,9 @@
    console.log('🌐 全局WebSocket收到消息111111111111111111111:', payload)
    // 先尝试直接处理消息(适用于mobile-web-view的voiceCallDetail页面的消息格式)
    const t = (payload.type || '').toString()
    const bizCode = payload.biz_code || ''
    console.log('📋 消息分析:type=' + t + ', biz_code=' + bizCode)
    // const bizCode = payload.biz_code || ''
    callStatus.value = t
    // console.log('📋 消息分析:type=' + t )
    // 处理语音通话请求
    if (t === 'call') {
      console.log('📞 全局收到来电 call,来自', payload.from)
@@ -36,15 +36,8 @@
      try {
        // 优先使用uni-app的导航API(适用于同应用内跳转)
        if (typeof uni !== 'undefined' && uni.navigateTo) {
          console.log('使用uni-app导航API跳转到语音通话页面');
          uni.navigateTo({
            url: `/subPackages/voiceCallDetail/index?voiceparams=${encodedParams}`,
            success: () => {
              console.log('✅ 应用内导航成功!');
            },
            fail: (err) => {
              console.error('❌ 应用内导航失败:', err);
            }
          });
        } else {
          console.error('无法跳转到语音通话页面:当前环境不支持导航');
@@ -56,64 +49,63 @@
    }
    // 然后处理biz_code格式的消息
    switch (bizCode) {
      case 'JOB_ISREFRESH':
        appStore.setJobUpdateKeyAdd()
        break
      case 'DEVICE_ISREFRESH':
        appStore.setDeviceUpdateKeyAdd()
        break
      case 'DOWNLOAD_PROGRESS':
        break
      case 'LOGOUT_USER':
        userStore.setUserInfo(null)
        uni.reLaunch({
          url: '/pages/login/index'
        })
        break
      case 'VoiceCall':
        // enterRoom(payload, userId.value)
        break
      default:
        // 记录未处理的消息
        console.log('未处理的WebSocket消息:', payload)
        break;
    }
    // switch (bizCode) {
    //   case 'JOB_ISREFRESH':
    //     appStore.setJobUpdateKeyAdd()
    //     break
    //   case 'DEVICE_ISREFRESH':
    //     appStore.setDeviceUpdateKeyAdd()
    //     break
    //   case 'DOWNLOAD_PROGRESS':
    //     break
    //   case 'LOGOUT_USER':
    //     userStore.setUserInfo(null)
    //     uni.reLaunch({
    //       url: '/pages/login/index'
    //     })
    //     break
    //   case 'VoiceCall':
    //     // enterRoom(payload, userId.value)
    //     break
    //   default:
    //     // 记录未处理的消息
    //     console.log('未处理的WebSocket消息:', payload)
    //     break;
    // }
  }
  // 初始化WebSocket连接
  function initWS() {
    try {
      // 检查是否已经有活跃的WebSocket连接
      if (!websocketService.getConnected()) {
        // 使用默认用户ID初始化WebSocket连接
        websocketService.init(defaultUserId, WS_BASE);
        console.log('🌐 全局WebSocket初始化成功,默认用户ID:', defaultUserId);
      } else {
        console.log('🌐 WebSocket已存在活跃连接,无需重新初始化');
      }
      // 只设置一次全局消息处理回调
      // 检查是否已经设置了回调
      if (typeof websocketService.getOnMessageCallback() !== 'function') {
        // 设置全局消息处理回调
        websocketService.setOnMessageCallback(messageHandler);
        console.log('✅ 全局WebSocket消息处理器已设置');
      websocketService.setOnMessageCallback(messageHandler);
      // 获取当前用户ID,优先使用store中的用户信息
      const userId = userStore.userInfo?.id || defaultUserId;
      // 检查是否已经有活跃的WebSocket连接
      if (!websocketService.getConnected() || websocketService.userId !== userId) {
        // 使用用户ID初始化WebSocket连接
        websocketService.init(userId, WS_BASE);
        // console.log('🌐 全局WebSocket初始化成功,用户ID:', userId);
      } else {
        console.log('✅ WebSocket消息处理器已存在,无需重新设置');
        websocketService.connect();
      }
    } catch (error) {
      console.error('❌ 全局WebSocket初始化失败:', error);
    }
  }
  // 监听用户信息变化,初始化WebSocket
  watch(
    () => userStore.userInfo,
    (newUserInfo) => {
      console.log('👤 用户信息变化,尝试初始化WebSocket');
    () => callStatus.value,
    (newValue) => {
      if (newValue === 'accept') {
        console.log('📞 通话中,跳过WebSocket初始化');
        return;
      }
      initWS();
    },
    { immediate: true }
    { immediate: true, deep: true }
  )
}
uniapps/work-app/src/pages/login/index.vue
@@ -44,7 +44,8 @@
import { getAssetsImage } from "@/utils/index.js";
import md5 from "js-md5";
import { loginByUsername } from "@/api/user/index.js";
import { useUserStore } from "@/store/index.js";
import { useUserStore, useLocationStore } from "@/store/index.js";
import locationUtil from "@/utils/location.js";
import websocketService from "@/utils/websocket.js";
import { HOME_PATH, LOGIN_PATH, removeQueryString } from "@/router";
@@ -56,6 +57,7 @@
const agreed = ref(true);
const userStore = useUserStore();
const locationStore = useLocationStore();
const loginForm = ref({
  username: "",
  password: "",
@@ -135,8 +137,22 @@
      userInfo.key,
      userInfo.code
    );
    userStore.setUserInfo(res.data.data);
    console.log('登录成功,WebSocket连接将由全局钩子统一管理');
    userStore.setUserInfo(res.data.data);
    // 请求位置权限并初始化位置服务
    try {
      // 请求位置权限
      const hasPermission = await locationUtil.requestLocationPermission();
      if (hasPermission) {
        console.log('获取位置权限成功');
        // 初始化位置服务
        await locationStore.initLocationService();
      } else {
        console.log('获取位置权限失败');
      }
    } catch (error) {
      console.error('处理位置服务失败:', error);
    }
    uni.reLaunch({
      url: "/pages/work/index",
    });
uniapps/work-app/src/store/index.js
@@ -6,6 +6,7 @@
import useAppStore from "./modules/app"
import useUserStore from "./modules/user"
import useMapStore from "./modules/map"
import useLocationStore from "./modules/location"
// 安装pinia状态管理插件
function setupStore (app) {
@@ -23,5 +24,5 @@
}
// 导出模块
export { useAppStore, useUserStore, useMapStore }
export { useAppStore, useUserStore, useMapStore, useLocationStore }
export default setupStore
uniapps/work-app/src/store/modules/location.js
New file
@@ -0,0 +1,137 @@
import { defineStore } from 'pinia'
import locationUtil from '@/utils/location.js'
const useLocationStore = defineStore('location', {
  state: () => ({
    // 当前位置信息
    currentLocation: null,
    // 位置权限状态
    hasPermission: false,
    // 系统定位服务是否开启
    systemLocationEnabled: true,
    // 位置更新间隔(毫秒)
    updateInterval: 5000,
    // 位置监听器ID
    watcherId: null
  }),
  getters: {
    /**
     * 获取当前位置
     * @returns {Object|null} 当前位置信息
     */
    getCurrentLocation: (state) => state.currentLocation,
    /**
     * 获取位置权限状态
     * @returns {boolean} 是否有位置权限
     */
    getPermissionStatus: (state) => state.hasPermission,
    /**
     * 获取系统定位服务状态
     * @returns {boolean} 系统定位服务是否开启
     */
    getSystemLocationStatus: (state) => state.systemLocationEnabled
  },
  actions: {
    /**
     * 初始化位置服务
     */
    async initLocationService() {
      try {
        // 检查系统定位服务是否开启
        this.systemLocationEnabled = await locationUtil.checkSystemLocationEnabled();
        if (!this.systemLocationEnabled) {
          console.warn('系统定位服务未开启');
        }
        // 请求位置权限
        this.hasPermission = await locationUtil.requestLocationPermission();
        if (this.hasPermission) {
          // 获取当前位置
          await this.updateCurrentLocation();
          // 开始位置监听
          this.startLocationWatch();
        }
      } catch (error) {
        console.error('初始化位置服务失败:', error);
      }
    },
    /**
     * 更新当前位置
     */
    async updateCurrentLocation() {
      try {
        const location = await locationUtil.getCurrentLocation();
        this.currentLocation = location;
        console.log('位置更新:', location);
        return location;
      } catch (error) {
        console.error('更新位置失败:', error);
        return null;
      }
    },
    /**
     * 开始位置监听
     */
    startLocationWatch() {
      if (this.watcherId) {
        this.stopLocationWatch();
      }
      this.watcherId = locationUtil.startLocationWatcher((location) => {
        this.currentLocation = location;
        // 可以在这里添加位置信息上报逻辑
        this.reportLocation(location);
      }, {
        interval: this.updateInterval
      });
    },
    /**
     * 停止位置监听
     */
    stopLocationWatch() {
      if (this.watcherId) {
        locationUtil.stopLocationWatcher();
        this.watcherId = null;
      }
    },
    /**
     * 重新请求位置权限
     */
    async reRequestPermission() {
      this.hasPermission = await locationUtil.requestLocationPermission();
      if (this.hasPermission) {
        await this.updateCurrentLocation();
        this.startLocationWatch();
      }
      return this.hasPermission;
    },
    /**
     * 位置信息上报(可根据实际需求实现)
     * @param {Object} location 位置信息
     */
    reportLocation(location) {
      // 这里可以添加位置信息上报到服务器的逻辑
      // 例如:api.uploadLocation(location)
      // console.log('位置上报:', location);
    },
    /**
     * 清理位置服务
     */
    cleanupLocationService() {
      this.stopLocationWatch();
      this.currentLocation = null;
    }
  }
})
export default useLocationStore
uniapps/work-app/src/utils/location.js
New file
@@ -0,0 +1,260 @@
/**
 * 位置管理工具类
 * 用于处理位置权限请求和实时位置获取
 */
import permission from './voiceCallByTX/TrtcCloud/permission.js'
// 平台判断
const isIos = () => {
  // #ifdef APP-PLUS
  return plus.os.name === "iOS";
  // #endif
  return false;
};
// 位置更新回调函数
let locationUpdateCallback = null;
// 位置监听器ID
let locationWatcherId = null;
/**
 * 请求位置权限
 * @returns {Promise<boolean>} 是否获取到权限
 */
export const requestLocationPermission = async () => {
  return new Promise(async (resolve) => {
    // #ifdef APP-PLUS
    if (isIos()) {
      // iOS平台
      const hasPermission = permission.judgeIosPermission('location');
      if (hasPermission) {
        resolve(true);
      } else {
        // iOS需要提示用户手动开启权限
        uni.showModal({
          title: '提示',
          content: '应用需要获取位置信息权限,请在设置中开启',
          confirmText: '去设置',
          cancelText: '取消',
          success: (res) => {
            if (res.confirm) {
              permission.gotoAppPermissionSetting();
              resolve(false);
            } else {
              resolve(false);
            }
          }
        });
      }
    } else {
      // Android平台
      const result = await permission.requestAndroidPermission('android.permission.ACCESS_FINE_LOCATION');
      if (result === 1) {
        resolve(true);
      } else if (result === 0) {
        // 拒绝本次申请
        resolve(false);
      } else if (result === -1) {
        // 永久拒绝
        uni.showModal({
          title: '提示',
          content: '应用需要获取位置信息权限,请在设置中开启',
          confirmText: '去设置',
          cancelText: '取消',
          success: (res) => {
            if (res.confirm) {
              permission.gotoAppPermissionSetting();
              resolve(false);
            } else {
              resolve(false);
            }
          }
        });
      }
    }
    // #endif
    // #ifdef H5
    // H5平台使用浏览器原生API请求权限
    if (navigator.geolocation) {
      navigator.permissions.query({ name: 'geolocation' }).then((result) => {
        if (result.state === 'granted') {
          resolve(true);
        } else if (result.state === 'prompt') {
          // 还未请求过权限,会在getLocation时提示
          resolve(true);
        } else {
          resolve(false);
        }
      }).catch(() => {
        resolve(false);
      });
    } else {
      resolve(false);
    }
    // #endif
    // #ifdef MP
    // 小程序平台
    uni.getSetting({
      success: (res) => {
        if (res.authSetting['scope.userLocation']) {
          resolve(true);
        } else {
          uni.authorize({
            scope: 'scope.userLocation',
            success: () => {
              resolve(true);
            },
            fail: () => {
              uni.showModal({
                title: '提示',
                content: '应用需要获取位置信息权限,请在设置中开启',
                confirmText: '去设置',
                cancelText: '取消',
                success: (res) => {
                  if (res.confirm) {
                    uni.openSetting();
                    resolve(false);
                  } else {
                    resolve(false);
                  }
                }
              });
            }
          });
        }
      }
    });
    // #endif
  });
};
/**
 * 获取当前位置信息
 * @returns {Promise<Object>} 位置信息对象
 */
export const getCurrentLocation = () => {
  return new Promise((resolve, reject) => {
    uni.getLocation({
      type: 'gcj02',
      altitude: true,
      success: (res) => {
        resolve({
          latitude: res.latitude,
          longitude: res.longitude,
          altitude: res.altitude,
          accuracy: res.accuracy,
          speed: res.speed,
          verticalAccuracy: res.verticalAccuracy,
          horizontalAccuracy: res.horizontalAccuracy
        });
      },
      fail: (err) => {
        console.error('获取位置失败:', err);
        reject(err);
      }
    });
  });
};
/**
 * 开始实时位置监听
 * @param {Function} callback 位置更新回调函数
 * @param {Object} options 监听选项
 * @returns {number} 监听器ID
 */
export const startLocationWatcher = (callback, options = {}) => {
  if (locationWatcherId) {
    stopLocationWatcher();
  }
  locationUpdateCallback = callback;
  locationWatcherId = uni.startLocationUpdate({
    success: () => {
      console.log('开始位置监听成功');
    },
    fail: (err) => {
      console.error('开始位置监听失败:', err);
      // 如果是因为权限问题,重新请求权限
      if (err.errCode === 12002) {
        requestLocationPermission();
      }
    }
  });
  // 监听位置更新
  uni.onLocationChange((res) => {
    const locationInfo = {
      latitude: res.latitude,
      longitude: res.longitude,
      altitude: res.altitude,
      accuracy: res.accuracy,
      speed: res.speed,
      verticalAccuracy: res.verticalAccuracy,
      horizontalAccuracy: res.horizontalAccuracy,
      timestamp: res.timestamp
    };
    if (locationUpdateCallback) {
      locationUpdateCallback(locationInfo);
    }
  });
  return locationWatcherId;
};
/**
 * 停止位置监听
 */
export const stopLocationWatcher = () => {
  if (locationWatcherId) {
    uni.stopLocationUpdate({
      success: () => {
        console.log('停止位置监听成功');
      },
      fail: (err) => {
        console.error('停止位置监听失败:', err);
      }
    });
    locationWatcherId = null;
  }
  // 移除位置更新监听
  uni.offLocationChange();
  locationUpdateCallback = null;
};
/**
 * 检查系统定位服务是否开启
 * @returns {Promise<boolean>} 是否开启
 */
export const checkSystemLocationEnabled = async () => {
  return new Promise((resolve) => {
    // #ifdef APP-PLUS
    const enabled = permission.checkSystemEnableLocation();
    resolve(enabled);
    // #endif
    // #ifdef H5
    resolve(navigator.geolocation ? true : false);
    // #endif
    // #ifdef MP
    uni.getSystemInfo({
      success: (res) => {
        // 小程序无法直接检查系统定位服务是否开启
        resolve(true);
      },
      fail: () => {
        resolve(false);
      }
    });
    // #endif
  });
};
export default {
  requestLocationPermission,
  getCurrentLocation,
  startLocationWatcher,
  stopLocationWatcher,
  checkSystemLocationEnabled
};
uniapps/work-app/src/utils/websocket.js
@@ -1,4 +1,4 @@
// WebSocket服务管理
import { useGlobalWS } from "@/hooks/useGlobalWS.js";// WebSocket服务管理
class WebSocketService {
  constructor() {
    this.socketTask = null
@@ -34,10 +34,10 @@
      this.socketTask = uni.connectSocket({
        url: this.url,
        success: () => {
          console.log('WebSocket连接已请求')
          // console.log('WebSocket连接已请求')
        },
        fail: (error) => {
          console.error('WebSocket连接请求失败:', error)
          // console.error('WebSocket连接请求失败:', error)
          if (this.onErrorCallback) {
            this.onErrorCallback(error)
          }
@@ -47,7 +47,7 @@
      // 监听连接打开
      this.socketTask.onOpen(() => {
        this.connected = true
        console.log('WebSocket已连接,userId:', this.userId)
        // console.log('WebSocket已连接,userId:', this.userId)
        this.startPing()
        if (this.onOpenCallback) {
          this.onOpenCallback()
@@ -57,7 +57,7 @@
      // 监听连接关闭
      this.socketTask.onClose(() => {
        this.connected = false
        console.log('WebSocket已关闭')
        // console.log('WebSocket已关闭')
        this.stopPing()
        if (this.onCloseCallback) {
          this.onCloseCallback()
@@ -66,7 +66,7 @@
      // 监听连接错误
      this.socketTask.onError((error) => {
        console.error('WebSocket错误:', error)
        // console.error('WebSocket错误:', error)
        this.connected = false
        this.stopPing()
        if (this.onErrorCallback) {
@@ -78,16 +78,16 @@
      this.socketTask.onMessage((res) => {
        try {
          const message = JSON.parse(res.data)
          console.log('收到WebSocket消息:', message)
          // console.log('收到WebSocket消息:', message)
          if (this.onMessageCallback) {
            this.onMessageCallback(message)
          }
        } catch (error) {
          console.error('解析WebSocket消息失败:', error)
          // console.error('解析WebSocket消息失败:', error)
        }
      })
    } catch (error) {
      console.error('WebSocket连接失败:', error)
      // console.error('WebSocket连接失败:', error)
      if (this.onErrorCallback) {
        this.onErrorCallback(error)
      }
@@ -112,10 +112,11 @@
    this.socketTask.send({
      data: JSON.stringify(message),
      success: () => {
        console.log('WebSocket消息发送成功:', message)
        // console.log('WebSocket消息发送成功:', message)
        useGlobalWS();
      },
      fail: (error) => {
        console.error('WebSocket消息发送失败:', error)
        // console.error('WebSocket消息发送失败:', error)
      }
    })
  }
@@ -146,10 +147,10 @@
        code: 1000,
        reason: '正常关闭',
        success: () => {
          console.log('WebSocket已关闭')
          // console.log('WebSocket已关闭')
        },
        fail: (error) => {
          console.error('WebSocket关闭失败:', error)
          // console.error('WebSocket关闭失败:', error)
        }
      })
      this.socketTask = null
@@ -160,10 +161,9 @@
  // 设置消息回调
  setOnMessageCallback(callback) {
    console.log('🔔 WebSocket消息回调已设置')
    this.onMessageCallback = callback
  }
  // 获取当前消息回调(用于检查是否已设置)
  getOnMessageCallback() {
    return this.onMessageCallback
uniapps/work-wx/src/api/index.js
@@ -19,6 +19,15 @@
  })
}
// 我的空域查询
export const droneFlightTaskApi = data => {
  return request({
    url: `/webservice/droneFlightTask/pageInfoOfMy`,
    method: 'post',
    data,
  })
}
// 设备列表
export const aircraftInfoPageInfoOfMyApi = data => {
  return request({
@@ -28,14 +37,14 @@
  })
}
// 我的设备
export const myDevicePageInfoApi = params => {
  return request({
    url: `/webservice/aircraftInfo/queryById/${params.id}`,
    method: 'post',
    params,
  })
}
// // 我的设备
// export const myDevicePageInfoApi = params => {
//   return request({
//     url: `/webservice/aircraftInfo/queryById/${params.id}`,
//     method: 'post',
//     params,
//   })
// }
// 飞行申请列表
export const flightPlanPageInfoApi = data => {
@@ -45,6 +54,13 @@
    data,
  })
}
// 飞行申请详情
export const flightPlanDetailsApi = id => {
  return request({
    url: `/webservice/flightPlan/queryById/${id}`,
    method: 'get'
  })
}
// 航空器注册
export const aircraftInfoSaveApi = data => {
@@ -52,6 +68,13 @@
    url: `/webservice/aircraftInfo/save`,
    method: 'post',
    data,
  })
}
// 注册器详情
export const aircraftInfoDetailsApi = id => {
  return request({
    url: `/webservice/aircraftInfo/queryById/${id}`,
    method: 'get'
  })
}
@@ -107,3 +130,49 @@
    method: 'get'
  })
}
// 下拉接口数据-飞行类型
export const proTypeApi = () => {
  return request({
    url: `/system/dict/data/type/pro_type`,
    method: 'get'
  })
}
// 申报 警务 文旅
export const flightTaskApi = () => {
  return request({
    url: `/system/dict/data/type/flight_task`,
    method: 'get'
  })
}
// 飞行规则
export const flightRulesApi = () => {
  return request({
    url: `/system/dict/data/type/flight_rules`,
    method: 'get'
  })
}
// 飞行规则 视距内飞行
export const flightModeApi = () => {
  return request({
    url: `/system/dict/data/type/flight_mode`,
    method: 'get'
  })
}
// 飞行计划 一般 紧急
export const flightPlanApi = () => {
  return request({
    url: `/system/dict/data/type/flight_plan`,
    method: 'get'
  })
}
// 无人机厂商
export const manufacturerInfoApi = data => {
  return request({
    url: `/webservice/webservice/manufacturerInfo/pageInfo`,
    method: 'post',
    data,
  })
}
uniapps/work-wx/src/pages.json
@@ -85,12 +85,24 @@
                    }
                },
                {
                    "path": "flightApplication/details",
                    "style": {
                        "navigationBarTitleText": "飞行详情"
                    }
                },
                {
                    "path": "deviceRegistration/add",
                    "style": {
                        "navigationBarTitleText": "飞行器注册"
                    }
                },
                {
                    "path": "deviceRegistration/details",
                    "style": {
                        "navigationBarTitleText": "飞行器详情"
                    }
                },
                {
                    "path": "regulationsDetail/details",
                    "style": {
                        "navigationBarTitleText": "详情"
uniapps/work-wx/src/pages/equipmentManagement/index.vue
@@ -4,7 +4,7 @@
    <view class="content">
      <view class="title">飞行设备</view>
      <u-list :style="{ height: 'calc(100vh - 160rpx)' }" @scrolltolower="scrolltolower" :lower-threshold="50">
        <view class="card" v-for="(item, index) in equipmentList" :key="index">
        <view class="card" v-for="(item, index) in equipmentList" :key="index" @click="handleClick(item)">
          <view class="left">
            <u-image :src="droneSvg" mode="aspectFill" :width="70" :height="90"></u-image>
          </view>
@@ -24,11 +24,11 @@
</template>
<script setup>
import { reactive, ref } from 'vue'
import { onHide, onShow, onLoad } from "@dcloudio/uni-app";
import lbPng from '@/static/images/lb.png'
import addSvg from '@/static/images/add.png'
import droneSvg from '@/static/images/drone.png'
import { myDevicePageInfoApi, aircraftInfoPageInfoOfMyApi } from '@/api/index.js'
import { aircraftInfoPageInfoOfMyApi } from '@/api/index.js'
const equipmentList = ref([])
const noticeTitle = ref('')
const formParams = ref({
@@ -60,23 +60,20 @@
    console.error('获取设备列表失败:', error)
  }
}
// 我的设备详情
const getMyDeviceDetail = async (id) => {
  try {
    const res = await myDevicePageInfoApi({ id })
    if (res.code === 200) {
      // 处理设备详情数据
    }
  } catch (error) {
    console.error('获取我的设备详情失败:', error)
  }
// 设备详情
const handleClick = (item) => {
  uni.navigateTo({
    url: '/subPackages/deviceRegistration/details?id=' + item.id,
  })
}
// 新增设备
const addEquipment = () => {
  uni.navigateTo({
    url: '/subPackages/deviceRegistration/add',
  })
}
function scrolltolower() {
  formParams.value.current++
  // 停止不刷新
@@ -85,7 +82,12 @@
  }
  getEquipmentList()
}
onMounted(() => {
// onMounted(() => {
//   getEquipmentList()
// })
onShow(() => {
  // 每次页面显示时重置为第一页并重新加载数据
  formParams.value.current = 1
  getEquipmentList()
})
</script>
uniapps/work-wx/src/pages/page/index.vue
@@ -76,7 +76,7 @@
  import flfgPng from '@/static/images/flfg.png'
  import zczdPng from '@/static/images/zczd.png'
  import docSvg from '@/static/images/doc.png'
  import { sysNoticePageInfoApi, flightAirspacePageInfoApi } from '@/api/index'
  import { sysNoticePageInfoApi, droneFlightTaskApi } from '@/api/index'
const lbList = reactive([]);
// lbPng 轮播图 5次
for (let i = 0; i < 5; i++) {
@@ -134,13 +134,13 @@
// 获取空域信息列表
async function getFlightAirspaceList() {
  try {
    const res = await flightAirspacePageInfoApi({
    const res = await droneFlightTaskApi({
      current: 1,
      size: 10,
    })
    announcementList.value = res.data.data.records.slice(0, 3).map(item => ({
      ...item,
      title: item.airspaceName,
      title: item.taskName,
      description: item.remark,
      time: item.createTime,
      num: item.num,
@@ -196,7 +196,7 @@
  // 跳转到空域信息详情页
  function airspaceClick(item) {
    uni.navigateTo({
      url: '/subPackages/regulationsDetail/details?id=' + item.id,
      url: '/subPackages/airspaceInformation/index?taskName=' + item.taskName,
    })
  }
  // 跳转到法律政策详情页
uniapps/work-wx/src/subPackages/airspaceInformation/index.vue
@@ -17,7 +17,7 @@
<script setup>
import { getWebViewUrl } from "@/utils/index.js";
import WebViewPlus from "@/components/WebViewPlus.vue";
import { onHide, onShow } from "@dcloudio/uni-app";
import { onHide, onShow, onLoad } from "@dcloudio/uni-app";
// const viewUrl = getWebViewUrl("/defaultMap");
let envParam = "";
@@ -29,55 +29,25 @@
// #endif
const updateKey = ref(0);
const viewUrl = computed(() => {
  return getWebViewUrl("/airMap", {
    isWeb: envParam,
    updateKey: updateKey.value,
  });
});
const viewUrl = ref('')
// computed(() => {
//   return getWebViewUrl("/airMap", {
//     isWeb: envParam,
//     updateKey: updateKey.value,
//   });
// });
const onPostMessage = (data) => {
  // #ifdef MP-WEIXIN
  if (data.type === "scanCode") {
  } else if (data.type === "jumpAddWork") {
  } else if (data.type === "jumpMapNav") {
    //事件导航
  } else if (data.type === "workid") {
    //事件详情
  }
  // #endif
  // #ifndef MP-WEIXIN
  if (data.type === "scanCode") {
    uni.navigateTo({
      url: "/subPackages/qrCode/index",
    });
  } else if (data.type === "jumpAddWork") {
    //新建任务
    const encodedData = encodeURIComponent(JSON.stringify(data.rowItem));
    uni.setStorageSync("webview_params", encodedData);
    uni.navigateTo({
      url: `/subPackages/taskDetail/addTask/index`,
    });
  } else if (data.type === "jumpMapNav") {
    //事件导航
    uni.navigateTo({
      url: `/subPackages/workDetail/mapWork/index?currentItem=${data.eventNum}`,
    });
  } else if (data.type === "workid") {
    //事件详情
    uni.navigateTo({
      url: `/subPackages/workDetail/index?eventNum=${data.eventNum}`,
    });
  }
  // #endif
  console.log('4444', data)
};
onShow(() => {
  // #ifndef MP-WEIXIN
onShow(() => {});
  // #endif
});
onLoad((queryParams) => {
  viewUrl.value = getWebViewUrl("/airMap", {
    taskName: queryParams.taskName || '',
  });
})
onHide(() => {
  updateKey.value = updateKey.value + 1;
uniapps/work-wx/src/subPackages/deviceRegistration/add.vue
@@ -100,12 +100,12 @@
         <u-form-item
                label="持有人"
                labelWidth="200rpx"
                prop="aircraftName"
                prop="holder"
                :borderBottom="true"
                ref="item1"
        >
            <u-input
                    v-model="formParams.aircraftName"
                    v-model="formParams.holder"
                    border="none"
                    placeholder="请输入"
            ></u-input>
@@ -273,14 +273,14 @@
            @cancel="isShowRegion = false"
        ></u-cascader>
    </u-form>
    <u-button @click="submitForm" color="#1D6FE9">提交</u-button>
    <u-button :disabled="isSubmitDisabled" @click="submitForm" color="#1D6FE9">提交</u-button>
    <!-- <u-button class="submit-button-wrapper" color="#1D6FE9" size="large" @click="submitForm">
            注册
        </u-button> -->
</view>
</template>
<script setup>
import { aircraftInfoSaveApi,uploadFileApi,areaDataApi } from '@/api/index'
import { aircraftInfoSaveApi,uploadFileApi, areaDataApi, proTypeApi, manufacturerInfoApi } from '@/api/index'
import { ref, computed, onMounted } from 'vue'
import dayjs from 'dayjs'
@@ -378,12 +378,12 @@
// 所属区域确认
const onRegionConfirm = (e) => {
    console.log(e,'888')
    formParams.value.region = e?.join(',')[2] || '' // 用逗号分隔的区域码
    formParams.value.region = e[2] || '' // 用逗号分隔的区域码
    // 通过arrRegion的数组值为[36,3608,360824] 从areaData里面反查出名称
    const regionNames = findRegionNamesByCodes(e, areaData.value)
    console.log(regionNames, '77777')
    formParams.value.regionText = regionNames.join('-') || '' // 用连字符连接的区域名称
    isShowRegion.value = false
    isShowRegion.value = false
}
// 是否有保险
const actionsHasInsurance = ref([
@@ -405,16 +405,7 @@
}
// 飞行器厂商
const showManufacturer = ref(false);
const manufacturerList = ref([
    {
        label: 'A厂商',
        value: 'A厂商'
    },
    {
        label: 'B厂商',
        value: 'B厂商'
    },
])
const manufacturerList = ref([])
// 选择飞行器厂商
const onPickerConfirmManufacturer = (e) => {
  const selected = e.value[0]
@@ -424,16 +415,7 @@
}
// 飞行器型号
const showType = ref(false);
const typeList = ref([
    {
        label: 'A型号',
        value: 'A型号'
    },
    {
        label: 'B型号',
        value: 'B型号'
    },
])
const typeList = ref([])
// 选择飞行器型号
const onPickerConfirmType = (e) => {
  const selected = e.value[0]
@@ -560,11 +542,14 @@
    });
}
// 提交表单
const isSubmitDisabled = ref(false)
const submitForm = async () => {
    try {
        if (await formRef.value.validate()) {
            isSubmitDisabled.value = true
            const res = await aircraftInfoSaveApi(formParams.value)
            if (res.data.code === 200) {
                isSubmitDisabled.value = false
                uni.showToast({
                    title: '航空器注册成功',
                    icon: 'success'
@@ -586,29 +571,35 @@
        // 这里可以添加额外的失败处理逻辑
    }
}
// 获取飞行器类型
function getProTypeApi() {
    proTypeApi().then(res => {
        typeList.value = res.data.data.map(item => ({
            ...item,
            label: item.dictLabel,
            value: item.dictValue
        }))
    })
}
// 获取无人机厂商
function getManufacturerInfoApi() {
    manufacturerInfoApi({
        current: 1,
        size: 1000,
    }).then(res => {
        manufacturerList.value = res.data.data.records.map(item => ({
            ...item,
            label: item.unitName,
            value: item.id,
        }))
    })
}
onMounted(async () => {
    getProTypeApi()
    getManufacturerInfoApi()
    // 动态导入xzqhData,确保在使用前已初始化
    const { xzqhData } = await import('@/static/xzqh/index')
    // console.log(xzqhData,'9999')
    // xzqhData.map 循环三层
    // 第一层:省份
    // 第二层:城市
    // 第三层:区县
    areaData.value = xzqhData
    // xzqhData.map(item => ({
    //     label: item.name,
    //     value: item.code,
    //     children: item.children.map(child => ({
    //         label: child.name,
    //         value: child.code,
    //         children: child.children?.map(district => ({
    //             label: district.name,
    //             value: district.code
    //         })) || []
    //     }))
    // }))
    // console.log(areaData.value, '8888')
})
</script>
<style scoped lang="scss">
uniapps/work-wx/src/subPackages/deviceRegistration/details.vue
New file
@@ -0,0 +1,443 @@
<!-- 飞行器详情 -->
<template>
<view class="deviceRegistration">
    <u-form labelPosition="top" :model="formParams" :rules="rules" ref="formRef">
        <u-form-item
                label="飞行器名称"
                labelWidth="200rpx"
                prop="name"
                :borderBottom="true"
                ref="item1"
        >
            <u-input v-model="formParams.name" border="none" placeholder="请输入"></u-input>
        </u-form-item>
        <u-form-item
                label="SN(出厂序列号)"
                labelWidth="230rpx"
                prop="sn"
                :borderBottom="true"
                ref="item2"
        >
            <u-input v-model="formParams.sn" border="none" placeholder="请输入"></u-input>
        </u-form-item>
        <u-form-item
                label="飞行器厂商"
                labelWidth="230rpx"
                prop="manufacturer"
                :borderBottom="true"
                ref="item2"
        >
            <u-input
                v-model="formParams.manufacturer"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择飞行器厂商"
                border="none"
            ></u-input>
        </u-form-item>
        <u-form-item
                label="飞行器型号"
                labelWidth="200rpx"
                prop="model"
                :borderBottom="true"
                ref="item1"
        >
            <u-input v-model="formParams.model" border="none" placeholder="请输入"></u-input>
        </u-form-item>
        <u-form-item
                label="是否改装"
                labelWidth="200rpx"
                prop="isModified"
                :borderBottom="true"
                ref="item1"
        >
            <u-input
                v-model="formParams.isModified"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择是否改装"
                border="none"
            ></u-input>
        </u-form-item>
        <u-form-item
                label="飞行器类型"
                labelWidth="200rpx"
                prop="type"
                :borderBottom="true"
                ref="item1"
        >
            <u-input
                v-model="formParams.type"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择飞行器类型"
                border="none"
            ></u-input>
        </u-form-item>
         <u-form-item
                label="持有人"
                labelWidth="200rpx"
                prop="holder"
                :borderBottom="true"
                ref="item1"
        >
            <u-input
                    v-model="formParams.holder"
                    border="none"
                    placeholder="请输入"
            ></u-input>
        </u-form-item>
         <u-form-item
                label="购买日期"
                labelWidth="200rpx"
                prop="purchaseDate"
                :borderBottom="true"
                ref="item1"
        >
            <u-input v-model="formParams.purchaseDate" border="none" placeholder="请选择日期" suffixIcon="calendar"></u-input>
        </u-form-item>
        <u-form-item
                label="有无保险"
                labelWidth="230rpx"
                prop="hasInsurance"
                :borderBottom="true"
                ref="item2"
        >
            <u-input
                v-model="formParams.hasInsurance"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择有无保险"
                border="none"
            ></u-input>
        </u-form-item>
        <u-form-item
                label="所属区域"
                labelWidth="230rpx"
                prop="region"
                :borderBottom="true"
                ref="item2"
        >
            <u-input
                v-model="formParams.region"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择所属区域"
                border="none"
            ></u-input>
        </u-form-item>
        <u-form-item
                label="民用航空器登记证书"
                labelWidth="300rpx"
                prop="caacRegistrationCode"
                :borderBottom="true"
                ref="item1"
        >
            <u-input
                    v-model="formParams.caacRegistrationCode"
                    border="none"
                    placeholder="请输入"
            ></u-input>
        </u-form-item>
        <u-form-item
                label="飞行器照片"
                labelWidth="200rpx"
                prop="deviceImage"
                :borderBottom="true"
                ref="item1"
        >
            <u-input
                    v-model="formParams.deviceImage"
                    border="none"
                    placeholder="请输入"
            ></u-input>
        </u-form-item>
        <u-form-item
                label="备注"
                labelWidth="200rpx"
                prop="remarks"
                :borderBottom="true"
                ref="item1"
        >
            <u-input
                    v-model="formParams.remarks"
                    border="none"
                    placeholder="请输入"
            ></u-input>
        </u-form-item>
    </u-form>
</view>
</template>
<script setup>
import { aircraftInfoSaveApi,uploadFileApi, areaDataApi, proTypeApi, manufacturerInfoApi, aircraftInfoDetailsApi } from '@/api/index'
import { ref, computed, onMounted } from 'vue'
import dayjs from 'dayjs'
import { onHide, onShow, onLoad } from "@dcloudio/uni-app";
const formRef = ref(null);
const formParams = ref({
    name: '',
    manufacturer: '', // 制造商
    manufacturerText: '', // 制造商文本
    model: '', // 型号
    sn: '', // sn编号
    type: '', // 航空器类型
    typeText: '', // 航空器类型文本
    holder: '', // 持有人
    pilotId: '', // 飞行员id
    status: '', // 状态 飞行器状态:0-未激活,1-正常,2-维修中,3-报废:
    purchaseDate: '', // 购买日期
    isModified: '', // 是否改装 0否 1是:
    isModifiedText: '', // 是否改装文本
    hasInsurance: '', // 是否有保险
    hasInsuranceText: '', // 是否有保险文本
    region: '', // 所属区域
    regionCode: '', // 所属区域
    regionText: '', // 所属区域文本
    caacRegistrationCode: '', // 民用航空器登记证书
    caacRegistrationCodeText: '', // 民用航空器登记证书文本
    deviceImage: '', // 飞行器照片
    deviceImageText: '', // 飞行器照片文本
    remarks: '', // 备注
})
const rules = ref({})
// 是否改装
const actionsModified = ref([
    {
        label: '否',
        value: 0
    },
    {
        label: '是',
        value: 1
    }
])
const showIsModified = ref(false);
const onPickerConfirm = (e) => {
  const selected = e.value[0]
  formParams.value.isModified = selected.value
  formParams.value.isModifiedText = selected.label
  showIsModified.value = false
}
const showPurchaseDate = ref(false);
const birthdayValue = ref(Date.now()); // 设置默认值为当前时间戳
// 购买日期
const onPurchaseDate = (e) => {
    formParams.value.purchaseDate = dayjs(e.value).format('YYYY-MM-DD')
    showPurchaseDate.value = false
}
const findRegionNamesByCodes = (codes, areaData) => {
  const result = []
  let currentData = areaData
  for (const code of codes) {
    // 转换为字符串比较(因为code可能是字符串类型)
    const codeStr = code.toString()
    const item = currentData.find(item => item.code === codeStr)
    if (!item) {
      console.warn(`未找到code为 ${codeStr} 的行政区划`)
      break
    }
    result.push(item.name)
    currentData = item.children || []
  }
  return result
}
// 所属区域确认
const onRegionConfirm = (e) => {
    console.log(e,'888')
    formParams.value.region = e?.join(',')[2] || '' // 用逗号分隔的区域码
    // 通过arrRegion的数组值为[36,3608,360824] 从areaData里面反查出名称
    const regionNames = findRegionNamesByCodes(e, areaData.value)
    console.log(regionNames, '77777')
    formParams.value.regionText = regionNames.join('-') || '' // 用连字符连接的区域名称
    isShowRegion.value = false
}
// 是否有保险
const actionsHasInsurance = ref([
    {
        label: '无',
        value: '0'
    },
    {
        label: '有',
        value: '1'
    }
])
const showHasInsurance = ref(false);
const onPickerConfirmHasInsurance = (e) => {
  const selected = e.value[0]
  formParams.value.hasInsurance = selected.value
  formParams.value.hasInsuranceText = selected.label
  showHasInsurance.value = false
}
// 飞行器厂商
const showManufacturer = ref(false);
const manufacturerList = ref([])
// 选择飞行器厂商
const onPickerConfirmManufacturer = (e) => {
  const selected = e.value[0]
  formParams.value.manufacturer = selected.value
  formParams.value.manufacturerText = selected.label
  showManufacturer.value = false
}
// 飞行器型号
const showType = ref(false);
const typeList = ref([])
// 选择飞行器型号
const onPickerConfirmType = (e) => {
  const selected = e.value[0]
  formParams.value.type = selected.value
  formParams.value.typeText = selected.label
  showType.value = false
}
// 获取所属单位
const isShowRegion = ref(false)
const areaData = ref([])
// function getAreaData() {
//     areaDataApi().then(res => {
//         if (res.data.code === 200) {
//             areaData.value = res.data.data
//         }
//     })
// }
// 获取飞行器类型
function getProTypeApi() {
    proTypeApi().then(res => {
        typeList.value = res.data.data.map(item => ({
            ...item,
            label: item.dictLabel,
            value: item.dictValue
        }))
    })
}
// 获取无人机厂商
function getManufacturerInfoApi() {
    manufacturerInfoApi({
        current: 1,
        size: 1000,
    }).then(res => {
        manufacturerList.value = res.data.data.records.map(item => ({
            ...item,
            label: item.unitName,
            value: item.id,
        }))
    })
}
onLoad((options) => {
    aircraftInfoDetailsApi(options.id).then(res => {
        formParams.value = res.data.data
        formParams.value.isModified = res.data.data.isModified ? '是' : '否'
        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
    })
})
onMounted(async () => {
    getProTypeApi()
    getManufacturerInfoApi()
    // 动态导入xzqhData,确保在使用前已初始化
    const { xzqhData } = await import('@/static/xzqh/index')
    areaData.value = xzqhData
})
</script>
<style scoped lang="scss">
.deviceRegistration {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    // padding: 24rpx;
    .select-wrapper {
        position: relative;
        width: 100%;
        .select-icon {
            position: absolute;
            right: 20rpx;
            top: 50%;
            transform: translateY(-50%);
            color: #999;
            font-size: 24rpx;
            pointer-events: none;
        }
    }
    .upload {
        font-family: Source Han Sans CN, Source Han Sans CN;
        font-weight: 400;
        font-size: 15px;
        color: #1D6FE9;
    }
    :deep(.u-form) {
        flex: 1;
        display: flex;
        flex-direction: column;
        overflow-y: auto;
        box-sizing: border-box;
        padding: 24rpx;
    }
    :deep(.u-button) {
        width: 80%;
        padding: 28rpx;
        border-radius: 8rpx 8rpx 8rpx 8rpx;
        margin-bottom: 20rpx;
    }
    :deep(.u-form-item) {
        margin-bottom: 40rpx;
        padding: 16rpx 24rpx 18rpx 24rpx;
        border-radius: 6px 6px 6px 6px;
        background: white;
        :deep(.u-line) {
            border-bottom: 1px solid #EFEFEF;
        }
        :deep(.u-input) {
            width: 100%;
            height: 80rpx;
            border-radius: 8rpx;
            padding: 0 20rpx;
            background-color: #f5f5f5;
            &::placeholder {
                font-family: Source Han Sans CN, Source Han Sans CN;
                font-weight: 400;
                font-size: 30rpx;
                color: #D2D2D2;
            }
        }
        .clickable-input {
            width: 100%;
            cursor: pointer;
        }
        :deep(.u-form-item__label) {
            margin-bottom: 20rpx;
            // font-weight: bold;
            // color: #333;
            font-family: Source Han Sans CN, Source Han Sans CN;
            font-weight: 400;
            font-size: 30rpx;
            color: #222324;
        }
        :deep(.u-form-item__required) {
            color: #FF2600;
            margin-right: 8rpx;
        }
    }
}
</style>
uniapps/work-wx/src/subPackages/flightApplication/add.vue
@@ -373,7 +373,7 @@
        @cancel="isShowFlightRouteInfo = false"
    />
    </u-form>
    <u-button @click="submitForm" color="#1D6FE9">飞行申请</u-button>
    <u-button :disabled="isSubmitDisabled" @click="submitForm" color="#1D6FE9">飞行申请</u-button>
    <!-- 飞任务类型 -->
    <view class="attention">
        注:新增空域/航线/飞手等前往吉安市低空经济服务一体化
@@ -381,7 +381,14 @@
</view>
</template>
<script setup>
import { flightPlanSaveApi, flightRoutePageInfoApi, aircraftInfoPageInfoOfMyApi, pilotInfoPageInfoOfMyApi, flightAirspacePageInfoApi, uploadFileApi } from '@/api/index'
import { flightPlanSaveApi,
    flightRoutePageInfoApi,
    aircraftInfoPageInfoOfMyApi,
    pilotInfoPageInfoOfMyApi,
    droneFlightTaskApi,
    uploadFileApi,
    flightTaskApi,flightRulesApi,flightModeApi,flightPlanApi
 } from '@/api/index'
import { ref, computed, onMounted } from 'vue'
import dayjs from 'dayjs'
@@ -400,7 +407,7 @@
    planType: '', // 计划类型
    planTypeText: '', // 计划类型文本
    contactPerson: '', // 联系人
    contactPhon: '', // 联系人电话
    contactPhone: '', // 联系人电话
    undertaker: '', // 承办人
    undertakerPhone: '', // 承办人电话
    timeRange: '', // 时间范围
@@ -432,13 +439,37 @@
    contactPerson: [
        { required: true, message: '请输入联系人', trigger: 'blur' }
    ],
    contactPhon: [
    contactPhone: [
        // 验证手机号码
        {
            validator: (rule, value, callback) => {
                const reg = /^1[3456789]\d{9}$/
                if (!reg.test(value)) {
                    callback(new Error('请输入正确的手机号码'))
                } else {
                    callback()
                }
            },
            trigger: 'blur'
        },
        { required: true, message: '请输入联系人电话', trigger: 'blur' }
    ],
    undertaker: [
        { required: true, message: '请输入承办人', trigger: 'blur' }
    ],
    undertakerPhone: [
        // 验证手机号码
        {
            validator: (rule, value, callback) => {
                const reg = /^1[3456789]\d{9}$/
                if (!reg.test(value)) {
                    callback(new Error('请输入正确的手机号码'))
                } else {
                    callback()
                }
            },
            trigger: 'blur'
        },
        { required: true, message: '请输入承办人联系电话', trigger: 'blur' }
    ],
    startTime: [
@@ -462,32 +493,7 @@
})
// 任务类型
const isShowFlightTaskType = ref(false)
const actionsFlightTaskType = [
    {
        label: '警务',
        value: '警务',
    },
    {
        label: '文旅',
        value: '文旅',
    },
    {
        label: '航拍',
        value: '航拍',
    },
    {
        label: '物流',
        value: '物流',
    },
    {
        label: '巡检',
        value: '巡检',
    },
    {
        label: '植保',
        value: '植保',
    },
]
const actionsFlightTaskType = ref([])
const onPickerFlightTaskType = (e) => {
  const selected = e.value[0]
  formParams.value.flightTaskType = selected.value
@@ -497,16 +503,7 @@
// 飞行规则
const isShowFlightRule = ref(false)
const actionsFlightRule = [
    {
        label: '目视餡相飞行规则(VFR)',
        value: '目视餡相飞行规则(VFR)',
    },
    {
        label: '仪表飞行规则(IFR)',
        value: '仪表飞行规则(IFR)',
    },
]
const actionsFlightRule = ref([])
const onPickerFlightRule = (e) => {
  const selected = e.value[0]
  formParams.value.flightRule = selected.value
@@ -515,16 +512,7 @@
}
// 飞行方式
const isShowFlightMode = ref(false)
const actionsFlightMode = [
    {
        label: '视距内飞行 (VLOS)',
        value: '视距内飞行 (VLOS)',
    },
    {
        label: '超视距飞行(BVLOS)',
        value: '超视距飞行(BVLOS)',
    },
]
const actionsFlightMode = ref([])
const onPickerFlightMode = (e) => {
  const selected = e.value[0]
  formParams.value.flightMode = selected.value
@@ -533,16 +521,7 @@
}
// 计划类型
const isShowPlanType = ref(false)
const actionsPlanType = [
    {
        label: '一般',
        value: '0',
    },
    {
        label: '紧急',
        value: '1',
    }
]
const actionsPlanType = ref([])
const onPickerPlanType = (e) => {
  const selected = e.value[0]
  formParams.value.planType = selected.value
@@ -611,12 +590,12 @@
const isShowAirspaceInfo = ref(false)
const airspaceInfoList = ref([])
function getAirspaceInfo() {
    flightAirspacePageInfoApi({
    droneFlightTaskApi({
        current: 1,
        size: 1000,
    }).then(res => {
        airspaceInfoList.value = res.data.data.records.map(item => ({
            label: item.airspaceName || '暂无',
            label: item.taskName || '暂无',
            value: item.id,
        }))
    })
@@ -635,7 +614,6 @@
        current: 1,
        size: 1000,
    }).then(res => {
        console.log(res.data.data, '0000')
        flightRouteInfoList.value = res.data.data.records.map(item => ({
            label: item.name,
            value: item.id,
@@ -703,12 +681,14 @@
        });
    });
};
const isSubmitDisabled = ref(false)
// 提交表单
const submitForm = async () => {
  await formRef.value.validate()
  isSubmitDisabled.value = true
  flightPlanSaveApi(formParams.value).then(res => {
    if (res.data.code === 200) {
        isSubmitDisabled.value = true
      uni.showToast({
        title: '提交成功',
        icon: 'success',
@@ -727,7 +707,52 @@
    }
  })
}
// 获取任务类型
function getTaskType() {
    flightTaskApi().then(res => {
        actionsFlightTaskType.value = res.data.data.map(item => ({
            ...item,
            label: item.dictLabel || '暂无',
            value: item.dictValue || '暂无',
        }))
    })
}
// 获取飞行模式
function getFlightMode() {
    flightModeApi().then(res => {
        actionsFlightMode.value = res.data.data.map(item => ({
            ...item,
            label: item.dictLabel || '暂无',
            value: item.dictValue || '暂无',
        }))
    })
}
// 获取飞行规则
function getFlightRules() {
    flightRulesApi().then(res => {
        actionsFlightRule.value = res.data.data.map(item => ({
            ...item,
            label: item.dictLabel || '暂无',
            value: item.dictValue || '暂无',
        }))
    })
}
// 获取飞行计划
function getFlightPlan() {
    flightPlanApi().then(res => {
        actionsPlanType.value = res.data.data.map(item => ({
            ...item,
            label: item.dictLabel || '暂无',
            value: item.dictValue || '暂无',
        }))
    })
}
onMounted(() => {
    // getProTypeApi()
    getTaskType()
    getFlightMode()
    getFlightRules()
    getFlightPlan()
    getAircraftInfo()
    getPilotInfo()
    getAirspaceInfo()
uniapps/work-wx/src/subPackages/flightApplication/details.vue
New file
@@ -0,0 +1,509 @@
<!-- 飞行活动申请 -->
<template>
<view class="flightApplication-add">
    <view class="title">
        基本信息
    </view>
    <u-form labelPosition="top" :model="formParams" :rules="rules" ref="formRef">
        <u-form-item
                label="飞行计划"
                labelWidth="200rpx"
                prop="flightPlanName"
                :borderBottom="true"
                ref="item1"
        >
            <u-input v-model="formParams.flightPlanName" border="none" placeholder="请输入飞行计划名称"></u-input>
        </u-form-item>
        <u-form-item
                label="任务类型"
                labelWidth="230rpx"
                prop="flightTaskType"
                :borderBottom="true"
                ref="item2"
        >
            <u-input
                v-model="formParams.flightTaskType"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择任务类型"
                border="none"
            ></u-input>
        </u-form-item>
        <u-form-item
                label="飞行规则"
                labelWidth="230rpx"
                prop="flightRule"
                :borderBottom="true"
                ref="item2"
        >
            <u-input
                v-model="formParams.flightRule"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择飞行规则"
                border="none"
            ></u-input>
        </u-form-item>
        <u-form-item
                label="飞行方式"
                labelWidth="200rpx"
                prop="flightMode"
                :borderBottom="true"
                ref="item1"
        >
            <u-input
                v-model="formParams.flightMode"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择飞行方式"
                border="none"
            ></u-input>
        </u-form-item>
        <u-form-item
                label="飞行活动开始日期"
                labelWidth="260rpx"
                prop="flightStartTime"
                :borderBottom="true"
                ref="item1"
        >
            <u-input v-model="formParams.flightStartTime" border="none" placeholder="请选择日期" suffixIcon="calendar"></u-input>
        </u-form-item>
         <u-form-item
                label="飞行活动结束日期"
                labelWidth="260rpx"
                prop="flightEndTime"
                :borderBottom="true"
                ref="item1"
        >
            <u-input v-model="formParams.flightEndTime" border="none" placeholder="请选择日期" suffixIcon="calendar"></u-input>
        </u-form-item>
        <u-form-item
                label="计划类型"
                labelWidth="200rpx"
                prop="planType"
                :borderBottom="true"
                ref="item1"
        >
            <u-input
                v-model="formParams.planType"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择计划类型"
                border="none"
            ></u-input>
        </u-form-item>
         <u-form-item
                label="联系人"
                labelWidth="200rpx"
                prop="contactPerson"
                :borderBottom="true"
                ref="item1"
        >
            <u-input v-model="formParams.contactPerson" border="none" placeholder="请输入"></u-input>
        </u-form-item>
        <u-form-item
                label="联系电话"
                labelWidth="200rpx"
                prop="contactPhone"
                :borderBottom="true"
                ref="item1"
        >
            <u-input v-model="formParams.contactPhone" border="none" placeholder="请输入"></u-input>
        </u-form-item>
        <u-form-item
                label="承办人"
                labelWidth="200rpx"
                prop="undertaker"
                :borderBottom="true"
                ref="item1"
        >
            <u-input v-model="formParams.undertaker" border="none" placeholder="请输入"></u-input>
        </u-form-item>
        <u-form-item
                label="承办人联系电话"
                labelWidth="300rpx"
                prop="undertakerPhone"
                :borderBottom="true"
                ref="item1"
        >
            <u-input v-model="formParams.undertakerPhone" border="none" placeholder="请输入"></u-input>
        </u-form-item>
        <u-form-item
                label="临时空域批件"
                labelWidth="200rpx"
                prop="aircraftName"
                :borderBottom="true"
                ref="item1"
        >
            <u-input v-model="formParams.tempAirspaceApproval" border="none" readonly></u-input>
        </u-form-item>
        <u-form-item
                label="气象条件"
                labelWidth="200rpx"
                prop="weatherCondition"
                :borderBottom="true"
                ref="item1"
        >
            <u-input
                    v-model="formParams.weatherCondition"
                    border="none"
                    placeholder="请输入"
            ></u-input>
        </u-form-item>
         <view class="title form-title">飞行器/飞手</view>
        <u-form-item
                label="飞行器"
                labelWidth="230rpx"
                prop="aircraftId"
                :borderBottom="true"
                ref="item2"
        >
            <u-input
                v-model="formParams.aircraftId"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择飞行器"
                border="none"
            ></u-input>
        </u-form-item>
        <u-form-item
                label="飞手"
                labelWidth="230rpx"
                prop="pilotId"
                :borderBottom="true"
                ref="item2"
        >
            <u-input
                v-model="formParams.pilotId"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择飞手"
                border="none"
            ></u-input>
        </u-form-item>
        <view class="title form-title">空域/航线/场点</view>
        <u-form-item
                label="空域"
                labelWidth="230rpx"
                prop="airspaceId"
                :borderBottom="true"
                ref="item2"
        >
            <u-input
                v-model="formParams.airspaceId"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择空域"
                border="none"
            ></u-input>
        </u-form-item>
        <u-form-item
                label="航线"
                labelWidth="230rpx"
                prop="routeId"
                :borderBottom="true"
                ref="item2"
        >
            <u-input
                v-model="formParams.routeId"
                disabled
                disabledColor="#ffffff"
                placeholder="请选择航线"
                border="none"
            ></u-input>
        </u-form-item>
    </u-form>
</view>
</template>
<script setup>
import { flightPlanSaveApi,
    flightRoutePageInfoApi,
    aircraftInfoPageInfoOfMyApi,
    pilotInfoPageInfoOfMyApi,
    flightAirspacePageInfoApi,
    uploadFileApi,
    flightTaskApi,flightRulesApi,flightModeApi,flightPlanApi,flightPlanDetailsApi
 } from '@/api/index'
import { ref, computed, onMounted } from 'vue'
import dayjs from 'dayjs'
const formRef = ref(null);
const formParams = ref({})
const rules = ref({})
// 任务类型
const isShowFlightTaskType = ref(false)
const actionsFlightTaskType = ref([])
// 飞行规则
const isShowFlightRule = ref(false)
const actionsFlightRule = ref([])
// 飞行方式
const isShowFlightMode = ref(false)
const actionsFlightMode = ref([])
// 计划类型
const isShowPlanType = ref(false)
const actionsPlanType = ref([])
// 是否显示开始时间选择器
const isShowStartTime = ref(false)
// 是否显示结束时间选择器
const isShowEndTime = ref(false)
const startTime = ref(Date.now()); // 设置默认值为当前时间戳
const endTime = ref(Date.now()); // 设置默认值为当前时间戳
const onConfirmStartTime = (e) => {
    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:ss')
    isShowEndTime.value = false
}
// 获取飞行器信息
const isShowAircraftInfo = ref(false)
const aircraftInfoList = ref([])
function getAircraftInfo() {
    aircraftInfoPageInfoOfMyApi({
        current: 1,
        size: 1000,
    }).then(res => {
        aircraftInfoList.value = res.data.data.records.map(item => ({
            label: item.name || '暂无',
            value: item.id,
        }))
    })
}
const onPickerAircraftInfo = (e) => {
  const selected = e.value[0]
  formParams.value.aircraftId = selected.value
  formParams.value.aircraftText = selected.label
  isShowAircraftInfo.value = false
}
// 获取飞手信息
const isShowPilotInfo = ref(false)
const pilotInfoList = ref([])
function getPilotInfo() {
    pilotInfoPageInfoOfMyApi({
        current: 1,
        size: 1000,
    }).then(res => {
        pilotInfoList.value = res.data.data.records.map(item => ({
            label: item.name || '暂无',
            value: item.id,
        }))
    })
}
// 获取空域信息
const isShowAirspaceInfo = ref(false)
const airspaceInfoList = ref([])
function getAirspaceInfo() {
    flightAirspacePageInfoApi({
        current: 1,
        size: 1000,
    }).then(res => {
        airspaceInfoList.value = res.data.data.records.map(item => ({
            label: item.airspaceName || '暂无',
            value: item.id,
        }))
    })
}
// 获取航线信息
const isShowFlightRouteInfo = ref(false)
const flightRouteInfoList = ref([])
function getFlightRouteInfo() {
    flightRoutePageInfoApi({
        current: 1,
        size: 1000,
    }).then(res => {
        flightRouteInfoList.value = res.data.data.records.map(item => ({
            label: item.name,
            value: item.id,
        }))
    })
}
// 获取任务类型
function getTaskType() {
    flightTaskApi().then(res => {
        actionsFlightTaskType.value = res.data.data.map(item => ({
            ...item,
            label: item.dictLabel || '暂无',
            value: item.dictCode || '暂无',
        }))
    })
}
// 获取飞行模式
function getFlightMode() {
    flightModeApi().then(res => {
        actionsFlightMode.value = res.data.data.map(item => ({
            ...item,
            label: item.dictLabel || '暂无',
            value: item.dictCode || '暂无',
        }))
    })
}
// 获取飞行规则
function getFlightRules() {
    flightRulesApi().then(res => {
        actionsFlightRule.value = res.data.data.map(item => ({
            ...item,
            label: item.dictLabel || '暂无',
            value: item.dictCode || '暂无',
        }))
    })
}
// 获取飞行计划
function getFlightPlan() {
    flightPlanApi().then(res => {
        actionsPlanType.value = res.data.data.map(item => ({
            ...item,
            label: item.dictLabel || '暂无',
            value: item.dictCode || '暂无',
        }))
    })
}
onLoad((options) => {
     flightPlanDetailsApi(options.id).then(res => {
        console.log(res.data.data, '789')
        // return
        formParams.value = res.data.data
        formParams.value.routeId = res.data.data.routeInfo.name
        formParams.value.airspaceId = res.data.data.airspaceInfo.taskName
        formParams.value.aircraftId = res.data.data.airspaceInfo.aircraftInfoList.map(item => item.name).join(',')
        formParams.value.pilotId = res.data.data.operatorInfoList.map(item => item.name).join(',')
        formParams.value.flightRule = actionsFlightRule.value.find(item => item.dictValue === res.data.data.flightRule)?.dictLabel || ''
        formParams.value.flightMode = actionsFlightMode.value.find(item => item.dictValue === res.data.data.flightMode)?.dictLabel || ''
        formParams.value.planType = actionsPlanType.value.find(item => item.dictValue === res.data.data.planType)?.dictLabel || ''
        formParams.value.flightTaskType = actionsFlightTaskType.value.find(item => item.dictValue === res.data.data.flightTaskType)?.dictLabel || ''
    })
})
onMounted(() => {
    getTaskType()
    getFlightMode()
    getFlightRules()
    getFlightPlan()
    // getAircraftInfo()
    // getPilotInfo()
    // getAirspaceInfo()
    // getFlightRouteInfo()
})
</script>
<style scoped lang="scss">
.flightApplication-add {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    .title {
        font-family: Source Han Sans CN, Source Han Sans CN;
        font-weight: 400;
        font-size: 17px;
        color: #222324;
        padding-left: 24rpx;
    }
    .form-title {
        margin-bottom: 24rpx;
        padding-left: 0rpx !important;
    }
    :deep(.u-button) {
        width: 80%;
        padding: 28rpx;
        border-radius: 8rpx 8rpx 8rpx 8rpx;
        margin-bottom: 20rpx;
    }
    .attention {
        font-family: Source Han Sans CN, Source Han Sans CN;
        font-weight: 400;
        font-size: 12px;
        color: #1D6FE9;
        //  margin-top: 24rpx;
        text-align: center;
    }
    .select-wrapper {
        position: relative;
        width: 100%;
        .select-icon {
            position: absolute;
            right: 20rpx;
            top: 50%;
            transform: translateY(-50%);
            color: #999;
            font-size: 24rpx;
            pointer-events: none;
        }
    }
    .upload {
        font-family: Source Han Sans CN, Source Han Sans CN;
        font-weight: 400;
        font-size: 15px;
        color: #1D6FE9;
    }
     :deep(.u-form) {
        flex: 1;
        display: flex;
        flex-direction: column;
        overflow-y: auto;
        box-sizing: border-box;
        padding: 24rpx;
    }
     .submit-button-wrapper {
        padding: 28rpx;
        // width: 80%;
        // height: 100rpx;
        border-radius: 8rpx 8rpx 8rpx 8rpx;
    }
    :deep(.u-form-item) {
        margin-bottom: 40rpx;
        padding: 16rpx 24rpx 18rpx 24rpx;
        border-radius: 6px 6px 6px 6px;
        background: white;
        :deep(.u-line) {
            border-bottom: 1px solid #EFEFEF;
        }
        :deep(.u-input) {
            width: 100%;
            height: 80rpx;
            border-radius: 8rpx;
            padding: 0 20rpx;
            background-color: #f5f5f5;
            &::placeholder {
                font-family: Source Han Sans CN, Source Han Sans CN;
                font-weight: 400;
                font-size: 30rpx;
                color: #D2D2D2;
            }
        }
        .clickable-input {
            width: 100%;
            cursor: pointer;
        }
        :deep(.u-form-item__label) {
            margin-bottom: 20rpx;
            // font-weight: bold;
            // color: #333;
            font-family: Source Han Sans CN, Source Han Sans CN;
            font-weight: 400;
            font-size: 30rpx;
            color: #222324;
        }
        :deep(.u-form-item__required) {
            color: #FF2600;
            margin-right: 8rpx;
        }
    }
}
</style>
uniapps/work-wx/src/subPackages/flightApplication/index.vue
@@ -18,7 +18,7 @@
            审批记录
        </view>
        <u-list :style="{ height: 'calc(100vh - 240rpx)' }" @scrolltolower="scrolltolower" :lower-threshold="50">
            <view class="approvalRecord" v-for="(approvalRecord,approvalRecordIndex) in approvalRecords" :key="approvalRecordIndex">
            <view class="approvalRecord" v-for="(approvalRecord,approvalRecordIndex) in approvalRecords" :key="approvalRecordIndex" @click="handleClick(approvalRecord)">
                <view class="name-status">
                    <view class="name-image">
                        <u-image :src="spSvg" class="icon" width="16" height="16"></u-image>
@@ -97,6 +97,12 @@
    }
    getApprovalRecords()
}
// 点击审批记录跳转详情页
function handleClick(item) {
    uni.navigateTo({
        url: '/subPackages/flightApplication/details?id=' + item.id,
    })
}
onMounted(() => {
    getApprovalRecords()
})
uniapps/work-wx/src/subPackages/userDetail/infos/index.vue
@@ -8,16 +8,27 @@
        <view class="detailBox">
            <div class="detailCon">
                <div class="orderRow">
                    <div class="rowTitle">姓名</div>
                    <div class="rowTitle">昵称</div>
                    <div>{{userInfo.nickName}}</div>
                </div>
                <!-- <div class="orderRow">
                    <div class="rowTitle">所属单位</div>
                    <div>{{userInfo.deptName}}</div>
                </div> -->
                <div class="orderRow">
                    <div class="rowTitle">认证状态</div>
                    <div>{{userInfo.status === 1 ? '已认证' : '未认证'}}</div>
                </div>
                <div class="orderRow">
                    <div class="rowTitle">性别</div>
                    <div>
                        <u-radio-group v-model="userInfo.sex">
                            <up-radio
                                v-for="(item, index) in sexList"
                                :key="index"
                                :label="item.label"
                                :name="item.name"
                                @change="radioChange"
                                >
                            </up-radio>
                        </u-radio-group>
                    </div>
                </div>
                <div class="orderRow">
                    <div class="rowTitle">手机号</div>
@@ -41,6 +52,7 @@
</template>
<script setup>
    import { uploadFileApi } from '@/api/index'
    import showDefaultHeader from "@/static/images/user/default-header.png";
    import {
        getEnvObj,
@@ -62,7 +74,22 @@
        phone: '',
        email: '',
        deptName: '',
        sex: 2,
    });
    const sexList = [
        {
            label: '男',
            name: '0'
        },
        {
            label: '女',
            name: '1'
        },
        {
            label: '未知',
            name: '2'
        }
    ]
    // 校验手机号
    const validatePhone = () => {
        const phone = userInfo.value.phone
@@ -97,15 +124,14 @@
    const getUserInfoData = () => {
        getUserInfo().then(res => {
            const user = res.data.data;
            console.log('user',user)
            userInfo.value = {
                id: user.userId,
                avatar: user.avatar,
                nickName: user.nickName,
                // realName: user.realName,
                phone: user.phonenumber,
                email: user.sysDept.email,
                deptName: user.sysDept.deptName,
                sex: user.sex || 2,
            };
        });
@@ -113,45 +139,47 @@
    const reset = () => {
        getUserInfoData();
    };
    const {
        VITE_API_BASE_URL
    } = getEnvObj()
    function uploadUtil(options) {
        const {
            formData,
            filePath,
            url
        } = options;
        return new Promise((resolve, reject) => {
            let accessToken = useUserStore()?.$state?.userInfo?.access_token;
            uni.uploadFile({
                url: `${VITE_API_BASE_URL}${url}`,
                name: 'file',
                header: {
                    'Blade-Auth': 'bearer ' + accessToken
                },
                filePath: filePath,
                formData,
                success: (res) => {
                    const resData = JSON.parse(res.data)
                    if (resData.code === 200 || resData.code === 0) {
                        resolve(res)
                    } else {
                        showToast(resData.message)
                        reject(res)
                    }
                },
                fail: (err) => {
                    reject(err)
                }
            });
        })
    }
    // 提交文件
function afterReadFile(event) {
    console.log('上传的文件:', event);
    // 准备上传参数
    const uploadParams = {
        name: event.name, // 服务器端接收文件的字段名
        file: event.file, // 传入文件对象
        filePath: event.filePath, // 文件路径
    };
    // 调用上传文件接口
    uploadFileApi(uploadParams).then(res => {
        uni.hideLoading();
        console.log('上传成功结果:', res);
        // 根据接口返回的数据结构处理结果
        if (res.data && res.data.code === 200) {
            const uploadResult = res.data.data;
            userInfo.value.avatar = uploadResult.fileUrl || '文件上传成功';
            uni.showToast({
                title: '上传成功',
                icon: 'success'
            });
        } else {
            uni.showToast({
                title: res.msg || '上传失败',
                icon: 'none'
            });
        }
    }).catch(err => {
        uni.hideLoading();
        console.error('上传失败:', err);
        uni.showToast({
            title: '上传失败',
            icon: 'none'
        });
    });
}
    const uploadAvatar = () => {
        uni.chooseImage({
            count: 1,
@@ -176,35 +204,11 @@
                    title: '上传中...'
                });
                // 上传文件
                uploadUtil({
                    filePath: filePath,
                    formData: {
                        fileName: fileName,
                        sn: 'avatar_upload'
                    },
                    url: '/blade-resource/oss/endpoint/put-file'
                }).then(res => {
                    const resData = JSON.parse(res.data);
                    if (resData.code === 200 || resData.code === 0) {
                        // 更新头像显示
                        userInfo.value.avatar = resData.data.link || resData.data.url;
                        uni.hideLoading();
                        uni.showToast({
                            title: '头像上传成功',
                            icon: 'success'
                        });
                    } else {
                        throw new Error(resData.message || '上传失败');
                    }
                }).catch(err => {
                    uni.hideLoading();
                    uni.showToast({
                        title: err.message || '上传失败',
                        icon: 'none'
                    });
                });
                afterReadFile({
                    file: tempFile,
                    filePath: filePath,
                    name: fileName,
                })
            }
        });
    }
@@ -329,7 +333,7 @@
    }
    .note {
        position: absolute;
        bottom: 400rpx;
        bottom: 300rpx;
        font-family: Source Han Sans CN, Source Han Sans CN;
        font-weight: 400;
        font-size: 10px;