Merge remote-tracking branch 'origin/master'
24 files modified
5 files added
| | |
| | | method: 'get' |
| | | }) |
| | | } |
| | | |
| | | // 区域列表 |
| | | export const areaDivideListApi = (params) => { |
| | | return request({ |
| | | url: '/drone-fw/area/fwAreaDivide/list', |
| | | method: 'get', |
| | | params |
| | | }) |
| | | } |
| | |
| | | |
| | | .command-data-cockpit-select { |
| | | .el-select__input { |
| | | color: #8ac3fd !important; |
| | | color: #ffffff !important; |
| | | } |
| | | |
| | | .el-select__wrapper { |
| | |
| | | |
| | | |
| | | ## 分页 |
| | | ## 列表 |
| | | |
| | | |
| | | **接口地址**:`/device/fwDevice/page` |
| | | **接口地址**:`/drone-fw/area/fwAreaDivide/list` |
| | | |
| | | |
| | | **请求方式**:`GET` |
| | |
| | | **响应数据类型**:`*/*` |
| | | |
| | | |
| | | **接口描述**:<p>传入fwDevice</p> |
| | | **接口描述**:<p>传入filterSelected或sceneId</p> |
| | | |
| | | |
| | | |
| | |
| | | |
| | | | 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | 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)|| |
| | | |
| | | |
| | | **响应状态**: |
| | |
| | | |
| | | | 状态码 | 说明 | schema | |
| | | | -------- | -------- | ----- | |
| | | |200|OK|R«IPage«FwDeviceVO»»| |
| | | |200|OK|R«List«FwAreaDivide对象»»| |
| | | |401|Unauthorized|| |
| | | |403|Forbidden|| |
| | | |404|Not Found|| |
| | |
| | | | 参数名称 | 参数说明 | 类型 | schema | |
| | | | -------- | -------- | ----- |----- | |
| | | |code|状态码|integer(int32)|integer(int32)| |
| | | |data|承载数据|IPage«FwDeviceVO»|IPage«FwDeviceVO»| |
| | | |  current||integer(int64)|| |
| | | |  pages||integer(int64)|| |
| | | |  records||array|FwDeviceVO| |
| | | |    azimuth|方位角 0-360°|integer|| |
| | | |    batteryPct|电量百分比 0-100|integer|| |
| | | |    belongDept|所属部门id|integer|| |
| | | |    belongDeptName|所属部门名称|string|| |
| | | |    charger|负责人|string|| |
| | | |    contactPhone|联系电话|string|| |
| | | |    createTime|创建时间|string|| |
| | | |    detectTargetCnt|侦测目标数量|integer|| |
| | | |    deviceAtt|设备属性(无线电/光电/雷达)|string|| |
| | | |    deviceModel|型号|string|| |
| | | |    deviceName|设备名称|string|| |
| | | |    deviceSpecification|规格|string|| |
| | | |    deviceType|设备类型(察打一体/便捷侦测箱/反制枪)|string|| |
| | | |    effectiveRangeKm|有效范围 km|number|| |
| | | |    elevation|俯仰角 -90-+90°|integer|| |
| | | |    id|主键|integer|| |
| | | |    manufacturer|生产厂商|string|| |
| | | |    purpose|用途|string|| |
| | | |    remark|备注|string|| |
| | | |    source|来源|string|| |
| | | |    status|运行状态(0:在线/1:离线/2:故障/3:报废)|integer|| |
| | | |    workMode|工作模式:1.侦测中/2.信号干扰中/3.诱导驱离中/4.待机|string|| |
| | | |  size||integer(int64)|| |
| | | |  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| |
| | | |  azimuth|方位角 0-360°|integer(int32)|| |
| | | |  batteryPct|电量百分比 0-100|integer(int32)|| |
| | | |  belongDept|所属部门id|integer(int64)|| |
| | | |  belongDeptName|所属部门名称|string|| |
| | | |  charger|负责人|string|| |
| | | |  contactPhone|联系电话|string|| |
| | | |data|承载数据|array|FwAreaDivide对象| |
| | | |  areaCode|区域编码|string|| |
| | | |  areaName|区域名称|string|| |
| | | |  areaSize|区域面积(km²)|number|| |
| | | |  areaType|区域类型:报警区/重点管制区|string|| |
| | | |  controlLevel|管控级别:日常/重点/严密|string|| |
| | | |  createDept|创建部门|integer(int64)|| |
| | | |  createTime|创建时间|string(date-time)|| |
| | | |  detectTargetCnt|侦测目标数量|integer(int32)|| |
| | | |  deviceAtt|设备属性(无线电/光电/雷达)|string|| |
| | | |  deviceModel|型号|string|| |
| | | |  deviceName|设备名称|string|| |
| | | |  deviceSpecification|规格|string|| |
| | | |  deviceType|设备类型(察打一体/便捷侦测箱/反制枪)|string|| |
| | | |  effectiveRangeKm|有效范围 km|number|| |
| | | |  elevation|俯仰角 -90-+90°|integer(int32)|| |
| | | |  id|主键|integer(int64)|| |
| | | |  manufacturer|生产厂商|string|| |
| | | |  purpose|用途|string|| |
| | | |  remark|备注|string|| |
| | | |  source|来源|string|| |
| | | |  status|运行状态(0:在线/1:离线/2:故障/3:报废)|integer(int32)|| |
| | | |  workMode|工作模式:1.侦测中/2.信号干扰中/3.诱导驱离中/4.待机|string|| |
| | | |  createUser|创建人|integer(int64)|| |
| | | |  deviceIds|关联设备ID,逗号分隔|string|| |
| | | |  flyDateEnd|可飞行日期时间-结束|string(date-time)|| |
| | | |  flyDateStart|可飞行日期时间-开始|string(date-time)|| |
| | | |  geom|经纬度面(存储地理面数据)|string|| |
| | | |  id|主键id|integer(int64)|| |
| | | |  isDeleted|是否已删除|integer(int32)|| |
| | | |  latitude|区域中心纬度|number(double)|| |
| | | |  longitude|区域中心经度|number(double)|| |
| | | |  policeStationContactPerson|派出所联系人|string|| |
| | | |  policeStationContactPhone|派出所联系电话|string|| |
| | | |  policeStationId|关联派出所id|integer(int64)|| |
| | | |  policeStationName|派出所名称|string|| |
| | | |  responseMechanism|响应机制内容|string|| |
| | | |  status|业务状态|integer(int32)|| |
| | | |  triggerCondition|触发条件|string|| |
| | | |  updateTime|更新时间|string(date-time)|| |
| | | |  updateUser|更新人|integer(int64)|| |
| | | |msg|返回消息|string|| |
| | | |success|是否成功|boolean|| |
| | | |
| | |
| | | ```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| |
| | | |  belongDept|所属部门||false|integer(int64)|| |
| | | |  charger|负责人||false|string|| |
| | | |  contactPhone|联系电话||false|string|| |
| | | |  deviceAtt|设备属性(无线电/光电/雷达)||false|string|| |
| | | |  deviceModel|型号||false|string|| |
| | | |  deviceName|设备名称||true|string|| |
| | | |  deviceSpecification|规格||false|string|| |
| | | |  deviceStatusList|设备状态,多个状态用逗号隔开||false|array|string| |
| | | |  deviceType|设备类型(察打一体/便捷侦测箱/反制枪)||false|string|| |
| | | |  effectiveRangeKm|有效范围 km||false|number|| |
| | | |  effectiveRangeKmIsNotNull|是否有范围 0不过滤 1有 2没有||false|integer(int32)|| |
| | | |  id|主键||false|integer(int64)|| |
| | | |  manufacturer|生产厂商||false|string|| |
| | | |  purpose|用途||false|string|| |
| | | |  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 |
| | | } |
| | |
| | | <template> |
| | | <div class="deviceChart"> |
| | | <div class="titleBg"> |
| | | <span>设备类型分析</span> |
| | | <span>出库去向统计</span> |
| | | </div> |
| | | <div class="chartBox" ref="chartRef"></div> |
| | | </div> |
| | |
| | | </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"> |
| | |
| | | </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' |
| | | |
| | |
| | | } |
| | | }) |
| | | |
| | | const areaOptions = ref([]) |
| | | |
| | | const rules = { |
| | | charger: fieldRules(true), // 负责人 |
| | | contactPhone: fieldRules(true, 50), // 联系电话 |
| | |
| | | } |
| | | |
| | | 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 => { |
| | |
| | | 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') |
| | |
| | | |
| | | onMounted(() => { |
| | | getUserList() |
| | | getAreaOptions() |
| | | }) |
| | | |
| | | defineExpose({ open }) |
| | |
| | | |
| | | <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> |
| | | |
| | |
| | | <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: { |
| | |
| | | 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) { |
| | |
| | | } |
| | | |
| | | 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( |
| | |
| | | (visible) => { |
| | | if (visible) { |
| | | searchParams.value.current = 1 |
| | | fetchDictList() |
| | | fetchAreaTree() |
| | | getList() |
| | | } else { |
| | | searchParams.value = initSearchParams() |
| | | } |
| | | } |
| | | ) |
| | |
| | | data |
| | | }) |
| | | } |
| | | |
| | | // 我的空域查询 |
| | | export const droneFlightTaskApi = data => { |
| | | return request({ |
| | | url: `/webservice/droneFlightTask/pageInfoOfMy`, |
| | | method: 'post', |
| | | data, |
| | | }) |
| | | } |
| | |
| | | let locationFlag = false |
| | | let watchId = null |
| | | let userLocationMarker = null |
| | | const setMapLocation = () => { |
| | | locationFlag = true |
| | | |
| | | // 初始化实时位置监听 |
| | | const initLocationWatch = () => { |
| | | // 在WebView加载的网页中 |
| | | // 检查浏览器是否支持 geolocation |
| | | if (navigator.geolocation) { |
| | |
| | | // 停止监听位置 |
| | | navigator.geolocation.clearWatch(watchId) |
| | | } |
| | | |
| | | // 开始持续获取用户的位置 |
| | | watchId = navigator.geolocation.watchPosition( |
| | | function (position) { |
| | |
| | | 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) { |
| | |
| | | ) |
| | | } 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 |
| | |
| | | initMap() |
| | | map.addLayer(layers[0].map) |
| | | |
| | | // 初始化实时位置监听 |
| | | initLocationWatch() |
| | | |
| | | EventBus.on('mapSetView', mapSetView) |
| | | EventBus.on('mapAddMarker', mapAddMarker) |
| | | EventBus.on('mapAddAirMarker', mapAddAirMarker) |
| | |
| | | }) |
| | | |
| | | 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) |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 位置标记闪烁效果 |
| | | .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> |
| New file |
| | |
| | | <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> |
| | |
| | | </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() |
| | |
| | | |
| | | <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> |
| | |
| | | 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' |
| | |
| | | |
| | | // 使用新接口搜索地址 |
| | | 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 |
| | |
| | | <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(() => { |
| | |
| | | export function useGlobalWS() { |
| | | const userStore = useUserStore(); |
| | | const appStore = useAppStore(); |
| | | const callStatus = ref(null) |
| | | |
| | | // 设置默认用户ID为3 |
| | | const defaultUserId = '3'; |
| | |
| | | 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) |
| | |
| | | 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('无法跳转到语音通话页面:当前环境不支持导航'); |
| | |
| | | } |
| | | |
| | | // 然后处理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 } |
| | | ) |
| | | } |
| | | |
| | | |
| | |
| | | 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"; |
| | |
| | | const agreed = ref(true); |
| | | |
| | | const userStore = useUserStore(); |
| | | const locationStore = useLocationStore(); |
| | | const loginForm = ref({ |
| | | username: "", |
| | | password: "", |
| | |
| | | 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", |
| | | }); |
| | |
| | | import useAppStore from "./modules/app" |
| | | import useUserStore from "./modules/user" |
| | | import useMapStore from "./modules/map" |
| | | import useLocationStore from "./modules/location" |
| | | |
| | | // 安装pinia状态管理插件 |
| | | function setupStore (app) { |
| | |
| | | } |
| | | |
| | | // 导出模块 |
| | | export { useAppStore, useUserStore, useMapStore } |
| | | export { useAppStore, useUserStore, useMapStore, useLocationStore } |
| | | export default setupStore |
| New file |
| | |
| | | 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 |
| New file |
| | |
| | | /** |
| | | * 位置管理工具类 |
| | | * 用于处理位置权限请求和实时位置获取 |
| | | */ |
| | | 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 |
| | | }; |
| | |
| | | // WebSocket服务管理 |
| | | import { useGlobalWS } from "@/hooks/useGlobalWS.js";// WebSocket服务管理 |
| | | class WebSocketService { |
| | | constructor() { |
| | | this.socketTask = null |
| | |
| | | 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) |
| | | } |
| | |
| | | // 监听连接打开 |
| | | 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() |
| | |
| | | // 监听连接关闭 |
| | | this.socketTask.onClose(() => { |
| | | this.connected = false |
| | | console.log('WebSocket已关闭') |
| | | // console.log('WebSocket已关闭') |
| | | this.stopPing() |
| | | if (this.onCloseCallback) { |
| | | this.onCloseCallback() |
| | |
| | | |
| | | // 监听连接错误 |
| | | this.socketTask.onError((error) => { |
| | | console.error('WebSocket错误:', error) |
| | | // console.error('WebSocket错误:', error) |
| | | this.connected = false |
| | | this.stopPing() |
| | | if (this.onErrorCallback) { |
| | |
| | | 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) |
| | | } |
| | |
| | | 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) |
| | | } |
| | | }) |
| | | } |
| | |
| | | code: 1000, |
| | | reason: '正常关闭', |
| | | success: () => { |
| | | console.log('WebSocket已关闭') |
| | | // console.log('WebSocket已关闭') |
| | | }, |
| | | fail: (error) => { |
| | | console.error('WebSocket关闭失败:', error) |
| | | // console.error('WebSocket关闭失败:', error) |
| | | } |
| | | }) |
| | | this.socketTask = null |
| | |
| | | |
| | | // 设置消息回调 |
| | | setOnMessageCallback(callback) { |
| | | console.log('🔔 WebSocket消息回调已设置') |
| | | this.onMessageCallback = callback |
| | | } |
| | | |
| | | |
| | | // 获取当前消息回调(用于检查是否已设置) |
| | | getOnMessageCallback() { |
| | | return this.onMessageCallback |
| | |
| | | }) |
| | | } |
| | | |
| | | // 我的空域查询 |
| | | export const droneFlightTaskApi = data => { |
| | | return request({ |
| | | url: `/webservice/droneFlightTask/pageInfoOfMy`, |
| | | method: 'post', |
| | | data, |
| | | }) |
| | | } |
| | | |
| | | // 设备列表 |
| | | export const aircraftInfoPageInfoOfMyApi = data => { |
| | | return request({ |
| | |
| | | }) |
| | | } |
| | | |
| | | // 我的设备 |
| | | 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 => { |
| | |
| | | data, |
| | | }) |
| | | } |
| | | // 飞行申请详情 |
| | | export const flightPlanDetailsApi = id => { |
| | | return request({ |
| | | url: `/webservice/flightPlan/queryById/${id}`, |
| | | method: 'get' |
| | | }) |
| | | } |
| | | |
| | | // 航空器注册 |
| | | export const aircraftInfoSaveApi = data => { |
| | |
| | | url: `/webservice/aircraftInfo/save`, |
| | | method: 'post', |
| | | data, |
| | | }) |
| | | } |
| | | // 注册器详情 |
| | | export const aircraftInfoDetailsApi = id => { |
| | | return request({ |
| | | url: `/webservice/aircraftInfo/queryById/${id}`, |
| | | method: 'get' |
| | | }) |
| | | } |
| | | |
| | |
| | | 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, |
| | | }) |
| | | } |
| | | |
| | |
| | | } |
| | | }, |
| | | { |
| | | "path": "flightApplication/details", |
| | | "style": { |
| | | "navigationBarTitleText": "飞行详情" |
| | | } |
| | | }, |
| | | { |
| | | "path": "deviceRegistration/add", |
| | | "style": { |
| | | "navigationBarTitleText": "飞行器注册" |
| | | } |
| | | }, |
| | | { |
| | | "path": "deviceRegistration/details", |
| | | "style": { |
| | | "navigationBarTitleText": "飞行器详情" |
| | | } |
| | | }, |
| | | { |
| | | "path": "regulationsDetail/details", |
| | | "style": { |
| | | "navigationBarTitleText": "详情" |
| | |
| | | <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> |
| | |
| | | </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({ |
| | |
| | | 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++ |
| | | // 停止不刷新 |
| | |
| | | } |
| | | getEquipmentList() |
| | | } |
| | | onMounted(() => { |
| | | // onMounted(() => { |
| | | // getEquipmentList() |
| | | // }) |
| | | onShow(() => { |
| | | // 每次页面显示时重置为第一页并重新加载数据 |
| | | formParams.value.current = 1 |
| | | getEquipmentList() |
| | | }) |
| | | </script> |
| | |
| | | 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++) { |
| | |
| | | // 获取空域信息列表 |
| | | 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, |
| | |
| | | // 跳转到空域信息详情页 |
| | | function airspaceClick(item) { |
| | | uni.navigateTo({ |
| | | url: '/subPackages/regulationsDetail/details?id=' + item.id, |
| | | url: '/subPackages/airspaceInformation/index?taskName=' + item.taskName, |
| | | }) |
| | | } |
| | | // 跳转到法律政策详情页 |
| | |
| | | <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 = ""; |
| | |
| | | // #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; |
| | |
| | | <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> |
| | |
| | | @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' |
| | | |
| | |
| | | // 所属区域确认 |
| | | 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([ |
| | |
| | | } |
| | | // 飞行器厂商 |
| | | 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] |
| | |
| | | } |
| | | // 飞行器型号 |
| | | 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] |
| | |
| | | }); |
| | | } |
| | | // 提交表单 |
| | | 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' |
| | |
| | | // 这里可以添加额外的失败处理逻辑 |
| | | } |
| | | } |
| | | // 获取飞行器类型 |
| | | 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"> |
| New file |
| | |
| | | <!-- 飞行器详情 --> |
| | | <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> |
| | |
| | | @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"> |
| | | 注:新增空域/航线/飞手等前往吉安市低空经济服务一体化 |
| | |
| | | </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' |
| | | |
| | |
| | | planType: '', // 计划类型 |
| | | planTypeText: '', // 计划类型文本 |
| | | contactPerson: '', // 联系人 |
| | | contactPhon: '', // 联系人电话 |
| | | contactPhone: '', // 联系人电话 |
| | | undertaker: '', // 承办人 |
| | | undertakerPhone: '', // 承办人电话 |
| | | timeRange: '', // 时间范围 |
| | |
| | | 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: [ |
| | |
| | | }) |
| | | // 任务类型 |
| | | 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 |
| | |
| | | |
| | | // 飞行规则 |
| | | 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 |
| | |
| | | } |
| | | // 飞行方式 |
| | | 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 |
| | |
| | | } |
| | | // 计划类型 |
| | | 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 |
| | |
| | | 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, |
| | | })) |
| | | }) |
| | |
| | | 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, |
| | |
| | | }); |
| | | }); |
| | | }; |
| | | 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', |
| | |
| | | } |
| | | }) |
| | | } |
| | | // 获取任务类型 |
| | | 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() |
| New file |
| | |
| | | <!-- 飞行活动申请 --> |
| | | <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> |
| | |
| | | 审批记录 |
| | | </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> |
| | |
| | | } |
| | | getApprovalRecords() |
| | | } |
| | | // 点击审批记录跳转详情页 |
| | | function handleClick(item) { |
| | | uni.navigateTo({ |
| | | url: '/subPackages/flightApplication/details?id=' + item.id, |
| | | }) |
| | | } |
| | | onMounted(() => { |
| | | getApprovalRecords() |
| | | }) |
| | |
| | | <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> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { uploadFileApi } from '@/api/index' |
| | | import showDefaultHeader from "@/static/images/user/default-header.png"; |
| | | import { |
| | | getEnvObj, |
| | |
| | | phone: '', |
| | | email: '', |
| | | deptName: '', |
| | | sex: 2, |
| | | }); |
| | | const sexList = [ |
| | | { |
| | | label: '男', |
| | | name: '0' |
| | | }, |
| | | { |
| | | label: '女', |
| | | name: '1' |
| | | }, |
| | | { |
| | | label: '未知', |
| | | name: '2' |
| | | } |
| | | ] |
| | | // 校验手机号 |
| | | const validatePhone = () => { |
| | | const phone = userInfo.value.phone |
| | |
| | | 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, |
| | | }; |
| | | |
| | | }); |
| | |
| | | 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, |
| | |
| | | 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, |
| | | }) |
| | | } |
| | | }); |
| | | } |
| | |
| | | } |
| | | .note { |
| | | position: absolute; |
| | | bottom: 400rpx; |
| | | bottom: 300rpx; |
| | | font-family: Source Han Sans CN, Source Han Sans CN; |
| | | font-weight: 400; |
| | | font-size: 10px; |