From 1f522544d640b629bf84055b654594094b2eea92 Mon Sep 17 00:00:00 2001 From: "sean.zhou" Date: Fri, 4 Nov 2022 20:40:49 +0800 Subject: [PATCH] initial v1.3.0-beta2 --- .gitignore | 1 + package.json | 3 + src/api/device-setting/index.ts | 24 ++ src/api/wayline.ts | 11 + src/components/GMap.vue | 23 +- .../device-log/DeviceLogDetailModal.vue | 4 +- src/components/g-map/DeviceSettingBox.vue | 242 ++++++++++++++++++ src/components/g-map/DeviceSettingPopover.vue | 106 ++++++++ src/components/g-map/DockControlPanel.vue | 99 ++++--- src/components/g-map/useDeviceSetting.ts | 56 ++++ src/components/g-map/useDockControl.ts | 5 +- src/components/livestream-agora.vue | 5 + src/components/livestream-others.vue | 3 + src/hooks/use-g-map-tsa.ts | 6 +- src/pages/page-pilot/pilot-home.vue | 12 +- src/pages/page-web/projects/wayline.vue | 75 +++++- src/store/index.ts | 13 +- src/types/device-setting.ts | 148 +++++++++++ src/types/device.ts | 12 +- src/utils/device-setting.ts | 193 ++++++++++++++ 20 files changed, 978 insertions(+), 63 deletions(-) create mode 100644 src/api/device-setting/index.ts create mode 100644 src/components/g-map/DeviceSettingBox.vue create mode 100644 src/components/g-map/DeviceSettingPopover.vue create mode 100644 src/components/g-map/useDeviceSetting.ts create mode 100644 src/types/device-setting.ts create mode 100644 src/utils/device-setting.ts diff --git a/.gitignore b/.gitignore index 7fb9919..a8bbacf 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ yarn-error.log* /coverage /backup node_modules +./src/api/http/config.ts \ No newline at end of file diff --git a/package.json b/package.json index 2cf085e..2b5640f 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "ant-design-vue/es/empty/style/css", "ant-design-vue/es/form/style/css", "ant-design-vue/es/image/style/css", + "ant-design-vue/es/input-number/style/css", "ant-design-vue/es/input/style/css", "ant-design-vue/es/layout/style/css", "ant-design-vue/es/menu/style/css", @@ -94,7 +95,9 @@ "ant-design-vue/es/tag/style/css", "ant-design-vue/es/tooltip/style/css", "ant-design-vue/es/tree/style/css", + "ant-design-vue/es/upload/style/css", "axios", + "lodash", "mitt", "moment", "reconnecting-websocket", diff --git a/src/api/device-setting/index.ts b/src/api/device-setting/index.ts new file mode 100644 index 0000000..6df9d2d --- /dev/null +++ b/src/api/device-setting/index.ts @@ -0,0 +1,24 @@ +import request, { IWorkspaceResponse } from '/@/api/http/request' +import { ELocalStorageKey } from '/@/types' +import { NightLightsStateEnum, DistanceLimitStatus, ObstacleAvoidance } from '/@/types/device-setting' + +const MNG_API_PREFIX = '/manage/api/v1' +const workspaceId: string = localStorage.getItem(ELocalStorageKey.WorkspaceId) || '' + +export interface PutDevicePropsBody { + night_lights_state?: NightLightsStateEnum;// 夜航灯开关 + height_limit?: number;// 限高设置 + distance_limit_status?: DistanceLimitStatus;// 限远开关 + obstacle_avoidance?: ObstacleAvoidance;// 飞行器避障开关设置 +} + +/** + * 设置设备属性 + * @param params + * @returns + */ +// /manage/api/v1/devices/{{workspace_id}}/devices/{{device_sn}}/property +export async function putDeviceProps (deviceSn: string, body: PutDevicePropsBody): Promise> { + const resp = await request.put(`${MNG_API_PREFIX}/devices/${workspaceId}/devices/${deviceSn}/property`, body) + return resp.data +} diff --git a/src/api/wayline.ts b/src/api/wayline.ts index 1ecbf30..6718d6d 100644 --- a/src/api/wayline.ts +++ b/src/api/wayline.ts @@ -89,3 +89,14 @@ export async function deleteTask (workspaceId: string, params: DeleteTaskParams) }) return result.data } + +// Upload Wayline file +export const importKmzFile = async function (workspaceId: string, file: {}): Promise> { + const url = `${HTTP_PREFIX}/workspaces/${workspaceId}/waylines/file/upload` + const result = await request.post(url, file, { + headers: { + 'Content-Type': 'multipart/form-data', + } + }) + return result.data +} diff --git a/src/components/GMap.vue b/src/components/GMap.vue index 24791c7..5f5bb4d 100644 --- a/src/components/GMap.vue +++ b/src/components/GMap.vue @@ -182,12 +182,12 @@ - - + + - {{ deviceInfo.dock.network_state.rate }} KB/S + {{ deviceInfo.dock.network_state?.rate }} KB/S @@ -199,13 +199,13 @@ - - + + @@ -264,8 +264,8 @@ - - 远程调试 + + 设备操作 @@ -840,6 +840,7 @@ export default defineComponent({ EDockModeCode, controlPanelVisible, dockDebugOnOff, + setControlPanelVisible, } } }) diff --git a/src/components/devices/device-log/DeviceLogDetailModal.vue b/src/components/devices/device-log/DeviceLogDetailModal.vue index c203ce3..9f36302 100644 --- a/src/components/devices/device-log/DeviceLogDetailModal.vue +++ b/src/components/devices/device-log/DeviceLogDetailModal.vue @@ -8,7 +8,7 @@
- + 下载机场日志
- + 下载飞行器日志 +
+
设备属性设置
+
+ +
+
+
{{ deviceSetting[DeviceSettingKeyEnum.NIGHT_LIGHTS_MODE_SET].label }}
+
{{ deviceSetting[DeviceSettingKeyEnum.NIGHT_LIGHTS_MODE_SET].value }}
+
+
+ + + Edit + +
+
+ +
+
+
{{ deviceSetting[DeviceSettingKeyEnum.HEIGHT_LIMIT_SET].label }}
+
{{ deviceSetting[DeviceSettingKeyEnum.HEIGHT_LIMIT_SET].value }}
+
+
+ + + Edit + +
+
+ +
+
+
{{ deviceSetting[DeviceSettingKeyEnum.DISTANCE_LIMIT_SET].label }}
+
{{ deviceSetting[DeviceSettingKeyEnum.DISTANCE_LIMIT_SET].value }}
+
+
+ + + Edit + +
+
+ +
+
+
{{ deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_HORIZON].label }}
+
{{ deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_HORIZON].value }}
+
+
+ + + Edit + +
+
+ +
+
+
{{ deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_UPSIDE].label }}
+
{{ deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_UPSIDE].value }}
+
+
+ + + Edit + +
+
+ +
+
+
{{ deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_DOWNSIDE].label }}
+
{{ deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_DOWNSIDE].value }}
+
+
+ + + Edit + +
+
+
+
+ + + + + diff --git a/src/components/g-map/DeviceSettingPopover.vue b/src/components/g-map/DeviceSettingPopover.vue new file mode 100644 index 0000000..72e0b0f --- /dev/null +++ b/src/components/g-map/DeviceSettingPopover.vue @@ -0,0 +1,106 @@ + + + + + diff --git a/src/components/g-map/DockControlPanel.vue b/src/components/g-map/DockControlPanel.vue index 284d9ca..058141f 100644 --- a/src/components/g-map/DockControlPanel.vue +++ b/src/components/g-map/DockControlPanel.vue @@ -2,24 +2,32 @@
- 远程调试 {{ props.sn}} + 设备操作 {{ props.sn}}
+ +
-
-
-
{{ cmdItem.label }}
-
{{ cmdItem.status }}
-
-
- - {{ cmdItem.operateText }} - -
-
+
+ 远程调试 + +
+
+
+
+
{{ cmdItem.label }}
+
{{ cmdItem.status }}
+
+
+ + {{ cmdItem.operateText }} + +
+
+
@@ -35,6 +43,7 @@ import { DeviceInfoType } from '/@/types/device' import { cmdList as baseCmdList, DeviceCmdItem } from '/@/types/device-cmd' import { useMyStore } from '/@/store' import { updateDeviceCmdInfoByOsd, updateDeviceCmdInfoByExecuteInfo } from '/@/utils/device-cmd' +import DeviceSettingBox from './DeviceSettingBox.vue' const props = defineProps<{ sn: string, @@ -71,8 +80,27 @@ function closeControlPanel () { } // dock 控制指令 +const debugStatus = ref(false) + +async function onDeviceStatusChange (status: boolean) { + let result = false + if (status) { + result = await dockDebugOnOff(props.sn, true) + } else { + result = await dockDebugOnOff(props.sn, false) + } + if (!result) { + if (status) { + debugStatus.value = false + } else { + debugStatus.value = true + } + } +} + const { sendDockControlCmd, + dockDebugOnOff } = useDockControl() async function sendControlCmd (cmdItem: DeviceCmdItem, index: number) { @@ -103,26 +131,39 @@ async function sendControlCmd (cmdItem: DeviceCmdItem, index: number) { } .control-cmd-wrapper{ - display: flex; - flex-wrap: wrap; - justify-content: space-between; - padding: 4px 10px; - .control-cmd-item{ - width: 220px; - height: 58px; + .control-cmd-header{ + font-size: 14px; + font-weight: 600; + padding: 10px 10px 0px; + + .debug-btn{ + margin-left: 10px; + border:1px solid #585858; + } + } + + .control-cmd-box{ display: flex; - align-items: center; + flex-wrap: wrap; justify-content: space-between; - border: 1px solid #666; - margin: 4px 0; - padding: 0 8px; - - .control-cmd-item-left{ + padding: 4px 10px; + .control-cmd-item{ + width: 220px; + height: 58px; display: flex; - flex-direction: column; - - .item-label{ - font-weight: 700; + align-items: center; + justify-content: space-between; + border: 1px solid #666; + margin: 4px 0; + padding: 0 8px; + + .control-cmd-item-left{ + display: flex; + flex-direction: column; + + .item-label{ + font-weight: 700; + } } } } diff --git a/src/components/g-map/useDeviceSetting.ts b/src/components/g-map/useDeviceSetting.ts new file mode 100644 index 0000000..e2cea30 --- /dev/null +++ b/src/components/g-map/useDeviceSetting.ts @@ -0,0 +1,56 @@ +import { message } from 'ant-design-vue' +import { putDeviceProps, PutDevicePropsBody } from '/@/api/device-setting' +import { DeviceSettingKeyEnum, DeviceSettingFormModel, ObstacleAvoidanceStatusEnum, NightLightsStateEnum, DistanceLimitStatusEnum } from '/@/types/device-setting' + +export function useDeviceSetting () { + // 生成参数 + function genDevicePropsBySettingKey (key: DeviceSettingKeyEnum, fromModel: DeviceSettingFormModel) { + const body = {} as PutDevicePropsBody + if (key === DeviceSettingKeyEnum.NIGHT_LIGHTS_MODE_SET) { + body.night_lights_state = fromModel.nightLightsState ? NightLightsStateEnum.OPEN : NightLightsStateEnum.CLOSE + } else if (key === DeviceSettingKeyEnum.HEIGHT_LIMIT_SET) { + body.height_limit = fromModel.heightLimit + } else if (key === DeviceSettingKeyEnum.DISTANCE_LIMIT_SET) { + body.distance_limit_status = {} + if (fromModel.distanceLimitStatus.state) { + body.distance_limit_status.state = DistanceLimitStatusEnum.SET + body.distance_limit_status.distance_limit = fromModel.distanceLimitStatus.distanceLimit + } else { + body.distance_limit_status.state = DistanceLimitStatusEnum.UNSET + } + } else if (key === DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_HORIZON) { + body.obstacle_avoidance = { + horizon: fromModel.obstacleAvoidanceHorizon ? ObstacleAvoidanceStatusEnum.OPEN : ObstacleAvoidanceStatusEnum.CLOSE + } + } else if (key === DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_UPSIDE) { + body.obstacle_avoidance = { + upside: fromModel.obstacleAvoidanceUpside ? ObstacleAvoidanceStatusEnum.OPEN : ObstacleAvoidanceStatusEnum.CLOSE + } + } else if (key === DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_DOWNSIDE) { + body.obstacle_avoidance = { + downside: fromModel.obstacleAvoidanceDownside ? ObstacleAvoidanceStatusEnum.OPEN : ObstacleAvoidanceStatusEnum.CLOSE + } + } + return body + } + + // 设置设备属性 + async function setDeviceProps (sn: string, body: PutDevicePropsBody) { + try { + const { code, message: msg } = await putDeviceProps(sn, body) + if (code === 0) { + // message.success('指令发送成功') + return true + } + throw (msg) + } catch (e) { + message.error('设备属性设置失败') + return false + } + } + + return { + genDevicePropsBySettingKey, + setDeviceProps + } +} diff --git a/src/components/g-map/useDockControl.ts b/src/components/g-map/useDockControl.ts index 94e302f..f97704d 100644 --- a/src/components/g-map/useDockControl.ts +++ b/src/components/g-map/useDockControl.ts @@ -12,13 +12,14 @@ export function useDockControl () { // 远程调试开关 async function dockDebugOnOff (sn: string, on: boolean) { - const success = await sendDockControlCmd({ + const result = await sendDockControlCmd({ sn: sn, cmd: on ? DeviceCmd.DebugModeOpen : DeviceCmd.DebugModeClose }, false) - if (success) { + if (result) { setControlPanelVisible(on) } + return result } // 发送指令 diff --git a/src/components/livestream-agora.vue b/src/components/livestream-agora.vue index 1860cce..4fec74a 100644 --- a/src/components/livestream-agora.vue +++ b/src/components/livestream-agora.vue @@ -298,6 +298,11 @@ const onStart = async () => { }) } const onStop = async () => { + livePara.videoId = + dronePara.droneSelected + + '/' + + dronePara.cameraSelected + '/' + (dronePara.videoSelected || nonSwitchable + '-0') + stopLivestream({ video_id: livePara.videoId }).then(res => { diff --git a/src/components/livestream-others.vue b/src/components/livestream-others.vue index 559db50..16b0bb3 100644 --- a/src/components/livestream-others.vue +++ b/src/components/livestream-others.vue @@ -318,6 +318,9 @@ const onStart = async () => { }) } const onStop = () => { + videoId.value = + droneSelected.value + '/' + cameraSelected.value + '/' + (videoSelected.value || nonSwitchable + '-0') + stopLivestream({ video_id: videoId.value }).then(res => { diff --git a/src/hooks/use-g-map-tsa.ts b/src/hooks/use-g-map-tsa.ts index 213e3b4..fe3a398 100644 --- a/src/hooks/use-g-map-tsa.ts +++ b/src/hooks/use-g-map-tsa.ts @@ -41,7 +41,11 @@ export function deviceTsaUpdate () { } function initMarker (type: string, name: string, sn: string, lng?: number, lat?: number) { - if (markers[sn] || AMap === undefined) { + if (AMap === undefined) { + location.reload() + return + } + if (markers[sn]) { return } markers[sn] = new AMap.Marker({ diff --git a/src/pages/page-pilot/pilot-home.vue b/src/pages/page-pilot/pilot-home.vue index 9edd096..1adb0d9 100644 --- a/src/pages/page-pilot/pilot-home.vue +++ b/src/pages/page-pilot/pilot-home.vue @@ -252,6 +252,12 @@ onMounted(() => { apiPilot.onBackClickReg() apiPilot.onStopPlatform() + window.connectCallback = arg => { + connectCallback(arg) + } + window.wsConnectCallback = arg => { + wsConnectCallback(arg) + } device.data.gateway_sn = apiPilot.getRemoteControllerSN() if (device.data.gateway_sn === EStatusValue.DISCONNECT.toString()) { message.warn('Data is not available, please restart the remote control.') @@ -296,12 +302,6 @@ onMounted(() => { bindParam.user_id = res.data.user_id bindParam.workspace_id = res.data.workspace_id }) - window.connectCallback = arg => { - connectCallback(arg) - } - window.wsConnectCallback = arg => { - wsConnectCallback(arg) - } }) const connectCallback = async (arg: any) => { diff --git a/src/pages/page-web/projects/wayline.vue b/src/pages/page-web/projects/wayline.vue index b64e8e8..6fe5083 100644 --- a/src/pages/page-web/projects/wayline.vue +++ b/src/pages/page-web/projects/wayline.vue @@ -3,12 +3,24 @@
- Flight Route Library - + Flight Route Library + + + + + + +
- +
@@ -72,14 +84,16 @@ import { reactive } from '@vue/reactivity' import { message } from 'ant-design-vue' import { onMounted, onUpdated, ref } from 'vue' -import { deleteWaylineFile, downloadWaylineFile, getWaylineFiles } from '/@/api/wayline' +import { deleteWaylineFile, downloadWaylineFile, getWaylineFiles, importKmzFile } from '/@/api/wayline' import { ELocalStorageKey } from '/@/types' -import { EllipsisOutlined, RocketOutlined, CameraFilled, UserOutlined } from '@ant-design/icons-vue' +import { EllipsisOutlined, RocketOutlined, CameraFilled, UserOutlined, SelectOutlined } from '@ant-design/icons-vue' import { EDeviceType } from '/@/types/device' import { useMyStore } from '/@/store' import { WaylineFile } from '/@/types/wayline' import { downloadFile } from '/@/utils/common' import { IPage } from '/@/api/http/type' +import { CURRENT_CONFIG } from '/@/api/http/config' +import { load } from '@amap/amap-jsapi-loader' const loading = ref(false) const store = useMyStore() @@ -97,15 +111,24 @@ const workspaceId = localStorage.getItem(ELocalStorageKey.WorkspaceId)! const deleteTip = ref(false) const deleteWaylineId = ref('') const canRefresh = ref(true) +const importVisible = ref(false) +const urlUpload = `${CURRENT_CONFIG.baseURL}wayline/api/v1/workspaces/${workspaceId}/waylines/file/upload` onMounted(() => { getWaylines() + setTimeout(() => { + const element = document.getElementsByClassName('scrollbar').item(0) as HTMLDivElement + const parent = element?.parentNode as HTMLDivElement + console.info(element, parent) + // console.info(element.scrollHeight, parent.clientHeight) + }, 1000) }) onUpdated(() => { const element = document.getElementsByClassName('scrollbar').item(0) as HTMLDivElement const parent = element?.parentNode as HTMLDivElement setTimeout(() => { + console.info(element, parent) if (element?.scrollHeight < parent?.clientHeight && pagination.total > waylinesData.data.length) { if (canRefresh.value) { pagination.page++ @@ -130,6 +153,7 @@ function getWaylines () { if (res.code !== 0) { return } + waylinesData.data = [] res.data.list.forEach((wayline: WaylineFile) => waylinesData.data.push(wayline)) pagination.total = res.data.pagination.total pagination.page = res.data.pagination.page @@ -151,8 +175,7 @@ function deleteWayline () { deleteWaylineId.value = '' deleteTip.value = false pagination.total-- - waylinesData.data = [] - setTimeout(getWaylines, 500) + getWaylines() }) } @@ -175,12 +198,50 @@ function selectRoute (wayline: WaylineFile) { function onScroll (e: any) { const element = e.srcElement + console.info(element) if (element.scrollTop + element.clientHeight === element.scrollHeight && Math.ceil(pagination.total / pagination.page_size) > pagination.page && canRefresh.value) { pagination.page++ getWaylines() } } +interface FileItem { + uid: string; + name?: string; + status?: string; + response?: string; + url?: string; +} + +interface FileInfo { + file: FileItem; + fileList: FileItem[]; +} +const fileList = ref([]) + +function beforeUpload (file: FileItem) { + fileList.value = [file] + loading.value = true + return true +} +const uploadFile = async () => { + console.info(loading.value) + fileList.value.forEach(async (file: FileItem) => { + const fileData = new FormData() + fileData.append('file', file, file.name) + await importKmzFile(workspaceId, fileData).then((res) => { + if (res.code === 0) { + message.success(`${file.name} file uploaded successfully`) + canRefresh.value = true + getWaylines() + } + }).finally(() => { + loading.value = false + fileList.value = [] + }) + }) +} +