Update v1.2.0
Esse commit está contido em:
gerado
+11
@@ -15,6 +15,7 @@
|
||||
"agora-rtc-sdk-ng": "^4.12.1",
|
||||
"ant-design-vue": "^2.2.8",
|
||||
"axios": "^0.21.1",
|
||||
"mitt": "^3.0.0",
|
||||
"query-string": "^7.0.1",
|
||||
"reconnecting-websocket": "^4.4.0",
|
||||
"vconsole": "^3.8.1",
|
||||
@@ -4901,6 +4902,11 @@
|
||||
"integrity": "sha1-Z9ZgFLZqaoqqDAg8X9WN9OTpdgI=",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mitt": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.0.tgz",
|
||||
"integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ=="
|
||||
},
|
||||
"node_modules/mixin-deep": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npm.taobao.org/mixin-deep/download/mixin-deep-1.3.2.tgz",
|
||||
@@ -11340,6 +11346,11 @@
|
||||
"resolved": "https://registry.npm.taobao.org/minimist/download/minimist-1.2.5.tgz",
|
||||
"integrity": "sha1-Z9ZgFLZqaoqqDAg8X9WN9OTpdgI="
|
||||
},
|
||||
"mitt": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.0.tgz",
|
||||
"integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ=="
|
||||
},
|
||||
"mixin-deep": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npm.taobao.org/mixin-deep/download/mixin-deep-1.3.2.tgz",
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"agora-rtc-sdk-ng": "^4.12.1",
|
||||
"ant-design-vue": "^2.2.8",
|
||||
"axios": "^0.21.1",
|
||||
"mitt": "^3.0.0",
|
||||
"query-string": "^7.0.1",
|
||||
"reconnecting-websocket": "^4.4.0",
|
||||
"vconsole": "^3.8.1",
|
||||
@@ -90,9 +91,11 @@
|
||||
"ant-design-vue/es/spin/style/css",
|
||||
"ant-design-vue/es/switch/style/css",
|
||||
"ant-design-vue/es/table/style/css",
|
||||
"ant-design-vue/es/tag/style/css",
|
||||
"ant-design-vue/es/tooltip/style/css",
|
||||
"ant-design-vue/es/tree/style/css",
|
||||
"axios",
|
||||
"mitt",
|
||||
"moment",
|
||||
"reconnecting-websocket",
|
||||
"vconsole",
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import request, { IWorkspaceResponse } from '/@/api/http/request'
|
||||
import { DeviceCmd } from '/@/types/device-cmd'
|
||||
|
||||
const CMD_API_PREFIX = '/control/api/v1'
|
||||
|
||||
export interface SendCmdParams {
|
||||
dock_sn: string, // 机场cn
|
||||
device_cmd: DeviceCmd // 指令
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送机场控制指令
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
// /control/api/v1/devices/{dock_sn}/jobs/{service_identifier}
|
||||
export async function postSendCmd (params: SendCmdParams): Promise<IWorkspaceResponse<{}>> {
|
||||
const resp = await request.post(`${CMD_API_PREFIX}/devices/${params.dock_sn}/jobs/${params.device_cmd}`)
|
||||
return resp.data
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
import request, { IWorkspaceResponse, IListWorkspaceResponse } from '/@/api/http/request'
|
||||
import { DeviceValue, DOMAIN } from '/@/types/device'
|
||||
import { DeviceLogUploadStatusEnum } from '/@/types/device-log'
|
||||
import { ELocalStorageKey } from '/@/types'
|
||||
import { CURRENT_CONFIG } from '/@/api/http/config'
|
||||
|
||||
const MNG_API_PREFIX = '/manage/api/v1'
|
||||
|
||||
const workspaceId: string = localStorage.getItem(ELocalStorageKey.WorkspaceId) || ''
|
||||
|
||||
export interface GetDeviceUploadLogListParams {
|
||||
device_sn: string,
|
||||
page: number,
|
||||
page_size: number,
|
||||
begin_time?: number, // 开始时间
|
||||
end_time?: number, // 结束时间
|
||||
status?: DeviceLogUploadStatusEnum, // 日志上传状态
|
||||
logs_information?: string // 搜索内容
|
||||
}
|
||||
|
||||
export interface BriefDeviceInfo {
|
||||
sn: string,
|
||||
device_model: DeviceValue,
|
||||
device_callsign: string
|
||||
}
|
||||
|
||||
export interface DeviceLogProgressInfo{
|
||||
device_sn: string,
|
||||
device_model_domain: DOMAIN,
|
||||
progress: number, // 进度
|
||||
result: number, // 上传结果
|
||||
upload_rate: number, // 上传速率
|
||||
status: DeviceLogUploadStatusEnum // 上传状态
|
||||
}
|
||||
|
||||
export interface DeviceLogItem {
|
||||
boot_index: number, // 日志id
|
||||
start_time: number, // 日志开始时间
|
||||
end_time: number, // 日志结束时间
|
||||
size: number // 日志大小
|
||||
}
|
||||
|
||||
export interface DeviceLogFileInfo {
|
||||
device_sn: string,
|
||||
module: DOMAIN,
|
||||
result: number,
|
||||
object_key: string,
|
||||
file_id: string,
|
||||
list: DeviceLogItem[]
|
||||
}
|
||||
|
||||
export interface DeviceLogFileListInfo {
|
||||
files: DeviceLogFileInfo[]
|
||||
}
|
||||
|
||||
export interface GetDeviceUploadLogListRsp {
|
||||
logs_id: string, // 记录id
|
||||
happen_time: string, // 发生时间
|
||||
user_name: string, // 用户
|
||||
logs_information: string, // 异常描述
|
||||
create_time: string, // 上传时间
|
||||
status:DeviceLogUploadStatusEnum, // 日志上传状态
|
||||
device_topo:{ // 设备topo
|
||||
hosts: BriefDeviceInfo[],
|
||||
parents: BriefDeviceInfo[]
|
||||
},
|
||||
logs_progress: DeviceLogProgressInfo[], // 日志上传进度
|
||||
device_logs: DeviceLogFileListInfo // 设备日志
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备上传日志列表信息
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export async function getDeviceUploadLogList (params: GetDeviceUploadLogListParams): Promise<IListWorkspaceResponse<GetDeviceUploadLogListRsp>> {
|
||||
const resp = await request.get(`${MNG_API_PREFIX}/workspaces/${workspaceId}/devices/${params.device_sn}/logs-uploaded`, {
|
||||
params: params
|
||||
})
|
||||
return resp.data
|
||||
}
|
||||
|
||||
export interface GetDeviceLogListParams{
|
||||
device_sn: string,
|
||||
domain: DOMAIN[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备日志列表信息
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export async function getDeviceLogList (params: GetDeviceLogListParams): Promise<IWorkspaceResponse<DeviceLogFileListInfo>> {
|
||||
const domain = params.domain ? params.domain : []
|
||||
const resp = await request.get(`${MNG_API_PREFIX}/workspaces/${workspaceId}/devices/${params.device_sn}/logs`, {
|
||||
params: {
|
||||
domain_list: domain.join(',')
|
||||
}
|
||||
})
|
||||
return resp.data
|
||||
}
|
||||
|
||||
export interface UploadDeviceLogBody {
|
||||
device_sn: string
|
||||
happen_time: string // 发生时间
|
||||
logs_information: string // 异常描述
|
||||
files:{
|
||||
list: DeviceLogItem[],
|
||||
device_sn: string,
|
||||
module: DOMAIN
|
||||
}[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备日志
|
||||
* @param body
|
||||
* @returns
|
||||
*/
|
||||
export async function postDeviceUpgrade (body: UploadDeviceLogBody): Promise<IWorkspaceResponse<{}>> {
|
||||
const resp = await request.post(`${MNG_API_PREFIX}/workspaces/${workspaceId}/devices/${body.device_sn}/logs`, body)
|
||||
return resp.data
|
||||
}
|
||||
|
||||
export type DeviceLogUploadAction = 'cancel'
|
||||
|
||||
export interface CancelDeviceLogUploadBody {
|
||||
device_sn: string
|
||||
status: DeviceLogUploadAction
|
||||
module_list: DOMAIN[]
|
||||
}
|
||||
|
||||
// 取消上传
|
||||
export async function cancelDeviceLogUpload (body: CancelDeviceLogUploadBody): Promise<IWorkspaceResponse<{}>> {
|
||||
const url = `${MNG_API_PREFIX}/workspaces/${workspaceId}/devices/${body.device_sn}/logs`
|
||||
const result = await request.delete(url, {
|
||||
data: body
|
||||
})
|
||||
return result.data
|
||||
}
|
||||
|
||||
export interface DeleteDeviceLogUploadBody {
|
||||
device_sn: string
|
||||
logs_id: string
|
||||
}
|
||||
|
||||
// 取消上传
|
||||
export async function deleteDeviceLogUpload (body: DeleteDeviceLogUploadBody): Promise<IWorkspaceResponse<{}>> {
|
||||
const url = `${MNG_API_PREFIX}/workspaces/${workspaceId}/devices/${body.device_sn}/logs/${body.logs_id}`
|
||||
const result = await request.delete(url, {
|
||||
data: body
|
||||
})
|
||||
return result.data
|
||||
}
|
||||
|
||||
export interface GetUploadDeviceLogUrlParams{
|
||||
logs_id: string,
|
||||
file_id: string,
|
||||
}
|
||||
|
||||
// export interface GetUploadDeviceLogRsp{
|
||||
// url: string
|
||||
// }
|
||||
|
||||
/**
|
||||
* 获取设备上传日志url
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export async function getUploadDeviceLogUrl (params: GetUploadDeviceLogUrlParams): Promise<IWorkspaceResponse<string>> {
|
||||
const resp = await request.get(`${MNG_API_PREFIX}/workspaces/${workspaceId}/logs/${params.logs_id}/url/${params.file_id}`)
|
||||
return resp.data
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import request, { IWorkspaceResponse } from '/@/api/http/request'
|
||||
import { DeviceFirmwareTypeEnum } from '/@/types/device'
|
||||
|
||||
const MNG_API_PREFIX = '/manage/api/v1'
|
||||
|
||||
export interface GetDeviceUpgradeInfoParams {
|
||||
device_name: string
|
||||
}
|
||||
|
||||
export interface GetDeviceUpgradeInfoRsp {
|
||||
device_name: string
|
||||
product_version: string
|
||||
release_note: string
|
||||
released_time: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备升级信息
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export async function getDeviceUpgradeInfo (params: GetDeviceUpgradeInfoParams): Promise<IWorkspaceResponse<GetDeviceUpgradeInfoRsp[]>> {
|
||||
const resp = await request.get(`${MNG_API_PREFIX}/workspaces/firmware-release-notes/latest`, {
|
||||
params: params
|
||||
})
|
||||
return resp.data
|
||||
}
|
||||
|
||||
export interface UpgradeDeviceInfo {
|
||||
device_name: string,
|
||||
sn: string,
|
||||
product_version: string,
|
||||
firmware_upgrade_type: DeviceFirmwareTypeEnum // 1-普通升级,2-一致性升级
|
||||
}
|
||||
|
||||
export type DeviceUpgradeBody = UpgradeDeviceInfo[]
|
||||
|
||||
/**
|
||||
* 设备升级
|
||||
* @param workspace_id
|
||||
* @param body
|
||||
* @returns
|
||||
*/
|
||||
export async function postDeviceUpgrade (workspace_id: string, body: DeviceUpgradeBody): Promise<IWorkspaceResponse<{}>> {
|
||||
const resp = await request.post(`${MNG_API_PREFIX}/devices/${workspace_id}/devices/ota`, body)
|
||||
return resp.data
|
||||
}
|
||||
@@ -19,11 +19,11 @@ export interface IListWorkspaceResponse<T> {
|
||||
}
|
||||
// Workspace
|
||||
export interface IWorkspaceResponse<T> {
|
||||
[x: string]: number;
|
||||
code: number;
|
||||
data: T;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export type IStatus = 'WAITING' | 'DOING' | 'SUCCESS' | 'FAILED';
|
||||
|
||||
export interface CommonListResponse<T> extends IResult {
|
||||
|
||||
+13
-4
@@ -1,4 +1,6 @@
|
||||
import request, { CommonListResponse, IListWorkspaceResponse, IPage, IWorkspaceResponse } from '/@/api/http/request'
|
||||
import { Device } from '/@/types/device'
|
||||
|
||||
const HTTP_PREFIX = '/manage/api/v1'
|
||||
|
||||
// login
|
||||
@@ -116,7 +118,14 @@ export const getDeviceBySn = async function (workspace_id: string, device_sn: st
|
||||
return result.data
|
||||
}
|
||||
|
||||
export const getBindingDevices = async function (workspace_id: string, body: IPage, domain: string): Promise<IWorkspaceResponse<any>> {
|
||||
/**
|
||||
* 获取绑定设备信息
|
||||
* @param workspace_id
|
||||
* @param body
|
||||
* @param domain
|
||||
* @returns
|
||||
*/
|
||||
export const getBindingDevices = async function (workspace_id: string, body: IPage, domain: string): Promise<IListWorkspaceResponse<Device>> {
|
||||
const url = `${HTTP_PREFIX}/devices/${workspace_id}/devices/bound?&page=${body.page}&page_size=${body.page_size}&domain=${domain}`
|
||||
const result = await request.get(url)
|
||||
return result.data
|
||||
@@ -141,11 +150,11 @@ export const updateDeviceHms = async function (workspace_id: string, device_sn:
|
||||
}
|
||||
|
||||
export const getDeviceHms = async function (body: HmsQueryBody, workspace_id: string, pagination: IPage): Promise<IListWorkspaceResponse<any>> {
|
||||
let url = `${HTTP_PREFIX}/devices/${workspace_id}/devices/hms?page=${pagination.page}&pageSize=${pagination.page_size}` +
|
||||
`&level=${body.level ?? ''}&beginTime=${body.begin_time ?? ''}&endTime=${body.end_time ?? ''}&message=${body.message ?? ''}&language=${body.language}`
|
||||
let url = `${HTTP_PREFIX}/devices/${workspace_id}/devices/hms?page=${pagination.page}&page_size=${pagination.page_size}` +
|
||||
`&level=${body.level ?? ''}&begin_time=${body.begin_time ?? ''}&end_time=${body.end_time ?? ''}&message=${body.message ?? ''}&language=${body.language}`
|
||||
body.sns.forEach((sn: string) => {
|
||||
if (sn !== '') {
|
||||
url = url.concat(`&deviceSn=${sn}`)
|
||||
url = url.concat(`&device_sn=${sn}`)
|
||||
}
|
||||
})
|
||||
const result = await request.get(url)
|
||||
|
||||
+13
-4
@@ -1,3 +1,4 @@
|
||||
import { message } from 'ant-design-vue'
|
||||
import request, { IPage, IWorkspaceResponse } from '/@/api/http/request'
|
||||
const HTTP_PREFIX = '/media/api/v1'
|
||||
|
||||
@@ -8,11 +9,19 @@ export const getMediaFiles = async function (wid: string, pagination: IPage): Pr
|
||||
return result.data
|
||||
}
|
||||
// Download Media File
|
||||
export const downloadMediaFile = async function (workspaceId: string, fingerprint: string): Promise<any> {
|
||||
const url = `${HTTP_PREFIX}/files/${workspaceId}/file/${fingerprint}/url`
|
||||
export const downloadMediaFile = async function (workspaceId: string, fileId: string): Promise<any> {
|
||||
const url = `${HTTP_PREFIX}/files/${workspaceId}/file/${fileId}/url`
|
||||
const result = await request.get(url, { responseType: 'blob' })
|
||||
if (result.data.code) {
|
||||
if (result.data.type === 'application/json') {
|
||||
const reader = new FileReader()
|
||||
reader.onload = function (e) {
|
||||
let text = reader.result as string
|
||||
const result = JSON.parse(text)
|
||||
message.error(result.message)
|
||||
}
|
||||
reader.readAsText(result.data, 'utf-8')
|
||||
return
|
||||
} else {
|
||||
return result.data
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
+11
-2
@@ -1,3 +1,4 @@
|
||||
import { message } from 'ant-design-vue'
|
||||
import request, { IPage, IWorkspaceResponse } from '/@/api/http/request'
|
||||
const HTTP_PREFIX = '/wayline/api/v1'
|
||||
|
||||
@@ -20,10 +21,18 @@ export const getWaylineFiles = async function (wid: string, body: {}): Promise<I
|
||||
export const downloadWaylineFile = async function (workspaceId: string, waylineId: string): Promise<any> {
|
||||
const url = `${HTTP_PREFIX}/workspaces/${workspaceId}/waylines/${waylineId}/url`
|
||||
const result = await request.get(url, { responseType: 'blob' })
|
||||
if (result.data.code) {
|
||||
if (result.data.type === 'application/json') {
|
||||
const reader = new FileReader()
|
||||
reader.onload = function (e) {
|
||||
let text = reader.result as string
|
||||
const result = JSON.parse(text)
|
||||
message.error(result.message)
|
||||
}
|
||||
reader.readAsText(result.data, 'utf-8')
|
||||
return
|
||||
} else {
|
||||
return result.data
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Delete Wayline File
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
import ReconnectingWebSocket from 'reconnecting-websocket'
|
||||
import { ELocalStorageKey } from '../types/enums'
|
||||
import { CURRENT_CONFIG as config } from '/@/api/http/config'
|
||||
|
||||
let socket: ReconnectingWebSocket
|
||||
|
||||
export default {
|
||||
init (getMsgFunc: any) {
|
||||
const token: string = localStorage.getItem(ELocalStorageKey.Token)!
|
||||
const wspath = config.websocketURL + '?x-auth-token=' + encodeURI(token)
|
||||
socket = new ReconnectingWebSocket(wspath, '', { maxRetries: 5 })
|
||||
socket.onopen = this.onOpen
|
||||
socket.onerror = this.onError
|
||||
socket.onmessage = getMsgFunc
|
||||
socket.onclose = this.onClose
|
||||
return socket
|
||||
},
|
||||
onOpen () {
|
||||
console.log('ws opened')
|
||||
},
|
||||
onError (err: any) {
|
||||
console.error(err)
|
||||
},
|
||||
onClose () {
|
||||
console.log('ws closed')
|
||||
},
|
||||
sendMsg (data: any) {
|
||||
socket.send(data)
|
||||
},
|
||||
close () {
|
||||
socket.close()
|
||||
}
|
||||
}
|
||||
+44
-10
@@ -137,21 +137,23 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="osdVisible.visible && osdVisible.is_dock" class="osd-panel fz12" style="height: 280px;">
|
||||
<!-- 机场OSD -->
|
||||
<div v-if="osdVisible.visible && osdVisible.is_dock" class="osd-panel fz12">
|
||||
<div class="fz16 pl5 pr5 flex-align-center flex-row flex-justify-between" style="border-bottom: 1px solid #515151; height: 10%;">
|
||||
<span>{{ osdVisible.gateway_callsign }}</span>
|
||||
<span><a style="color: white;" @click="() => osdVisible.visible = false"><CloseOutlined /></a></span>
|
||||
</div>
|
||||
<div style="height: 45%; border-bottom: 1px solid #515151;">
|
||||
<div class="flex-column flex-align-center flex-justify-center" style="float: left; width: 60px; height: 100%; background: #2d2d2d;">
|
||||
<!-- 机场 -->
|
||||
<div class ="flex-display" style="border-bottom: 1px solid #515151;">
|
||||
<div class="flex-column flex-align-stretch flex-justify-center" style="width: 60px; background: #2d2d2d;">
|
||||
<a-tooltip :title="osdVisible.model">
|
||||
<div class="flex-column flex-align-center flex-justify-center" style="width: 90%;">
|
||||
<div class="flex-column flex-align-center flex-justify-center" style="width: 90%;">
|
||||
<span><RobotFilled style="font-size: 48px;"/></span>
|
||||
<span class="mt10">Dock</span>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<div class="osd">
|
||||
<div class="osd flex-1" style="flex: 1">
|
||||
<a-row>
|
||||
<a-col span="16" :style="deviceInfo.dock.mode_code === EDockModeCode.Disconnected ? 'color: red; font-weight: 700;': 'color: rgb(25,190,107)'">
|
||||
{{ EDockModeCode[deviceInfo.dock.mode_code] }}</a-col>
|
||||
@@ -260,10 +262,20 @@
|
||||
</a-tooltip>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row class="p5">
|
||||
<a-col span="24">
|
||||
<a-button type="primary" :disabled="controlPanelVisible" size="small" @click="dockDebugOnOff(osdVisible.gateway_sn, true)">
|
||||
远程调试
|
||||
</a-button>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<DockControlPanel v-if="controlPanelVisible" :sn="osdVisible.gateway_sn" :deviceInfo="deviceInfo" @close-control-panel="dockDebugOnOff">
|
||||
</DockControlPanel>
|
||||
</div>
|
||||
</div>
|
||||
<div style="height: 45%;">
|
||||
<div class="flex-column flex-align-center flex-justify-center" style="float: left; width: 60px; height: 100%; background: #2d2d2d;">
|
||||
<!-- 飞机-->
|
||||
<div class ="flex-display">
|
||||
<div class="flex-column flex-align-stretch flex-justify-center" style="width: 60px; background: #2d2d2d;">
|
||||
<a-tooltip :title="osdVisible.model">
|
||||
<div style="width: 90%;" class="flex-column flex-align-center flex-justify-center">
|
||||
<span><a-image :src="M30" :preview="false"/></span>
|
||||
@@ -271,7 +283,7 @@
|
||||
</div>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<div class="osd">
|
||||
<div class="osd flex-1">
|
||||
<a-row>
|
||||
<a-col span="16" :style="!deviceInfo.device || deviceInfo.device?.mode_code === EModeCode.Disconnected ? 'color: red; font-weight: 700;': 'color: rgb(25,190,107)'">
|
||||
{{ !deviceInfo.device ? EModeCode[EModeCode.Disconnected] : EModeCode[deviceInfo.device?.mode_code] }}</a-col>
|
||||
@@ -409,6 +421,8 @@ import {
|
||||
FieldTimeOutlined, CloudOutlined, CloudFilled, FolderOpenOutlined, RobotFilled, ArrowUpOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
import { EDeviceTypeName } from '../types'
|
||||
import DockControlPanel from './g-map/DockControlPanel.vue'
|
||||
import { useDockControl } from './g-map/useDockControl'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@@ -428,7 +442,8 @@ export default defineComponent({
|
||||
FolderOpenOutlined,
|
||||
RobotFilled,
|
||||
ArrowUpOutlined,
|
||||
ArrowDownOutlined
|
||||
ArrowDownOutlined,
|
||||
DockControlPanel
|
||||
},
|
||||
name: 'GMap',
|
||||
props: {},
|
||||
@@ -642,6 +657,15 @@ export default defineComponent({
|
||||
useMouseToolHook.mouseTool(type, getDrawCallback)
|
||||
mouseMode.value = bool
|
||||
}
|
||||
|
||||
// dock 控制指令
|
||||
const {
|
||||
controlPanelVisible,
|
||||
setControlPanelVisible,
|
||||
sendDockControlCmd,
|
||||
dockDebugOnOff,
|
||||
} = useDockControl()
|
||||
|
||||
onMounted(() => {
|
||||
const app = getApp()
|
||||
useGMapManageHook.globalPropertiesConfig(app)
|
||||
@@ -813,15 +837,19 @@ export default defineComponent({
|
||||
EModeCode,
|
||||
str,
|
||||
EDockModeCode,
|
||||
controlPanelVisible,
|
||||
dockDebugOnOff,
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.g-map-wrapper {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.g-action-panle {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
@@ -845,13 +873,19 @@ export default defineComponent({
|
||||
border: 1px solid $primary;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
// antd button 光晕
|
||||
&:deep(.ant-btn){
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.osd-panel {
|
||||
position: absolute;
|
||||
left: 350px;
|
||||
top: 10px;
|
||||
width: 480px;
|
||||
height: 160px;
|
||||
background: black;
|
||||
color: white;
|
||||
border-radius: 2px;
|
||||
|
||||
@@ -32,7 +32,7 @@ import { ELocalStorageKey } from '../types/enums'
|
||||
import { downloadFile } from '../utils/common'
|
||||
import { downloadMediaFile, getMediaFiles } from '/@/api/media'
|
||||
import { DownloadOutlined } from '@ant-design/icons-vue'
|
||||
import { Pagination } from 'ant-design-vue'
|
||||
import { message, Pagination } from 'ant-design-vue'
|
||||
import { load } from '@amap/amap-jsapi-loader'
|
||||
|
||||
const workspaceId = localStorage.getItem(ELocalStorageKey.WorkspaceId)!
|
||||
@@ -101,6 +101,7 @@ interface MediaFile {
|
||||
file_name: string,
|
||||
file_path: string,
|
||||
create_time: string,
|
||||
file_id: string,
|
||||
}
|
||||
|
||||
const mediaData = reactive({
|
||||
@@ -128,12 +129,11 @@ function refreshData (page: Pagination) {
|
||||
|
||||
function downloadMedia (media: MediaFile) {
|
||||
loading.value = true
|
||||
downloadMediaFile(workspaceId, media.fingerprint).then(res => {
|
||||
if (res.code && res.code !== 0) {
|
||||
downloadMediaFile(workspaceId, media.file_id).then(res => {
|
||||
if (!res) {
|
||||
return
|
||||
}
|
||||
const suffix = media.file_name.substring(media.file_name.lastIndexOf('.'))
|
||||
const data = new Blob([res.data], { type: suffix === '.mp4' ? 'video/mp4' : 'image/jpeg' })
|
||||
const data = new Blob([res])
|
||||
downloadFile(data, media.file_name)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<template #status="{ record }">
|
||||
<span v-if="taskProgressMap[record.bid]">
|
||||
<a-progress type="line" :percent="taskProgressMap[record.bid]?.progress?.percent"
|
||||
:status="taskProgressMap[record.bid]?.status === ETaskStatus.FAILED ? 'exception' : taskProgressMap[record.bid]?.status === ETaskStatus.OK ? 'success' : 'normal'">
|
||||
:status="taskProgressMap[record.bid]?.status.indexOf(ETaskStatus.FAILED) != -1 ? 'exception' : taskProgressMap[record.bid]?.status.indexOf(ETaskStatus.OK) != -1 ? 'success' : 'normal'">
|
||||
<template #format="percent">
|
||||
<a-tooltip :title="taskProgressMap[record.bid]?.status">
|
||||
<div style="white-space: nowrap; text-overflow: ellipsis; overflow: hidden; position: absolute; left: 5px; top: -12px;">
|
||||
|
||||
@@ -28,7 +28,6 @@ import { createVNode, defineComponent } from 'vue'
|
||||
import { getRoot } from '/@/root'
|
||||
import * as icons from '@ant-design/icons-vue'
|
||||
import { ERouterName } from '/@/types'
|
||||
import websocket from '/@/api/websocket'
|
||||
|
||||
interface IOptions {
|
||||
key: number
|
||||
@@ -58,7 +57,7 @@ export default defineComponent({
|
||||
{ key: 1, label: 'Livestream', path: '/' + ERouterName.LIVESTREAM, icon: 'VideoCameraOutlined' },
|
||||
{ key: 2, label: 'Annotations', path: '/' + ERouterName.LAYER, icon: 'EnvironmentOutlined' },
|
||||
{ key: 3, label: 'Media Files', path: '/' + ERouterName.MEDIA, icon: 'PictureOutlined' },
|
||||
{ key: 4, label: 'Fligth Route Library', path: '/' + ERouterName.WAYLINE, icon: 'NodeIndexOutlined' },
|
||||
{ key: 4, label: 'Flight Route Library', path: '/' + ERouterName.WAYLINE, icon: 'NodeIndexOutlined' },
|
||||
{ key: 5, label: 'Task Plan Library', path: '/' + ERouterName.TASK, icon: 'CalendarOutlined' }
|
||||
]
|
||||
|
||||
@@ -66,10 +65,11 @@ export default defineComponent({
|
||||
const path = typeof item.path === 'string' ? item.path : item.path.path
|
||||
return root.$route.path?.indexOf(path) === 0
|
||||
}
|
||||
|
||||
function goHome () {
|
||||
root.$router.push('/' + ERouterName.MEMBERS)
|
||||
websocket.close()
|
||||
}
|
||||
|
||||
return {
|
||||
options,
|
||||
selectedRoute,
|
||||
@@ -44,8 +44,6 @@ import { getPlatformInfo } from '/@/api/manage'
|
||||
import { ELocalStorageKey, ERouterName } from '/@/types'
|
||||
import { UserOutlined, ExportOutlined } from '@ant-design/icons-vue'
|
||||
import cloudapi from '/@/assets/icons/cloudapi.png'
|
||||
import ReconnectingWebSocket from 'reconnecting-websocket'
|
||||
import websocket from '/@/api/websocket'
|
||||
|
||||
const root = getRoot()
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
<template>
|
||||
<a-drawer
|
||||
title="Hms Info"
|
||||
placement="right"
|
||||
v-model:visible="sVisible"
|
||||
@update:visible="onVisibleChange"
|
||||
:destroyOnClose="true"
|
||||
:width="800">
|
||||
<div class="flex-row flex-align-center">
|
||||
<div style="width: 240px;">
|
||||
<a-range-picker
|
||||
v-model:value="time"
|
||||
format="YYYY-MM-DD"
|
||||
:placeholder="['Start Time', 'End Time']"
|
||||
@change="onTimeChange"/>
|
||||
</div>
|
||||
<div class="ml5">
|
||||
<a-select
|
||||
style="width: 150px"
|
||||
v-model:value="param.level"
|
||||
@select="onLevelSelect">
|
||||
<a-select-option
|
||||
v-for="item in levels"
|
||||
:key="item.label"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</div>
|
||||
<div class="ml5">
|
||||
<a-select
|
||||
v-model:value="param.domain"
|
||||
:disabled="!param.children_sn || !param.device_sn"
|
||||
style="width: 150px"
|
||||
@select="onDeviceTypeSelect">
|
||||
<a-select-option
|
||||
v-for="item in deviceTypes"
|
||||
:key="item.label"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</div>
|
||||
<div class="ml5">
|
||||
<a-input-search
|
||||
v-model:value="param.message"
|
||||
placeholder="input search message"
|
||||
style="width: 200px"
|
||||
@search="getHms"/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<a-table :columns="hmsColumns" :scroll="{ x: '100%', y: 600 }" :data-source="hmsData.data" :pagination="hmsPaginationProp" @change="refreshHmsData" row-key="hms_id"
|
||||
:rowClassName="rowClassName" :loading="loading">
|
||||
<template #time="{ record }">
|
||||
<div>{{ record.create_time }}</div>
|
||||
<div :style="record.update_time ? '' : record.level === EHmsLevel.CAUTION ? 'color: orange;' :
|
||||
record.level === EHmsLevel.WARN ? 'color: red;' : 'color: #28d445;'">{{ record.update_time ?? 'It is happening...' }}</div>
|
||||
</template>
|
||||
<template #level="{ text }">
|
||||
<div class="flex-row flex-align-center">
|
||||
<div :class="text === EHmsLevel.CAUTION ? 'caution' : text === EHmsLevel.WARN ? 'warn' : 'notice'" style="width: 10px; height: 10px; border-radius: 50%;"></div>
|
||||
<div style="margin-left: 3px;">{{ EHmsLevel[text] }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-for="col in ['code', 'message']" #[col]="{ text }" :key="col">
|
||||
<a-tooltip :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</a-drawer>
|
||||
</template>
|
||||
|
||||
<!-- 暂时只抽取该组件 -->
|
||||
<script lang="ts" setup>
|
||||
import { watchEffect, reactive, ref, defineProps, defineEmits } from 'vue'
|
||||
import { getDeviceHms, HmsQueryBody } from '/@/api/manage'
|
||||
import moment, { Moment } from 'moment'
|
||||
import { ColumnProps, TableState } from 'ant-design-vue/lib/table/interface'
|
||||
import { Device, DeviceHms } from '/@/types/device'
|
||||
import { IPage } from '/@/api/http/type'
|
||||
import { EDeviceTypeName, EHmsLevel, ELocalStorageKey } from '/@/types'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean,
|
||||
device: null | Device,
|
||||
}>()
|
||||
const emit = defineEmits(['update:visible', 'ok', 'cancel'])
|
||||
|
||||
const workspaceId: string = localStorage.getItem(ELocalStorageKey.WorkspaceId) || ''
|
||||
// 健康状态
|
||||
const sVisible = ref(false)
|
||||
|
||||
watchEffect(() => {
|
||||
sVisible.value = props.visible
|
||||
// 显示弹框时,获取设备hms信息
|
||||
if (props.visible) {
|
||||
showHms()
|
||||
}
|
||||
})
|
||||
|
||||
function onVisibleChange (sVisible: boolean) {
|
||||
setVisible(sVisible)
|
||||
}
|
||||
|
||||
function setVisible (v: boolean, e?: Event) {
|
||||
sVisible.value = v
|
||||
emit('update:visible', v, e)
|
||||
}
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const hmsColumns: ColumnProps[] = [
|
||||
{ title: 'Alarm Begin | End Time', dataIndex: 'create_time', width: '25%', className: 'titleStyle', slots: { customRender: 'time' } },
|
||||
{ title: 'Level', dataIndex: 'level', width: '120px', className: 'titleStyle', slots: { customRender: 'level' } },
|
||||
{ title: 'Device', dataIndex: 'domain', width: '12%', className: 'titleStyle' },
|
||||
{ title: 'Error Code', dataIndex: 'key', width: '20%', className: 'titleStyle', slots: { customRender: 'code' } },
|
||||
{ title: 'Hms Message', dataIndex: 'message_en', className: 'titleStyle', ellipsis: true, slots: { customRender: 'message' } },
|
||||
]
|
||||
|
||||
interface DeviceHmsData {
|
||||
data: DeviceHms[]
|
||||
}
|
||||
|
||||
const hmsData = reactive<DeviceHmsData>({
|
||||
data: []
|
||||
})
|
||||
|
||||
type Pagination = TableState['pagination']
|
||||
|
||||
const hmsPaginationProp = reactive({
|
||||
pageSizeOptions: ['20', '50', '100'],
|
||||
showQuickJumper: true,
|
||||
showSizeChanger: true,
|
||||
pageSize: 50,
|
||||
current: 1,
|
||||
total: 0
|
||||
})
|
||||
|
||||
// 获取分页信息
|
||||
function getPaginationBody () {
|
||||
return {
|
||||
page: hmsPaginationProp.current,
|
||||
page_size: hmsPaginationProp.pageSize
|
||||
} as IPage
|
||||
}
|
||||
|
||||
function showHms () {
|
||||
const dock = props.device
|
||||
if (!dock) return
|
||||
if (dock.domain === EDeviceTypeName.Dock) {
|
||||
getDeviceHmsBySn(dock.device_sn, dock.children?.[0].device_sn ?? '')
|
||||
}
|
||||
if (dock.domain === EDeviceTypeName.Aircraft) {
|
||||
param.domain = EDeviceTypeName.Aircraft
|
||||
getDeviceHmsBySn('', dock.device_sn)
|
||||
}
|
||||
}
|
||||
|
||||
function refreshHmsData (page: Pagination) {
|
||||
hmsPaginationProp.current = page?.current!
|
||||
hmsPaginationProp.pageSize = page?.pageSize!
|
||||
getHms()
|
||||
}
|
||||
|
||||
const param = reactive<HmsQueryBody>({
|
||||
sns: [],
|
||||
device_sn: '',
|
||||
children_sn: '',
|
||||
language: 'en',
|
||||
begin_time: new Date(new Date().setDate(new Date().getDate() - 7)).setHours(0, 0, 0, 0),
|
||||
end_time: new Date().setHours(23, 59, 59, 999),
|
||||
domain: '',
|
||||
level: '',
|
||||
message: ''
|
||||
})
|
||||
|
||||
const levels = [
|
||||
{
|
||||
label: 'All',
|
||||
value: ''
|
||||
}, {
|
||||
label: EHmsLevel[0],
|
||||
value: EHmsLevel.NOTICE
|
||||
}, {
|
||||
label: EHmsLevel[1],
|
||||
value: EHmsLevel.CAUTION
|
||||
}, {
|
||||
label: EHmsLevel[2],
|
||||
value: EHmsLevel.WARN
|
||||
}
|
||||
]
|
||||
|
||||
const deviceTypes = [
|
||||
{
|
||||
label: 'All',
|
||||
value: ''
|
||||
}, {
|
||||
label: EDeviceTypeName.Aircraft,
|
||||
value: EDeviceTypeName.Aircraft
|
||||
}, {
|
||||
label: EDeviceTypeName.Dock,
|
||||
value: EDeviceTypeName.Dock
|
||||
}
|
||||
]
|
||||
|
||||
const rowClassName = (record: any, index: number) => {
|
||||
const className = []
|
||||
if ((index & 1) === 0) {
|
||||
className.push('table-striped')
|
||||
}
|
||||
if (record.domain !== EDeviceTypeName.Dock) {
|
||||
className.push('child-row')
|
||||
}
|
||||
return className.toString().replaceAll(',', ' ')
|
||||
}
|
||||
|
||||
const time = ref([moment(param.begin_time), moment(param.end_time)])
|
||||
|
||||
function getHms () {
|
||||
loading.value = true
|
||||
getDeviceHms(param, workspaceId, getPaginationBody())
|
||||
.then(res => {
|
||||
hmsPaginationProp.total = res.data.pagination.total
|
||||
hmsPaginationProp.current = res.data.pagination.page
|
||||
hmsData.data = res.data.list
|
||||
hmsData.data.forEach(hms => {
|
||||
hms.domain = hms.sn === param.children_sn ? EDeviceTypeName.Aircraft : EDeviceTypeName.Dock
|
||||
})
|
||||
loading.value = false
|
||||
}).catch(_err => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function getDeviceHmsBySn (sn: string, childSn: string) {
|
||||
param.device_sn = sn
|
||||
param.children_sn = childSn
|
||||
param.sns = [param.device_sn, param.children_sn]
|
||||
getHms()
|
||||
}
|
||||
|
||||
function onTimeChange (newTime: [Moment, Moment]) {
|
||||
param.begin_time = newTime[0].valueOf()
|
||||
param.end_time = newTime[1].valueOf()
|
||||
getHms()
|
||||
}
|
||||
|
||||
function onDeviceTypeSelect (val: string) {
|
||||
param.sns = [param.device_sn, param.children_sn]
|
||||
if (val === EDeviceTypeName.Dock) {
|
||||
param.sns = [param.device_sn, '']
|
||||
}
|
||||
if (val === EDeviceTypeName.Aircraft) {
|
||||
param.sns = ['', param.children_sn]
|
||||
}
|
||||
getHms()
|
||||
}
|
||||
|
||||
function onLevelSelect (val: number) {
|
||||
param.level = val
|
||||
getHms()
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,150 @@
|
||||
<template>
|
||||
<a-modal
|
||||
title="日志上传详情"
|
||||
v-model:visible="sVisible"
|
||||
width="900px"
|
||||
:footer="null"
|
||||
@update:visible="onVisibleChange">
|
||||
<div class="device-log-detail-wrap">
|
||||
<div class="device-log-list">
|
||||
<div class="log-list-item">
|
||||
<a-button type="primary" class="download-btn" :disabled="!airportTableLogState.logList?.file_id" size="small" @click="onDownloadLog(airportTableLogState.logList.file_id)">
|
||||
下载机场日志
|
||||
</a-button>
|
||||
<a-table :columns="airportLogColumns"
|
||||
:scroll="{ x: '100%', y: 600 }"
|
||||
:data-source="airportTableLogState.logList?.list"
|
||||
rowKey="boot_index"
|
||||
:pagination = "false"
|
||||
>
|
||||
<template #log_time="{record}">
|
||||
<div>{{getLogTime(record)}}</div>
|
||||
</template>
|
||||
<template #size="{record}">
|
||||
<div>{{getLogSize(record.size)}}</div>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
<div class="log-list-item">
|
||||
<a-button type="primary" class="download-btn" :disabled="!droneTableLogState.logList?.file_id" size="small" @click="onDownloadLog(droneTableLogState.logList.file_id)">
|
||||
下载飞行器日志
|
||||
</a-button>
|
||||
<a-table :columns="droneLogColumns"
|
||||
:scroll="{ x: '100%', y: 600 }"
|
||||
:data-source="droneTableLogState.logList?.list"
|
||||
rowKey="boot_index"
|
||||
:pagination = "false"
|
||||
>
|
||||
<template #log_time="{record}">
|
||||
<div>{{getLogTime(record)}}</div>
|
||||
</template>
|
||||
<template #size="{record}">
|
||||
<div>{{getLogSize(record.size)}}</div>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watchEffect, reactive, ref, defineProps, defineEmits } from 'vue'
|
||||
import { ColumnProps, TableState } from 'ant-design-vue/lib/table/interface'
|
||||
import { IPage } from '/@/api/http/type'
|
||||
import { DOMAIN } from '/@/types/device'
|
||||
import { DeviceLogFileInfo, GetDeviceUploadLogListRsp, getUploadDeviceLogUrl } from '/@/api/device-log'
|
||||
import { useDeviceLogUploadDetail } from './use-device-log-upload-detail'
|
||||
import { download } from '/@/utils/download'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean,
|
||||
deviceLog: null | GetDeviceUploadLogListRsp,
|
||||
}>()
|
||||
const emit = defineEmits(['update:visible'])
|
||||
|
||||
const sVisible = ref(false)
|
||||
|
||||
watchEffect(() => {
|
||||
sVisible.value = props.visible
|
||||
if (props.visible) {
|
||||
classifyDeviceLog()
|
||||
}
|
||||
})
|
||||
|
||||
function onVisibleChange (sVisible: boolean) {
|
||||
setVisible(sVisible)
|
||||
}
|
||||
|
||||
function setVisible (v: boolean, e?: Event) {
|
||||
sVisible.value = v
|
||||
emit('update:visible', v, e)
|
||||
}
|
||||
|
||||
// 表格
|
||||
const airportLogColumns: ColumnProps[] = [
|
||||
{ title: '机场日志', dataIndex: 'time', width: '70%', slots: { customRender: 'log_time' } },
|
||||
{ title: '文件大小', dataIndex: 'size', width: '30%', slots: { customRender: 'size' } },
|
||||
]
|
||||
|
||||
const droneLogColumns: ColumnProps[] = [
|
||||
{ title: '飞行器日志', dataIndex: 'time', width: '70%', slots: { customRender: 'log_time' } },
|
||||
{ title: '文件大小', dataIndex: 'size', width: '30%', slots: { customRender: 'size' } },
|
||||
]
|
||||
|
||||
const airportTableLogState = reactive({
|
||||
logList: {} as DeviceLogFileInfo,
|
||||
})
|
||||
|
||||
const droneTableLogState = reactive({
|
||||
logList: {} as DeviceLogFileInfo,
|
||||
})
|
||||
|
||||
function classifyDeviceLog () {
|
||||
if (!props.deviceLog) return
|
||||
const { device_logs } = props.deviceLog
|
||||
const { files } = device_logs || {}
|
||||
if (files && files.length > 0) {
|
||||
files.forEach(file => {
|
||||
if (file.module === DOMAIN.DOCK) {
|
||||
airportTableLogState.logList = file
|
||||
} else if (file.module === DOMAIN.DRONE) {
|
||||
droneTableLogState.logList = file
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const { getLogTime, getLogSize } = useDeviceLogUploadDetail()
|
||||
|
||||
async function onDownloadLog (fileId: string) {
|
||||
const { data } = await getUploadDeviceLogUrl({
|
||||
file_id: fileId,
|
||||
logs_id: props.deviceLog?.logs_id || ''
|
||||
})
|
||||
if (data) {
|
||||
download(data)
|
||||
// download('https:/github.com/dji-sdk/Mobile-SDK-Android-V5/archive/refs/heads/dev-sdk-main.zip')
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.device-log-detail-wrap{
|
||||
|
||||
.device-log-list{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 8px 0;
|
||||
.log-list-item{
|
||||
width: 420px;
|
||||
|
||||
.download-btn{
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
>
|
||||
@@ -0,0 +1,210 @@
|
||||
<template>
|
||||
<a-modal
|
||||
title="设备日志上传"
|
||||
v-model:visible="sVisible"
|
||||
width="900px"
|
||||
:footer="null"
|
||||
@update:visible="onVisibleChange">
|
||||
<div class="device-log-upload-wrap">
|
||||
<div class="page-action-row">
|
||||
<a-button type="primary" :disabled="deviceLogUploadBtnDisabled" @click="uploadDeviceLog">上传日志</a-button>
|
||||
</div>
|
||||
<div class="device-log-list">
|
||||
<div class="log-list-item">
|
||||
<a-table :columns="airportLogColumns"
|
||||
:scroll="{ x: '100%', y: 600 }"
|
||||
:data-source="airportTableLogState.logList?.list"
|
||||
:loading="airportTableLogState.tableLoading"
|
||||
:row-selection="airportTableLogState.rowSelection"
|
||||
rowKey="boot_index"
|
||||
:pagination = "false">
|
||||
<template #log_time="{record}">
|
||||
<div>{{getLogTime(record)}}</div>
|
||||
</template>
|
||||
<template #size="{record}">
|
||||
<div>{{getLogSize(record.size)}}</div>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
<div class="log-list-item">
|
||||
<a-table :columns="droneLogColumns"
|
||||
:scroll="{ x: '100%', y: 600 }"
|
||||
:data-source="droneTableLogState.logList?.list"
|
||||
:loading="droneTableLogState.tableLoading"
|
||||
:row-selection="droneTableLogState.rowSelection"
|
||||
rowKey="boot_index"
|
||||
:pagination = "false">
|
||||
<template #log_time="{record}">
|
||||
<div>{{getLogTime(record)}}</div>
|
||||
</template>
|
||||
<template #size="{record}">
|
||||
<div>{{getLogSize(record.size)}}</div>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watchEffect, reactive, ref, computed, defineProps, defineEmits } from 'vue'
|
||||
import { ColumnProps, TableState } from 'ant-design-vue/lib/table/interface'
|
||||
import { IPage } from '/@/api/http/type'
|
||||
import { Device, DOMAIN } from '/@/types/device'
|
||||
import { getDeviceLogList, postDeviceUpgrade, DeviceLogFileInfo, UploadDeviceLogBody, DeviceLogItem } from '/@/api/device-log'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { useDeviceLogUploadDetail } from './use-device-log-upload-detail'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean,
|
||||
device: null | Device,
|
||||
}>()
|
||||
const emit = defineEmits(['update:visible', 'upload-log-ok'])
|
||||
|
||||
const sVisible = ref(false)
|
||||
|
||||
watchEffect(() => {
|
||||
sVisible.value = props.visible
|
||||
// 显示弹框时,获取设备日志信息
|
||||
if (props.visible) {
|
||||
getDeviceLogInfo()
|
||||
}
|
||||
})
|
||||
|
||||
function onVisibleChange (sVisible: boolean) {
|
||||
setVisible(sVisible)
|
||||
if (!sVisible) {
|
||||
resetTableLogState()
|
||||
}
|
||||
}
|
||||
|
||||
function setVisible (v: boolean, e?: Event) {
|
||||
sVisible.value = v
|
||||
emit('update:visible', v, e)
|
||||
}
|
||||
|
||||
// 表格
|
||||
const airportLogColumns: ColumnProps[] = [
|
||||
{ title: '机场日志', dataIndex: 'time', width: 100, slots: { customRender: 'log_time' } },
|
||||
{ title: '文件大小', dataIndex: 'size', width: 25, slots: { customRender: 'size' } },
|
||||
]
|
||||
|
||||
const droneLogColumns: ColumnProps[] = [
|
||||
{ title: '飞行器日志', dataIndex: 'time', width: 100, slots: { customRender: 'log_time' } },
|
||||
{ title: '文件大小', dataIndex: 'size', width: 25, slots: { customRender: 'size' } },
|
||||
]
|
||||
|
||||
const airportTableLogState = reactive({
|
||||
logList: {} as DeviceLogFileInfo,
|
||||
tableLoading: false,
|
||||
selectRow: [],
|
||||
rowSelection: {
|
||||
columnWidth: 15,
|
||||
selectedRowKeys: [] as number[],
|
||||
onChange: (selectedRowKeys:number[], selectedRows: []) => {
|
||||
airportTableLogState.rowSelection.selectedRowKeys = selectedRowKeys
|
||||
airportTableLogState.selectRow = selectedRows
|
||||
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows)
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
function resetTableLogState () {
|
||||
airportTableLogState.logList = {} as DeviceLogFileInfo
|
||||
airportTableLogState.selectRow = []
|
||||
airportTableLogState.tableLoading = false
|
||||
}
|
||||
|
||||
const droneTableLogState = reactive({
|
||||
logList: {} as DeviceLogFileInfo,
|
||||
tableLoading: false,
|
||||
selectRow: [],
|
||||
rowSelection: {
|
||||
columnWidth: 15,
|
||||
selectedRowKeys: [] as number[],
|
||||
onChange: (selectedRowKeys: number[], selectedRows: []) => {
|
||||
droneTableLogState.rowSelection.selectedRowKeys = selectedRowKeys
|
||||
droneTableLogState.selectRow = selectedRows
|
||||
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows)
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
const deviceLogUploadBtnDisabled = computed(() => {
|
||||
return (airportTableLogState.rowSelection.selectedRowKeys && airportTableLogState.rowSelection.selectedRowKeys.length <= 0) &&
|
||||
(droneTableLogState.rowSelection.selectedRowKeys && droneTableLogState.rowSelection.selectedRowKeys.length <= 0)
|
||||
})
|
||||
|
||||
// 获取设备内日志
|
||||
async function getDeviceLogInfo () {
|
||||
airportTableLogState.tableLoading = true
|
||||
droneTableLogState.tableLoading = true
|
||||
try {
|
||||
const { code, data } = await getDeviceLogList({
|
||||
device_sn: props.device?.device_sn || '',
|
||||
domain: [DOMAIN.DOCK, DOMAIN.DRONE]
|
||||
})
|
||||
if (code === 0) {
|
||||
const { files } = data
|
||||
if (files && files.length > 0) {
|
||||
files.forEach(file => {
|
||||
if (file.module === DOMAIN.DOCK) {
|
||||
airportTableLogState.logList = file
|
||||
} else if (file.module === DOMAIN.DRONE) {
|
||||
droneTableLogState.logList = file
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
}
|
||||
airportTableLogState.tableLoading = false
|
||||
droneTableLogState.tableLoading = false
|
||||
}
|
||||
|
||||
// 日志上传
|
||||
async function uploadDeviceLog () {
|
||||
const body = {
|
||||
device_sn: props.device?.device_sn || '',
|
||||
files: [] as any
|
||||
} as UploadDeviceLogBody
|
||||
if (airportTableLogState.selectRow && airportTableLogState.selectRow.length > 0) {
|
||||
body.files.push({
|
||||
list: airportTableLogState.selectRow,
|
||||
device_sn: airportTableLogState.logList.device_sn,
|
||||
module: airportTableLogState.logList.module
|
||||
})
|
||||
}
|
||||
if (droneTableLogState.selectRow && droneTableLogState.selectRow.length > 0) {
|
||||
body.files.push({
|
||||
list: droneTableLogState.selectRow,
|
||||
device_sn: droneTableLogState.logList.device_sn,
|
||||
module: droneTableLogState.logList.module
|
||||
})
|
||||
}
|
||||
const { code } = await postDeviceUpgrade(body)
|
||||
if (code === 0) {
|
||||
message.success('日志上传任务执行成功')
|
||||
emit('upload-log-ok')
|
||||
setVisible(false)
|
||||
}
|
||||
}
|
||||
|
||||
const { getLogTime, getLogSize } = useDeviceLogUploadDetail()
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.device-log-upload-wrap{
|
||||
|
||||
.device-log-list{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 8px 0;
|
||||
.log-list-item{
|
||||
width: 420px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,326 @@
|
||||
<template>
|
||||
<a-drawer
|
||||
title="设备日志上传记录"
|
||||
placement="right"
|
||||
v-model:visible="sVisible"
|
||||
@update:visible="onVisibleChange"
|
||||
:width="800">
|
||||
<!-- 设备日志上传记录 -->
|
||||
<div class="device-log-upload-record-wrap">
|
||||
<div class="page-action-row">
|
||||
<a-button type="primary" @click="onUploadDeviceLog">上传日志</a-button>
|
||||
</div>
|
||||
<div class="device-log-upload-list">
|
||||
<a-table :columns="deviceLogUploadListColumns"
|
||||
:scroll="{ x: '100%', y: 600 }"
|
||||
:data-source="deviceUploadLogState.uploadLogList"
|
||||
:loading="deviceUploadLogState.loading"
|
||||
:pagination="deviceUploadLogState.paginationProp"
|
||||
@change="onDeviceUploadLogTableChange"
|
||||
rowKey="logs_id">
|
||||
<!-- 设备类型 -->
|
||||
<template #device_type="{ record }">
|
||||
<div>
|
||||
<div v-if="getDeviceInfo(record).parents && getDeviceInfo(record).parents.length > 0">{{ DEVICE_NAME[getDeviceInfo(record).parents[0].device_model.key]}}</div>
|
||||
<div v-if="getDeviceInfo(record).hosts && getDeviceInfo(record).hosts.length > 0">{{ DEVICE_NAME[getDeviceInfo(record).hosts[0].device_model.key]}}</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 设备sn -->
|
||||
<template #device_sn="{ record }">
|
||||
<div>
|
||||
<div v-if="getDeviceInfo(record).parents && getDeviceInfo(record).parents.length > 0">{{ getDeviceInfo(record).parents[0].sn }}</div>
|
||||
<div v-if="getDeviceInfo(record).hosts && getDeviceInfo(record).hosts.length > 0">{{ getDeviceInfo(record).hosts[0].sn }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 上传状态 -->
|
||||
<template #status="{ record }">
|
||||
<div>
|
||||
<div>
|
||||
<span class="circle-icon" :style="{backgroundColor: getDeviceLogUploadStatus(record).color}"></span>
|
||||
{{ getDeviceLogUploadStatus(record).text }}
|
||||
</div>
|
||||
<div v-if="record.status === DeviceLogUploadStatusEnum.Uploading">
|
||||
<a-progress :percent="getLogProgress(record)" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 操作 -->
|
||||
<template #action="{ record }">
|
||||
<div class="row-action">
|
||||
<a-tooltip title="查看详情">
|
||||
<FileTextOutlined @click="showDeviceLogDetail(record)"/>
|
||||
</a-tooltip>
|
||||
<span v-if="record.status === DeviceLogUploadStatusEnum.Uploading">
|
||||
<a-tooltip title="取消">
|
||||
<StopOutlined @click="onCancelUploadDeviceLog(record)"/>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
<span v-else>
|
||||
<a-tooltip title="删除">
|
||||
<DeleteOutlined @click="onDeleteUploadDeviceLog(record)"/>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</div>
|
||||
</a-drawer>
|
||||
<!-- 设备日志上传弹框 -->
|
||||
<DeviceLogUploadModal
|
||||
v-model:visible="deviceLogUploadModalVisible"
|
||||
:device="props.device"
|
||||
@upload-log-ok="onUploadLogOk"
|
||||
></DeviceLogUploadModal>
|
||||
|
||||
<!-- 设备日志上传详情弹框 -->
|
||||
<DeviceLogDetailModal
|
||||
v-model:visible="deviceLogDetailModalVisible"
|
||||
:deviceLog="currentDeviceLog"
|
||||
></DeviceLogDetailModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watchEffect, reactive, ref, defineProps, defineEmits } from 'vue'
|
||||
import { ColumnProps, TableState } from 'ant-design-vue/lib/table/interface'
|
||||
import { IPage } from '/@/api/http/type'
|
||||
import { Device, DOMAIN, DEVICE_NAME } from '/@/types/device'
|
||||
import DeviceLogUploadModal from './DeviceLogUploadModal.vue'
|
||||
import DeviceLogDetailModal from './DeviceLogDetailModal.vue'
|
||||
import { getDeviceUploadLogList, GetDeviceUploadLogListRsp, cancelDeviceLogUpload, deleteDeviceLogUpload } from '/@/api/device-log'
|
||||
import { StopOutlined, DeleteOutlined, FileTextOutlined } from '@ant-design/icons-vue'
|
||||
import { DeviceLogUploadStatusEnum, DeviceLogUploadStatusMap, DeviceLogUploadStatusColor, DeviceLogUploadInfo, DeviceLogUploadWsStatusMap, DeviceLogProgressInfo } from '/@/types/device-log'
|
||||
import { useDeviceLogUploadProgressEvent } from './use-device-log-upload-progress-event'
|
||||
import { Modal } from 'ant-design-vue'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean,
|
||||
device: null | Device,
|
||||
}>()
|
||||
const emit = defineEmits(['update:visible'])
|
||||
|
||||
const sVisible = ref(false)
|
||||
|
||||
watchEffect(() => {
|
||||
sVisible.value = props.visible
|
||||
// 显示弹框时,获取设备日志上传记录信息
|
||||
if (props.visible) {
|
||||
getDeviceUploadLogInfo()
|
||||
}
|
||||
})
|
||||
|
||||
function onVisibleChange (sVisible: boolean) {
|
||||
setVisible(sVisible)
|
||||
}
|
||||
|
||||
function setVisible (v: boolean, e?: Event) {
|
||||
sVisible.value = v
|
||||
emit('update:visible', v, e)
|
||||
}
|
||||
|
||||
// 日志列表
|
||||
const deviceLogUploadListColumns: ColumnProps[] = [
|
||||
{ title: '上传时间', dataIndex: 'create_time', width: 100 },
|
||||
{ title: '设备型号', dataIndex: 'device_type', width: 80, slots: { customRender: 'device_type' } },
|
||||
{ title: '设备SN', dataIndex: 'device_sn', width: 120, slots: { customRender: 'device_sn' } },
|
||||
{ title: '上传状态', dataIndex: 'status', width: 120, slots: { customRender: 'status' } },
|
||||
{ title: '操作', dataIndex: 'actions', width: 80, slots: { customRender: 'action' } },
|
||||
]
|
||||
|
||||
const deviceUploadLogState = reactive({
|
||||
uploadLogList: [] as GetDeviceUploadLogListRsp[],
|
||||
loading: false,
|
||||
paginationProp: {
|
||||
pageSizeOptions: ['20', '50', '100'],
|
||||
showQuickJumper: true,
|
||||
showSizeChanger: true,
|
||||
pageSize: 50,
|
||||
current: 1,
|
||||
total: 0
|
||||
}
|
||||
})
|
||||
|
||||
// 获取上传的设备日志
|
||||
async function getDeviceUploadLogInfo () {
|
||||
deviceUploadLogState.loading = true
|
||||
try {
|
||||
const { code, data } = await getDeviceUploadLogList({
|
||||
device_sn: props.device?.device_sn || '',
|
||||
page: deviceUploadLogState.paginationProp.current,
|
||||
page_size: deviceUploadLogState.paginationProp.pageSize
|
||||
})
|
||||
if (code === 0) {
|
||||
deviceUploadLogState.uploadLogList = data.list
|
||||
deviceUploadLogState.paginationProp.total = data.pagination.total
|
||||
deviceUploadLogState.paginationProp.current = data.pagination.page
|
||||
deviceUploadLogState.paginationProp.pageSize = data.pagination.page_size
|
||||
}
|
||||
deviceUploadLogState.loading = false
|
||||
} catch (error) {
|
||||
deviceUploadLogState.loading = false
|
||||
}
|
||||
}
|
||||
type Pagination = TableState['pagination']
|
||||
|
||||
// 获取设备信息
|
||||
function getDeviceInfo (deviceLogItem: GetDeviceUploadLogListRsp) {
|
||||
const { device_topo: deviceTopo } = deviceLogItem
|
||||
return deviceTopo
|
||||
}
|
||||
|
||||
// 获取上传状态
|
||||
function getDeviceLogUploadStatus (deviceLogItem: GetDeviceUploadLogListRsp) {
|
||||
const statusObj = {
|
||||
color: '',
|
||||
text: ''
|
||||
}
|
||||
const { status } = deviceLogItem
|
||||
statusObj.color = DeviceLogUploadStatusColor[status]
|
||||
statusObj.text = DeviceLogUploadStatusMap[status]
|
||||
return statusObj
|
||||
}
|
||||
|
||||
// 获取上传进度
|
||||
function getLogProgress (deviceLogItem: GetDeviceUploadLogListRsp) {
|
||||
let percent = 0
|
||||
const { logs_progress } = deviceLogItem
|
||||
if (logs_progress && logs_progress.length > 0) {
|
||||
logs_progress.forEach(log => {
|
||||
percent += (log.progress || 0)
|
||||
})
|
||||
percent = percent / logs_progress.length
|
||||
}
|
||||
return Math.floor(percent)
|
||||
}
|
||||
|
||||
// 设备日志上传进度更新
|
||||
function onDeviceLogUploadWs (data: DeviceLogUploadInfo) {
|
||||
const { sn, output } = data
|
||||
if (output) {
|
||||
const { files, status, logs_id: logId } = output || {}
|
||||
const deviceLogItem = deviceUploadLogState.uploadLogList.find(log => log.logs_id === logId)
|
||||
if (!deviceLogItem) return
|
||||
if (status) {
|
||||
deviceLogItem.status = DeviceLogUploadWsStatusMap[status]
|
||||
}
|
||||
if (files && files.length > 0) {
|
||||
const logsProgress = [] as DeviceLogProgressInfo[]
|
||||
files.forEach(file => {
|
||||
logsProgress.push({
|
||||
...file,
|
||||
status: DeviceLogUploadWsStatusMap[file.status]
|
||||
})
|
||||
})
|
||||
deviceLogItem.logs_progress = logsProgress
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useDeviceLogUploadProgressEvent(onDeviceLogUploadWs)
|
||||
|
||||
// 搜索
|
||||
async function onDeviceUploadLogTableChange (page: Pagination) {
|
||||
deviceUploadLogState.paginationProp.current = page?.current || 1
|
||||
deviceUploadLogState.paginationProp.pageSize = page?.pageSize || 20
|
||||
await getDeviceUploadLogInfo()
|
||||
}
|
||||
|
||||
// 查看上传设备日志详情
|
||||
const deviceLogDetailModalVisible = ref(false)
|
||||
const currentDeviceLog = ref({} as GetDeviceUploadLogListRsp)
|
||||
|
||||
function showDeviceLogDetail (deviceLogItem: GetDeviceUploadLogListRsp) {
|
||||
if (!deviceLogItem) return
|
||||
currentDeviceLog.value = deviceLogItem
|
||||
deviceLogDetailModalVisible.value = true
|
||||
}
|
||||
|
||||
// 取消上传设备日志
|
||||
async function onCancelUploadDeviceLog (deviceLogItem: GetDeviceUploadLogListRsp) {
|
||||
Modal.confirm({
|
||||
title: '取消日志上传',
|
||||
content: '您确认取消设备日志上传吗?',
|
||||
okType: 'danger',
|
||||
onOk () {
|
||||
cancelDeviceLogUploadOk()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async function cancelDeviceLogUploadOk () {
|
||||
const { code } = await cancelDeviceLogUpload({
|
||||
device_sn: props.device?.device_sn || '',
|
||||
module_list: [DOMAIN.DOCK, DOMAIN.DRONE],
|
||||
status: 'cancel'
|
||||
})
|
||||
if (code === 0) {
|
||||
await getDeviceUploadLogInfo()
|
||||
}
|
||||
}
|
||||
|
||||
// 删除上传的设备日志
|
||||
function onDeleteUploadDeviceLog (deviceLogItem: GetDeviceUploadLogListRsp) {
|
||||
Modal.confirm({
|
||||
title: '删除上传日志',
|
||||
content: '您确认删除该条已上传设备日志吗?',
|
||||
okType: 'danger',
|
||||
onOk () {
|
||||
deleteUploadDeviceLogOk(deviceLogItem)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async function deleteUploadDeviceLogOk (deviceLogItem: GetDeviceUploadLogListRsp) {
|
||||
const { code } = await deleteDeviceLogUpload({
|
||||
device_sn: props.device?.device_sn || '',
|
||||
logs_id: deviceLogItem.logs_id
|
||||
})
|
||||
if (code === 0) {
|
||||
await getDeviceUploadLogInfo()
|
||||
}
|
||||
}
|
||||
|
||||
// 上传日志
|
||||
const deviceLogUploadModalVisible = ref(false)
|
||||
|
||||
function onUploadDeviceLog () {
|
||||
deviceLogUploadModalVisible.value = true
|
||||
}
|
||||
|
||||
function onUploadLogOk () {
|
||||
// 刷新列表
|
||||
getDeviceUploadLogInfo()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.device-log-upload-record-wrap{
|
||||
.page-action-row{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.device-log-upload-list{
|
||||
padding: 20px 0 10px;
|
||||
}
|
||||
|
||||
.circle-icon {
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-right: 3px;
|
||||
border-radius: 50%;
|
||||
vertical-align: middle;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.row-action{
|
||||
color: #2d8cf0;
|
||||
|
||||
& > span{
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,23 @@
|
||||
import { DeviceLogItem } from '/@/api/device-log'
|
||||
import { bytesToSize } from '/@/utils/bytes'
|
||||
import { formatUnixTime } from '/@/utils/time'
|
||||
import {
|
||||
DATE_FORMAT_MINUTE
|
||||
} from '/@/utils/constants'
|
||||
|
||||
export function useDeviceLogUploadDetail () {
|
||||
function getLogTime (deviceLog: DeviceLogItem): string {
|
||||
const startTime = formatUnixTime(deviceLog.start_time, DATE_FORMAT_MINUTE)
|
||||
const endTime = formatUnixTime(deviceLog.end_time, DATE_FORMAT_MINUTE)
|
||||
return `${startTime} — ${endTime}`
|
||||
}
|
||||
|
||||
function getLogSize (size: number) {
|
||||
return bytesToSize(size)
|
||||
}
|
||||
|
||||
return {
|
||||
getLogTime,
|
||||
getLogSize
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import EventBus from '/@/event-bus/'
|
||||
import { onMounted, onBeforeUnmount } from 'vue'
|
||||
import { DeviceLogUploadInfo } from '/@/types/device-log'
|
||||
|
||||
export function useDeviceLogUploadProgressEvent (onDeviceLogUploadWs: (data: DeviceLogUploadInfo) => void): void {
|
||||
function handleDeviceLogUploadProgress (payload: any) {
|
||||
onDeviceLogUploadWs(payload.data)
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
// console.log('payload', payload.data)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
EventBus.on('deviceLogUploadProgress', handleDeviceLogUploadProgress)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
EventBus.off('deviceLogUploadProgress', handleDeviceLogUploadProgress)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<div class="firmware_upgrade_wrap">
|
||||
<!-- 版本 -->
|
||||
<span class="version"> {{ device.firmware_version }}</span>
|
||||
<!-- tag -->
|
||||
<span v-if="getTagStatus(device)"
|
||||
class="status-tag pointer">
|
||||
<a-tag class="pointer"
|
||||
:color="getFirmwareTag(device.firmware_status).color"
|
||||
@click="deviceUpgrade(device)">
|
||||
{{ getFirmwareTag(device.firmware_status).text }}
|
||||
</a-tag>
|
||||
</span>
|
||||
<!-- 进度 -->
|
||||
<span v-if="device.firmware_status === DeviceFirmwareStatusEnum.DuringUpgrade">
|
||||
{{ `${device.firmware_progress}%`}}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineProps, defineEmits, ref, watch, computed } from 'vue'
|
||||
import { Device, DeviceFirmwareStatusEnum, DeviceFirmwareStatus, DeviceFirmwareStatusColor } from '/@/types/device'
|
||||
|
||||
const props = defineProps<{
|
||||
device: Device,
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['device-upgrade'])
|
||||
const needUpgrade = computed(() => {
|
||||
return props.device.firmware_status === DeviceFirmwareStatusEnum.ConsistencyUpgrade ||
|
||||
props.device.firmware_status === DeviceFirmwareStatusEnum.ToUpgraded
|
||||
})
|
||||
|
||||
function getTagStatus (record: Device) {
|
||||
return record.firmware_status && record.firmware_status !== DeviceFirmwareStatusEnum.None
|
||||
}
|
||||
|
||||
function getFirmwareTag (status: DeviceFirmwareStatusEnum) {
|
||||
return {
|
||||
text: DeviceFirmwareStatus[status] || '',
|
||||
color: DeviceFirmwareStatusColor[status] || ''
|
||||
}
|
||||
}
|
||||
|
||||
function deviceUpgrade (record: Device) {
|
||||
if (!needUpgrade.value) return
|
||||
emit('device-upgrade', record)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.firmware_upgrade_wrap{
|
||||
|
||||
.status-tag{
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<a-modal :visible="sVisible"
|
||||
:title="title"
|
||||
:closable="false"
|
||||
centered
|
||||
@update:visible="onVisibleChange"
|
||||
@cancel="onCancel"
|
||||
@ok="onConfirm">
|
||||
<div>
|
||||
升级固件版本: {{ deviceUpgradeInfo?.product_version }}
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineProps, defineEmits, ref, Ref, watchEffect } from 'vue'
|
||||
import { Device, DeviceFirmwareStatusEnum, DeviceFirmwareStatus, DeviceFirmwareTypeEnum } from '/@/types/device'
|
||||
import { getDeviceUpgradeInfo, GetDeviceUpgradeInfoRsp, DeviceUpgradeBody } from '/@/api/device-upgrade'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean,
|
||||
title: string,
|
||||
device: null | Device,
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['update:visible', 'ok', 'cancel'])
|
||||
|
||||
const deviceUpgradeInfo:Ref<GetDeviceUpgradeInfoRsp> = ref({} as GetDeviceUpgradeInfoRsp)
|
||||
const sVisible = ref(false)
|
||||
|
||||
watchEffect(() => {
|
||||
sVisible.value = props.visible
|
||||
// 显示弹框时,获取设备升级信息
|
||||
if (props.visible) {
|
||||
initDeviceUpgradeInfo()
|
||||
}
|
||||
})
|
||||
|
||||
function onVisibleChange (sVisible: boolean) {
|
||||
setVisible(sVisible)
|
||||
}
|
||||
|
||||
function setVisible (v: boolean, e?: Event) {
|
||||
sVisible.value = v
|
||||
emit('update:visible', v, e)
|
||||
}
|
||||
|
||||
// 获取设备升级信息
|
||||
async function initDeviceUpgradeInfo () {
|
||||
if (!props.device?.device_name) {
|
||||
return
|
||||
}
|
||||
const { code, data } = await getDeviceUpgradeInfo({ device_name: props.device?.device_name })
|
||||
if (code === 0) {
|
||||
deviceUpgradeInfo.value = data && data[0]
|
||||
}
|
||||
}
|
||||
|
||||
// 提交
|
||||
function checkConfirm () {
|
||||
if (!deviceUpgradeInfo.value.product_version) {
|
||||
return false
|
||||
}
|
||||
if (!props.device) {
|
||||
return false
|
||||
}
|
||||
if (props.device.firmware_status !== DeviceFirmwareStatusEnum.ToUpgraded && props.device.firmware_status !== DeviceFirmwareStatusEnum.ConsistencyUpgrade) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function onConfirm (e: Event) {
|
||||
if (!checkConfirm()) {
|
||||
return
|
||||
}
|
||||
setVisible(false, e)
|
||||
emit('ok', [{
|
||||
device_name: props.device?.device_name,
|
||||
sn: props.device?.device_sn,
|
||||
product_version: deviceUpgradeInfo.value.product_version,
|
||||
firmware_upgrade_type: props.device?.firmware_status === DeviceFirmwareStatusEnum.ToUpgraded ? DeviceFirmwareTypeEnum.ToUpgraded : DeviceFirmwareTypeEnum.ConsistencyUpgrade // 1-普通升级,2-一致性升级
|
||||
}] as DeviceUpgradeBody, e)
|
||||
}
|
||||
|
||||
function onCancel (e: Event) {
|
||||
setVisible(false, e)
|
||||
emit('cancel', e)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,19 @@
|
||||
import EventBus from '/@/event-bus/'
|
||||
import { onMounted, onBeforeUnmount } from 'vue'
|
||||
import { DeviceCmdExecuteInfo, DeviceCmdExecuteStatus } from '/@/types/device-cmd'
|
||||
|
||||
export function useDeviceUpgradeEvent (onDeviceUpgradeWs: (payload: DeviceCmdExecuteInfo) => void): void {
|
||||
function handleDeviceUpgrade (payload: any) {
|
||||
onDeviceUpgradeWs(payload.data)
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
// console.log('payload', payload.data)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
EventBus.on('deviceUpgrade', handleDeviceUpgrade)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
EventBus.off('deviceUpgrade', handleDeviceUpgrade)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Ref, ref } from 'vue'
|
||||
import { Device } from '/@/types/device'
|
||||
import { postDeviceUpgrade, DeviceUpgradeBody } from '/@/api/device-upgrade'
|
||||
|
||||
export function useDeviceFirmwareUpgrade (workspaceId: string) {
|
||||
const deviceFirmwareUpgradeModalVisible = ref(false)
|
||||
const selectedDevice: Ref<null | Device> = ref(null)
|
||||
|
||||
function setDeviceFirmwareUpgradeModalVisible (visible: boolean) {
|
||||
deviceFirmwareUpgradeModalVisible.value = visible
|
||||
}
|
||||
|
||||
function setSelectedDevice (device: null | Device) {
|
||||
selectedDevice.value = device
|
||||
}
|
||||
|
||||
// 点击设备升级
|
||||
function onDeviceUpgrade (record: Device) {
|
||||
if (!record) {
|
||||
return
|
||||
}
|
||||
setSelectedDevice(record)
|
||||
setDeviceFirmwareUpgradeModalVisible(true)
|
||||
}
|
||||
|
||||
// 确认设备升级
|
||||
async function onUpgradeDeviceOk (deviceUpgradeBody: DeviceUpgradeBody) {
|
||||
const { code } = await postDeviceUpgrade(workspaceId, deviceUpgradeBody)
|
||||
if (code === 0) {
|
||||
// setDeviceFirmwareUpgradeModalVisible(false)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
deviceFirmwareUpgradeModalVisible,
|
||||
setDeviceFirmwareUpgradeModalVisible,
|
||||
selectedDevice,
|
||||
setSelectedDevice,
|
||||
onDeviceUpgrade,
|
||||
onUpgradeDeviceOk,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<div class="dock-control-panel">
|
||||
<!-- title -->
|
||||
<div class="dock-control-panel-header fz16 pl5 pr5 flex-align-center flex-row flex-justify-between">
|
||||
<span>远程调试 {{ props.sn}}</span>
|
||||
<span @click="closeControlPanel">
|
||||
<CloseOutlined />
|
||||
</span>
|
||||
</div>
|
||||
<!-- cmd -->
|
||||
<div class="control-cmd-wrapper">
|
||||
<div v-for="(cmdItem, index) in cmdList" :key="cmdItem.cmdKey" class="control-cmd-item">
|
||||
<div class="control-cmd-item-left">
|
||||
<div class="item-label">{{ cmdItem.label }}</div>
|
||||
<div class="item-status">{{ cmdItem.status }}</div>
|
||||
</div>
|
||||
<div class="control-cmd-item-right">
|
||||
<a-button :loading="cmdItem.loading" size="small" type="primary" @click="sendControlCmd(cmdItem, index)">
|
||||
{{ cmdItem.operateText }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineProps, defineEmits, ref, watch } from 'vue'
|
||||
import {
|
||||
CloseOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
import { useDockControl } from './useDockControl'
|
||||
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'
|
||||
|
||||
const props = defineProps<{
|
||||
sn: string,
|
||||
deviceInfo: DeviceInfoType,
|
||||
}>()
|
||||
|
||||
const store = useMyStore()
|
||||
const initCmdList = baseCmdList.map(cmdItem => Object.assign({}, cmdItem))
|
||||
const cmdList = ref(initCmdList)
|
||||
|
||||
// 根据机场指令执行状态更新信息
|
||||
watch(() => store.state.devicesCmdExecuteInfo, (devicesCmdExecuteInfo) => {
|
||||
if (props.sn && devicesCmdExecuteInfo[props.sn]) {
|
||||
updateDeviceCmdInfoByExecuteInfo(cmdList.value, devicesCmdExecuteInfo[props.sn])
|
||||
}
|
||||
}, {
|
||||
immediate: true,
|
||||
deep: true,
|
||||
})
|
||||
|
||||
// 根据设备osd信息更新信息
|
||||
watch(() => props.deviceInfo, (value) => {
|
||||
updateDeviceCmdInfoByOsd(cmdList.value, value)
|
||||
// console.log('deviceInfo', value)
|
||||
}, {
|
||||
immediate: true,
|
||||
deep: true
|
||||
})
|
||||
|
||||
const emit = defineEmits(['close-control-panel'])
|
||||
|
||||
function closeControlPanel () {
|
||||
emit('close-control-panel', props.sn, false)
|
||||
}
|
||||
|
||||
// dock 控制指令
|
||||
const {
|
||||
sendDockControlCmd,
|
||||
} = useDockControl()
|
||||
|
||||
async function sendControlCmd (cmdItem: DeviceCmdItem, index: number) {
|
||||
const success = await sendDockControlCmd({
|
||||
sn: props.sn,
|
||||
cmd: cmdItem.cmdKey
|
||||
}, true)
|
||||
if (success) {
|
||||
// updateDeviceSingleCmdInfo(cmdList.value[index])
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.dock-control-panel{
|
||||
position: absolute;
|
||||
left: calc(100% + 10px);
|
||||
top: 0px;
|
||||
width: 480px;
|
||||
padding: 0 !important;
|
||||
background: #000;
|
||||
color: #fff;
|
||||
border-radius: 2px;
|
||||
|
||||
.dock-control-panel-header{
|
||||
border-bottom: 1px solid #515151;
|
||||
}
|
||||
|
||||
.control-cmd-wrapper{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
padding: 4px 10px;
|
||||
.control-cmd-item{
|
||||
width: 220px;
|
||||
height: 58px;
|
||||
display: flex;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,48 @@
|
||||
import { message } from 'ant-design-vue'
|
||||
import { ref } from 'vue'
|
||||
import { postSendCmd } from '/@/api/device-cmd'
|
||||
import { DeviceCmd } from '/@/types/device-cmd'
|
||||
|
||||
export function useDockControl () {
|
||||
const controlPanelVisible = ref(false)
|
||||
|
||||
function setControlPanelVisible (visible: boolean) {
|
||||
controlPanelVisible.value = visible
|
||||
}
|
||||
|
||||
// 远程调试开关
|
||||
async function dockDebugOnOff (sn: string, on: boolean) {
|
||||
const success = await sendDockControlCmd({
|
||||
sn: sn,
|
||||
cmd: on ? DeviceCmd.DebugModeOpen : DeviceCmd.DebugModeClose
|
||||
}, false)
|
||||
if (success) {
|
||||
setControlPanelVisible(on)
|
||||
}
|
||||
}
|
||||
|
||||
// 发送指令
|
||||
async function sendDockControlCmd (params: {
|
||||
sn: string,
|
||||
cmd: DeviceCmd
|
||||
}, tip = true) {
|
||||
try {
|
||||
const { code, message: msg } = await postSendCmd({ dock_sn: params.sn, device_cmd: params.cmd })
|
||||
if (code === 0) {
|
||||
tip && message.success('指令发送成功')
|
||||
return true
|
||||
}
|
||||
throw (msg)
|
||||
} catch (e) {
|
||||
tip && message.error('指令发送失败')
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
controlPanelVisible,
|
||||
setControlPanelVisible,
|
||||
sendDockControlCmd,
|
||||
dockDebugOnOff,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import mitt, { Emitter } from 'mitt'
|
||||
|
||||
type Events = {
|
||||
deviceUpgrade: any;
|
||||
deviceLogUploadProgress: any
|
||||
};
|
||||
|
||||
const emitter: Emitter<Events> = mitt<Events>()
|
||||
|
||||
export default emitter
|
||||
@@ -0,0 +1,21 @@
|
||||
import { onMounted, onUnmounted } from 'vue'
|
||||
import ReconnectingWebSocket from 'reconnecting-websocket'
|
||||
import ConnectWebSocket, { MessageHandler } from '/@/websocket'
|
||||
import { getWebsocketUrl } from '/@/websocket/util/config'
|
||||
|
||||
/**
|
||||
* 接收一个message函数
|
||||
* @param messageHandler
|
||||
*/
|
||||
export function useConnectWebSocket (messageHandler: MessageHandler) {
|
||||
const webSocket = new ConnectWebSocket(getWebsocketUrl())
|
||||
|
||||
onMounted(() => {
|
||||
webSocket?.registerMessageHandler(messageHandler)
|
||||
webSocket?.initSocket()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
webSocket?.close()
|
||||
})
|
||||
}
|
||||
@@ -10,16 +10,18 @@ import { GeojsonCoordinate } from '/@/types/map'
|
||||
|
||||
export function useGMapCover () {
|
||||
const root = getRoot()
|
||||
const AMap = root.$aMapObj
|
||||
const AMap = root.$aMap
|
||||
|
||||
const normalColor = '#2D8CF0'
|
||||
const store = rootStore
|
||||
const coverList = store.state.coverList
|
||||
|
||||
function AddCoverToMap (cover :any) {
|
||||
root.$aMap.add(cover)
|
||||
root.$map.add(cover)
|
||||
coverList.push(cover)
|
||||
// console.log('coverList:', store.state.coverList)
|
||||
}
|
||||
|
||||
function getPinIcon (color?:string) {
|
||||
// console.log('color', color)
|
||||
const colorObj: {
|
||||
@@ -31,7 +33,6 @@ export function useGMapCover () {
|
||||
'b620e0': pinb620e0,
|
||||
'e23c39': pine23c39,
|
||||
'ffbb00': pineffbb00,
|
||||
|
||||
}
|
||||
const iconName = (color?.replaceAll('#', '') || '').toLocaleLowerCase()
|
||||
return new AMap.Icon({
|
||||
@@ -41,6 +42,7 @@ export function useGMapCover () {
|
||||
// imageSize: new AMap.Size(40, 50)
|
||||
})
|
||||
}
|
||||
|
||||
function init2DPin (name: string, coordinates:GeojsonCoordinate, color?:string, data?:{}) {
|
||||
console.log(name, coordinates[0], coordinates[1], color, data)
|
||||
const pin = new AMap.Marker({
|
||||
@@ -54,8 +56,9 @@ export function useGMapCover () {
|
||||
// console.log('coordinates pin', pin)
|
||||
AddCoverToMap(pin)
|
||||
}
|
||||
|
||||
function AddOverlayGroup (overlayGroup) {
|
||||
root.$aMap.add(overlayGroup)
|
||||
root.$map.add(overlayGroup)
|
||||
coverList.push(overlayGroup)
|
||||
}
|
||||
function initPolyline (name: string, coordinates:GeojsonCoordinate[], color?:string, data?:{}) {
|
||||
@@ -74,6 +77,7 @@ export function useGMapCover () {
|
||||
})
|
||||
AddOverlayGroup(polyline)
|
||||
}
|
||||
|
||||
function initPolygon (name: string, coordinates:GeojsonCoordinate[], color?:string, data?:{}) {
|
||||
const path = [] as GeojsonCoordinate[]
|
||||
coordinates.forEach(coordinate => {
|
||||
@@ -92,6 +96,7 @@ export function useGMapCover () {
|
||||
})
|
||||
AddOverlayGroup(Polygon)
|
||||
}
|
||||
|
||||
function removeCoverFromMap (id:string) {
|
||||
for (let i = 0; i < coverList.length; i++) {
|
||||
const ele = coverList[i]
|
||||
@@ -99,12 +104,13 @@ export function useGMapCover () {
|
||||
const extdata = ele?.getExtData()
|
||||
if (extdata?.id === id) {
|
||||
console.log(extdata)
|
||||
root.$aMap.remove(ele)
|
||||
root.$map.remove(ele)
|
||||
coverList.slice(i, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getElementFromMap (id:string) {
|
||||
// console.log('start', new Date().getTime())
|
||||
const ele = coverList.find(ele => ele?.getExtData().id === id)
|
||||
@@ -118,6 +124,7 @@ export function useGMapCover () {
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
function updatePinElement (id:string, name: string, coordinates:GeojsonCoordinate, color?:string) {
|
||||
const element = getElementFromMap(id) as any
|
||||
if (element) {
|
||||
@@ -133,6 +140,7 @@ export function useGMapCover () {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
init2DPin,
|
||||
initPolyline,
|
||||
|
||||
@@ -6,23 +6,30 @@ import { message } from 'ant-design-vue'
|
||||
|
||||
export function deviceTsaUpdate () {
|
||||
const root = getRoot()
|
||||
const AMap = root.$aMapObj
|
||||
const AMap = root.$aMap
|
||||
|
||||
const map = root.$aMap
|
||||
const icons: {
|
||||
[key: string]: string
|
||||
} = {
|
||||
'sub-device' : '/@/assets/icons/drone.png',
|
||||
'sub-device': '/@/assets/icons/drone.png',
|
||||
'gateway': '/@/assets/icons/rc.png',
|
||||
'dock': '/@/assets/icons/dock.png'
|
||||
}
|
||||
const markers = store.state.markerInfo.coverMap
|
||||
const paths = store.state.markerInfo.pathMap
|
||||
|
||||
const passedPolyline = new AMap.Polyline({
|
||||
map: map,
|
||||
strokeColor: '#939393' // 线颜色
|
||||
})
|
||||
|
||||
// Fix: 航迹初始化报错
|
||||
// TODO: 从时序上解决
|
||||
let trackLine = null as any
|
||||
function getTrackLineInstance () {
|
||||
if (!trackLine) {
|
||||
trackLine = new AMap.Polyline({
|
||||
map: root.$map,
|
||||
strokeColor: '#939393' // 线颜色
|
||||
})
|
||||
}
|
||||
return trackLine
|
||||
}
|
||||
|
||||
function initIcon (type: string) {
|
||||
return new AMap.Icon({
|
||||
@@ -36,13 +43,13 @@ export function deviceTsaUpdate () {
|
||||
return
|
||||
}
|
||||
markers[sn] = new AMap.Marker({
|
||||
position: new AMap.LngLat(lng ? lng : 113.935913, lat ? lat : 22.525335),
|
||||
position: new AMap.LngLat(lng || 113.935913, lat || 22.525335),
|
||||
icon: initIcon(type),
|
||||
title: name,
|
||||
anchor: 'top-center',
|
||||
offset: [0, -20],
|
||||
})
|
||||
root.$aMap.add(markers[sn])
|
||||
root.$map.add(markers[sn])
|
||||
|
||||
// markers[sn].on('moving', function (e: any) {
|
||||
// let path = paths[sn]
|
||||
@@ -52,19 +59,21 @@ export function deviceTsaUpdate () {
|
||||
// }
|
||||
// path.push(e.passedPath[0])
|
||||
// path.push(e.passedPath[1])
|
||||
// passedPolyline.setPath(path)
|
||||
// getTrackLineInstance().setPath(path)
|
||||
// })
|
||||
}
|
||||
|
||||
function removeMarker (sn: string) {
|
||||
if (!markers[sn]) {
|
||||
return
|
||||
}
|
||||
root.$aMap.remove(markers[sn])
|
||||
passedPolyline.setPath([])
|
||||
root.$map.remove(markers[sn])
|
||||
getTrackLineInstance().setPath([])
|
||||
delete markers[sn]
|
||||
delete paths[sn]
|
||||
}
|
||||
function addMarker(sn: string, lng?: number, lat?: number) {
|
||||
|
||||
function addMarker (sn: string, lng?: number, lat?: number) {
|
||||
getDeviceBySn(localStorage.getItem(ELocalStorageKey.WorkspaceId)!, sn)
|
||||
.then(data => {
|
||||
if (data.code !== 0) {
|
||||
@@ -74,7 +83,8 @@ export function deviceTsaUpdate () {
|
||||
initMarker(data.data.domain, data.data.nickname, sn, lng, lat)
|
||||
})
|
||||
}
|
||||
function moveTo(sn: string, lng: number, lat: number) {
|
||||
|
||||
function moveTo (sn: string, lng: number, lat: number) {
|
||||
let marker = markers[sn]
|
||||
if (!marker) {
|
||||
addMarker(sn, lng, lat)
|
||||
@@ -86,7 +96,7 @@ export function deviceTsaUpdate () {
|
||||
autoRotation: true
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
marker: markers,
|
||||
initMarker,
|
||||
|
||||
@@ -4,30 +4,35 @@ import { AMapConfig } from '/@/constants/index'
|
||||
|
||||
export function useGMapManage () {
|
||||
const state = reactive({
|
||||
mapEntity: null,
|
||||
mapObj: null,
|
||||
aMap: null, // Map类
|
||||
map: null, // 地图对象
|
||||
mouseTool: null,
|
||||
})
|
||||
|
||||
async function initMap (container: string, app:App) {
|
||||
AMapLoader.load({
|
||||
...AMapConfig
|
||||
}).then((AMap) => {
|
||||
state.mapObj = AMap
|
||||
state.mapEntity = new AMap.Map(container, {
|
||||
state.aMap = AMap
|
||||
state.map = new AMap.Map(container, {
|
||||
center: [113.935913, 22.525335],
|
||||
zoom: 15
|
||||
})
|
||||
state.mouseTool = new AMap.MouseTool(state.mapEntity)
|
||||
app.config.globalProperties.$aMap = state.mapEntity
|
||||
app.config.globalProperties.$aMapObj = state.mapObj
|
||||
state.mouseTool = new AMap.MouseTool(state.map)
|
||||
|
||||
// 挂在到全局
|
||||
app.config.globalProperties.$aMap = state.aMap
|
||||
app.config.globalProperties.$map = state.map
|
||||
app.config.globalProperties.$mouseTool = state.mouseTool
|
||||
}).catch(e => {
|
||||
console.log(e)
|
||||
})
|
||||
}
|
||||
|
||||
function globalPropertiesConfig (app:App) {
|
||||
initMap('g-container', app)
|
||||
}
|
||||
|
||||
return {
|
||||
globalPropertiesConfig,
|
||||
}
|
||||
|
||||
@@ -6,13 +6,14 @@ import { MapDoodleEnum } from '/@/types/map-enum'
|
||||
|
||||
export function useMouseTool () {
|
||||
const root = getRoot()
|
||||
const AMap = root.$aMapObj
|
||||
|
||||
const state = reactive({
|
||||
pinNum: 0,
|
||||
polylineNum: 0,
|
||||
PolygonNum: 0,
|
||||
currentType: '',
|
||||
})
|
||||
|
||||
function drawPin (type:MapDoodleType, getDrawCallback:Function) {
|
||||
root?.$mouseTool.marker({
|
||||
title: type + state.pinNum,
|
||||
@@ -21,6 +22,7 @@ export function useMouseTool () {
|
||||
state.pinNum++
|
||||
root?.$mouseTool.on('draw', getDrawCallback)
|
||||
}
|
||||
|
||||
function drawPolyline (type:MapDoodleType, getDrawCallback:Function) {
|
||||
root?.$mouseTool.polyline({
|
||||
strokeColor: '#2d8cf0',
|
||||
@@ -32,6 +34,7 @@ export function useMouseTool () {
|
||||
})
|
||||
root?.$mouseTool.on('draw', getDrawCallback)
|
||||
}
|
||||
|
||||
function drawPolygon (type:MapDoodleType, getDrawCallback:Function) {
|
||||
root?.$mouseTool.polygon({
|
||||
strokeColor: '#2d8cf0',
|
||||
@@ -44,10 +47,12 @@ export function useMouseTool () {
|
||||
})
|
||||
root?.$mouseTool.on('draw', getDrawCallback)
|
||||
}
|
||||
|
||||
function drawOff (type:MapDoodleType) {
|
||||
root?.$mouseTool.close()
|
||||
root?.$mouseTool.off('draw')
|
||||
}
|
||||
|
||||
function mouseTool (type: MapDoodleType, getDrawCallback: Function) {
|
||||
state.currentType = type
|
||||
switch (type) {
|
||||
@@ -65,6 +70,7 @@ export function useMouseTool () {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
mouseTool
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
<template>
|
||||
<div class="element-map-wrapper">
|
||||
<GMap/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted } from 'vue'
|
||||
import GMap from '/@/components/GMap.vue'
|
||||
export default defineComponent({
|
||||
name: 'Elements',
|
||||
components: { GMap },
|
||||
setup () {
|
||||
return {
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
@@ -130,9 +130,8 @@ import { EBizCode, EComponentName, EDownloadOwner, ELocalStorageKey, ERouterName
|
||||
import cloudapi from '/@/assets/icons/cloudapi.png'
|
||||
import { RightOutlined, CloudOutlined, CloudSyncOutlined, SyncOutlined } from '@ant-design/icons-vue'
|
||||
import { useMyStore } from '/@/store'
|
||||
import ReconnectingWebSocket from 'reconnecting-websocket'
|
||||
import websocket from '/@/api/websocket'
|
||||
import { DeviceStatus } from '/@/types/device'
|
||||
import { useConnectWebSocket } from '/@/hooks/use-connect-websocket'
|
||||
|
||||
const root = getRoot()
|
||||
const gatewayState = ref<boolean>(localStorage.getItem(ELocalStorageKey.GatewayOnline) === 'true')
|
||||
@@ -211,8 +210,10 @@ const modules = [{
|
||||
|
||||
const store = useMyStore()
|
||||
|
||||
const wsGetMsg = async (res: any) => {
|
||||
const payload = JSON.parse(res.data)
|
||||
const messageHandler = async (payload: any) => {
|
||||
if (!payload) {
|
||||
return
|
||||
}
|
||||
switch (payload.biz_code) {
|
||||
case EBizCode.DeviceOnline: {
|
||||
console.info('online: ', payload)
|
||||
@@ -241,8 +242,11 @@ const wsGetMsg = async (res: any) => {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 监听ws 消息
|
||||
useConnectWebSocket(messageHandler)
|
||||
|
||||
let bindNum: number
|
||||
let socket: ReconnectingWebSocket
|
||||
|
||||
onMounted(() => {
|
||||
apiPilot.onBackClickReg()
|
||||
@@ -260,8 +264,6 @@ onMounted(() => {
|
||||
device.data.sn = apiPilot.getAircraftSN()
|
||||
getDeviceInfo()
|
||||
|
||||
socket = websocket.init(wsGetMsg)
|
||||
|
||||
const isLoaded = apiPilot.isComponentLoaded(EComponentName.Thing)
|
||||
if (isLoaded) {
|
||||
username.value = '' + localStorage.getItem(ELocalStorageKey.Username)
|
||||
@@ -302,10 +304,6 @@ onMounted(() => {
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
socket.close()
|
||||
})
|
||||
|
||||
const connectCallback = async (arg: any) => {
|
||||
if (arg) {
|
||||
thingState.value = EStatusValue.CONNECTED
|
||||
|
||||
@@ -11,16 +11,12 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Topbar from './topbar.vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import Topbar from '/@/components/common/topbar.vue'
|
||||
import { onMounted, reactive, ref, UnwrapRef, watch } from 'vue'
|
||||
import { getPlatformInfo, getUserInfo } from '/@/api/manage'
|
||||
import websocket from '/@/api/websocket'
|
||||
import { useGMapCover } from '/@/hooks/use-g-map-cover'
|
||||
import { getRoot } from '/@/root'
|
||||
import { useMyStore } from '/@/store'
|
||||
import { ELocalStorageKey, ERouterName } from '/@/types'
|
||||
import ReconnectingWebSocket from 'reconnecting-websocket'
|
||||
import { EBizCode, ELocalStorageKey, ERouterName } from '/@/types'
|
||||
import { useConnectWebSocket } from '/@/hooks/use-connect-websocket'
|
||||
import EventBus from '/@/event-bus'
|
||||
|
||||
interface FormState {
|
||||
user: string
|
||||
@@ -28,7 +24,25 @@ interface FormState {
|
||||
}
|
||||
|
||||
const root = getRoot()
|
||||
const showLogin = ref(true)
|
||||
|
||||
const messageHandler = async (payload: any) => {
|
||||
if (!payload) {
|
||||
return
|
||||
}
|
||||
switch (payload.biz_code) {
|
||||
case EBizCode.DeviceUpgrade: {
|
||||
EventBus.emit('deviceUpgrade', payload)
|
||||
break
|
||||
}
|
||||
case EBizCode.DeviceLogUploadProgress: {
|
||||
EventBus.emit('deviceLogUploadProgress', payload)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 监听ws 消息
|
||||
useConnectWebSocket(messageHandler)
|
||||
|
||||
onMounted(() => {
|
||||
const token = localStorage.getItem(ELocalStorageKey.Token)
|
||||
@@ -34,7 +34,7 @@
|
||||
class="m0"
|
||||
type="primary"
|
||||
html-type="submit"
|
||||
:disabled="formState.user === '' || formState.password === ''"
|
||||
:disabled="loginBtnDisabled"
|
||||
@click="onSubmit"
|
||||
>
|
||||
Login
|
||||
@@ -49,23 +49,27 @@
|
||||
import djiLogo from '/@/assets/icons/dji_logo.png'
|
||||
import { LockOutlined, UserOutlined } from '@ant-design/icons-vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { reactive, ref, UnwrapRef } from 'vue'
|
||||
import { reactive, computed, UnwrapRef } from 'vue'
|
||||
import { login, LoginBody } from '/@/api/manage'
|
||||
import { getRoot } from '/@/root'
|
||||
import { ELocalStorageKey, ERouterName, EUserType } from '/@/types'
|
||||
import router from '/@/router'
|
||||
|
||||
const root = getRoot()
|
||||
|
||||
const formState: UnwrapRef<LoginBody> = reactive({
|
||||
username: 'adminPC',
|
||||
password: 'adminPC',
|
||||
flag: EUserType.Web,
|
||||
})
|
||||
|
||||
const loginBtnDisabled = computed(() => {
|
||||
return !formState.username || !formState.password
|
||||
})
|
||||
|
||||
const onSubmit = async (e: any) => {
|
||||
const result = await login(formState)
|
||||
if (result.code === 0) {
|
||||
console.log(result)
|
||||
localStorage.setItem(ELocalStorageKey.Token, result.data.access_token)
|
||||
localStorage.setItem(ELocalStorageKey.WorkspaceId, result.data.workspace_id)
|
||||
localStorage.setItem(ELocalStorageKey.Username, result.data.username)
|
||||
@@ -0,0 +1,436 @@
|
||||
|
||||
<template>
|
||||
<a-menu v-model:selectedKeys="current" mode="horizontal" @select="select">
|
||||
<a-menu-item :key="EDeviceTypeName.Aircraft" class="ml20">
|
||||
Aircraft
|
||||
</a-menu-item>
|
||||
<a-menu-item :key="EDeviceTypeName.Dock">
|
||||
Dock
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
<div class="device-table-wrap table flex-display flex-column">
|
||||
<a-table :columns="columns" :data-source="data.device" :pagination="paginationProp" @change="refreshData" row-key="device_sn" :expandedRowKeys="expandRows"
|
||||
:row-selection="rowSelection" :rowClassName="rowClassName" :scroll="{ x: '100%', y: 600 }"
|
||||
:expandIcon="expandIcon" :loading="loading">
|
||||
<template v-for="col in ['nickname']" #[col]="{ text, record }" :key="col">
|
||||
<div>
|
||||
<a-input
|
||||
v-if="editableData[record.device_sn]"
|
||||
v-model:value="editableData[record.device_sn][col]"
|
||||
style="margin: -5px 0"
|
||||
/>
|
||||
<template v-else>
|
||||
{{ text }}
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<template v-for="col in ['sn', 'workspace']" #[col]="{ text }" :key="col">
|
||||
<a-tooltip :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<!-- 固件版本 -->
|
||||
<template #firmware_version="{ record }">
|
||||
<span v-if="judgeCurrentType(EDeviceTypeName.Dock)">
|
||||
<DeviceFirmwareUpgrade :device="record"
|
||||
class="table-flex-col"
|
||||
@device-upgrade="onDeviceUpgrade"
|
||||
/>
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ record.firmware_version }}
|
||||
</span>
|
||||
</template>
|
||||
<!-- 状态 -->
|
||||
<template #status="{ text }">
|
||||
<span v-if="text" class="flex-row flex-align-center">
|
||||
<span class="mr5" style="width: 12px; height: 12px; border-radius: 50%; background-color: green;" />
|
||||
<span>Online</span>
|
||||
</span>
|
||||
<span class="flex-row flex-align-center" v-else>
|
||||
<span class="mr5" style="width: 12px; height: 12px; border-radius: 50%; background-color: red;" />
|
||||
<span>Offline</span>
|
||||
</span>
|
||||
</template>
|
||||
<!-- 操作 -->
|
||||
<template #action="{ record }">
|
||||
<div class="editable-row-operations">
|
||||
<!-- 编辑态操作 -->
|
||||
<div v-if="editableData[record.device_sn]">
|
||||
<a-tooltip title="Confirm changes">
|
||||
<span @click="save(record)" style="color: #28d445;"><CheckOutlined /></span>
|
||||
</a-tooltip>
|
||||
<a-tooltip title="Modification canceled">
|
||||
<span @click="() => delete editableData[record.device_sn]" style="color: #e70102;"><CloseOutlined /></span>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<!-- 非编辑态操作 -->
|
||||
<div v-else class="flex-align-center flex-row" style="color: #2d8cf0">
|
||||
<a-tooltip v-if="current.indexOf(EDeviceTypeName.Dock) !== -1" title="设备日志">
|
||||
<CloudServerOutlined @click="showDeviceLogUploadRecord(record)"/>
|
||||
</a-tooltip>
|
||||
<a-tooltip v-if="current.indexOf(EDeviceTypeName.Dock) !== -1" title="Hms Info">
|
||||
<FileSearchOutlined @click="showHms(record)"/>
|
||||
</a-tooltip>
|
||||
<a-tooltip title="Edit">
|
||||
<EditOutlined @click="edit(record)"/>
|
||||
</a-tooltip>
|
||||
<a-tooltip title="Delete">
|
||||
<DeleteOutlined @click="() => { deleteTip = true, deleteSn = record.device_sn }"/>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</a-table>
|
||||
<a-modal v-model:visible="deleteTip" width="450px" :closable="false" centered :okButtonProps="{ danger: true }" @ok="unbind">
|
||||
<p class="pt10 pl20" style="height: 50px;">Delete device from workspace?</p>
|
||||
<template #title>
|
||||
<div class="flex-row flex-justify-center">
|
||||
<span>Delete devices</span>
|
||||
</div>
|
||||
</template>
|
||||
</a-modal>
|
||||
|
||||
<!-- 设备升级 -->
|
||||
<DeviceFirmwareUpgradeModal title="设备升级"
|
||||
v-model:visible="deviceFirmwareUpgradeModalVisible"
|
||||
:device="selectedDevice"
|
||||
@ok="onUpgradeDeviceOk"
|
||||
></DeviceFirmwareUpgradeModal>
|
||||
|
||||
<!-- 设备日志上传记录 -->
|
||||
<DeviceLogUploadRecordDrawer
|
||||
v-model:visible="deviceLogUploadRecordVisible"
|
||||
:device="currentDevice"
|
||||
></DeviceLogUploadRecordDrawer>
|
||||
|
||||
<!-- hms 信息 -->
|
||||
<DeviceHmsDrawer
|
||||
v-model:visible="hmsVisible"
|
||||
:device="currentDevice">
|
||||
</DeviceHmsDrawer>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ColumnProps, TableState } from 'ant-design-vue/lib/table/interface'
|
||||
import { h, onMounted, reactive, ref, UnwrapRef } from 'vue'
|
||||
import { IPage } from '/@/api/http/type'
|
||||
import { BindBody, bindDevice, getBindingDevices, unbindDevice, updateDevice } from '/@/api/manage'
|
||||
import { EDeviceTypeName, ELocalStorageKey } from '/@/types'
|
||||
import { EditOutlined, CheckOutlined, CloseOutlined, DeleteOutlined, FileSearchOutlined, CloudServerOutlined } from '@ant-design/icons-vue'
|
||||
import { Device, DeviceFirmwareStatusEnum } from '/@/types/device'
|
||||
import DeviceFirmwareUpgrade from '/@/components/devices/device-upgrade/DeviceFirmwareUpgrade.vue'
|
||||
import DeviceFirmwareUpgradeModal from '/@/components/devices/device-upgrade/DeviceFirmwareUpgradeModal.vue'
|
||||
import { useDeviceFirmwareUpgrade } from '/@/components/devices/device-upgrade/use-device-upgrade'
|
||||
import { useDeviceUpgradeEvent } from '/@/components/devices/device-upgrade/use-device-upgrade-event'
|
||||
import { DeviceCmdExecuteInfo, DeviceCmdExecuteStatus } from '/@/types/device-cmd'
|
||||
import DeviceLogUploadRecordDrawer from '/@/components/devices/device-log/DeviceLogUploadRecordDrawer.vue'
|
||||
import DeviceHmsDrawer from '/@/components/devices/device-hms/DeviceHmsDrawer.vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
|
||||
interface DeviceData {
|
||||
device: Device[]
|
||||
}
|
||||
|
||||
const loading = ref(true)
|
||||
const deleteTip = ref<boolean>(false)
|
||||
const deleteSn = ref<string>()
|
||||
const columns: ColumnProps[] = [
|
||||
{ title: 'Model', dataIndex: 'device_name', width: 100, className: 'titleStyle' },
|
||||
{ title: 'SN', dataIndex: 'device_sn', width: 100, className: 'titleStyle', ellipsis: true, slots: { customRender: 'sn' } },
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'nickname',
|
||||
width: 100,
|
||||
sorter: (a: Device, b: Device) => a.nickname.localeCompare(b.nickname),
|
||||
className: 'titleStyle',
|
||||
ellipsis: true,
|
||||
slots: { customRender: 'nickname' }
|
||||
},
|
||||
{ title: 'Firmware Version', dataIndex: 'firmware_version', width: 150, className: 'titleStyle', slots: { customRender: 'firmware_version' } },
|
||||
{ title: 'Status', dataIndex: 'status', width: 100, className: 'titleStyle', slots: { customRender: 'status' } },
|
||||
{
|
||||
title: 'Workspace',
|
||||
dataIndex: 'workspace_name',
|
||||
width: 100,
|
||||
className: 'titleStyle',
|
||||
ellipsis: true,
|
||||
slots: { customRender: 'workspace' },
|
||||
customRender: ({ text, record, index }) => {
|
||||
const obj = {
|
||||
children: text,
|
||||
props: {} as any,
|
||||
}
|
||||
if (current.value.indexOf(EDeviceTypeName.Dock) !== -1) {
|
||||
if (record.domain === EDeviceTypeName.Aircraft) {
|
||||
obj.children = ''
|
||||
}
|
||||
}
|
||||
return obj
|
||||
}
|
||||
},
|
||||
{ title: 'Joined', dataIndex: 'bound_time', width: 150, sorter: (a: Device, b: Device) => a.bound_time.localeCompare(b.bound_time), className: 'titleStyle' },
|
||||
{ title: 'Last Online', dataIndex: 'login_time', width: 150, sorter: (a: Device, b: Device) => a.login_time.localeCompare(b.login_time), className: 'titleStyle' },
|
||||
{
|
||||
title: 'Actions',
|
||||
dataIndex: 'actions',
|
||||
width: 100,
|
||||
className: 'titleStyle',
|
||||
slots: { customRender: 'action' }
|
||||
},
|
||||
]
|
||||
|
||||
const expandIcon = (props: any) => {
|
||||
if (judgeCurrentType(EDeviceTypeName.Dock) && !props.expanded) {
|
||||
return h('div',
|
||||
{
|
||||
style: 'border-left: 2px solid rgb(200,200,200); border-bottom: 2px solid rgb(200,200,200); height: 16px; width: 16px; float: left;',
|
||||
class: 'mt-5 ml0',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const rowClassName = (record: any, index: number) => {
|
||||
const className = []
|
||||
if ((index & 1) === 0) {
|
||||
className.push('table-striped')
|
||||
}
|
||||
if (record.domain !== EDeviceTypeName.Dock) {
|
||||
className.push('child-row')
|
||||
}
|
||||
return className.toString().replaceAll(',', ' ')
|
||||
}
|
||||
|
||||
const expandRows = ref<string[]>([])
|
||||
const data = reactive<DeviceData>({
|
||||
device: []
|
||||
})
|
||||
|
||||
const paginationProp = reactive({
|
||||
pageSizeOptions: ['20', '50', '100'],
|
||||
showQuickJumper: true,
|
||||
showSizeChanger: true,
|
||||
pageSize: 50,
|
||||
current: 1,
|
||||
total: 0
|
||||
})
|
||||
|
||||
// 获取分页信息
|
||||
function getPaginationBody () {
|
||||
return {
|
||||
page: paginationProp.current,
|
||||
page_size: paginationProp.pageSize
|
||||
} as IPage
|
||||
}
|
||||
|
||||
const rowSelection = {
|
||||
onChange: (selectedRowKeys: (string | number)[], selectedRows: []) => {
|
||||
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows)
|
||||
},
|
||||
onSelect: (record: any, selected: boolean, selectedRows: []) => {
|
||||
console.log(record, selected, selectedRows)
|
||||
},
|
||||
onSelectAll: (selected: boolean, selectedRows: [], changeRows: []) => {
|
||||
console.log(selected, selectedRows, changeRows)
|
||||
},
|
||||
getCheckboxProps: (record: any) => ({
|
||||
disabled: judgeCurrentType(EDeviceTypeName.Dock) && record.domain !== EDeviceTypeName.Dock,
|
||||
style: judgeCurrentType(EDeviceTypeName.Dock) && record.domain !== EDeviceTypeName.Dock ? 'display: none' : ''
|
||||
}),
|
||||
}
|
||||
type Pagination = TableState['pagination']
|
||||
|
||||
const workspaceId: string = localStorage.getItem(ELocalStorageKey.WorkspaceId) || ''
|
||||
const editableData: UnwrapRef<Record<string, Device>> = reactive({})
|
||||
const current = ref([EDeviceTypeName.Aircraft])
|
||||
|
||||
function judgeCurrentType (type: EDeviceTypeName): boolean {
|
||||
return current.value.indexOf(type) !== -1
|
||||
}
|
||||
|
||||
// 设备升级
|
||||
const {
|
||||
deviceFirmwareUpgradeModalVisible,
|
||||
selectedDevice,
|
||||
onDeviceUpgrade,
|
||||
onUpgradeDeviceOk
|
||||
} = useDeviceFirmwareUpgrade(workspaceId)
|
||||
|
||||
function onDeviceUpgradeWs (payload: DeviceCmdExecuteInfo) {
|
||||
updateDevicesByWs(data.device, payload)
|
||||
}
|
||||
|
||||
function updateDevicesByWs (devices: Device[], payload: DeviceCmdExecuteInfo) {
|
||||
if (!devices || devices.length <= 0) {
|
||||
return
|
||||
}
|
||||
for (let i = 0; i < devices.length; i++) {
|
||||
if (devices[i].device_sn === payload.sn) {
|
||||
if (!payload.output) return
|
||||
const { status, progress } = payload.output
|
||||
if (status === DeviceCmdExecuteStatus.Sent || status === DeviceCmdExecuteStatus.InProgress) { // 升级中
|
||||
devices[i].firmware_status = DeviceFirmwareStatusEnum.DuringUpgrade
|
||||
devices[i].firmware_progress = progress?.percent || 0
|
||||
} else { // 终态:成功,失败,超时
|
||||
if (status === DeviceCmdExecuteStatus.Failed || status === DeviceCmdExecuteStatus.Timeout) {
|
||||
message.error(`设备(${payload.sn}) 升级失败`)
|
||||
}
|
||||
// 拉取列表
|
||||
getDevices(current.value[0], true)
|
||||
}
|
||||
return
|
||||
}
|
||||
if (devices[i].children) {
|
||||
updateDevicesByWs(devices[i].children || [], payload)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useDeviceUpgradeEvent(onDeviceUpgradeWs)
|
||||
|
||||
// 获取设备列表信息
|
||||
function getDevices (domain: string, closeLoading?: boolean) {
|
||||
if (!closeLoading) {
|
||||
loading.value = true
|
||||
}
|
||||
getBindingDevices(workspaceId, getPaginationBody(), domain).then(res => {
|
||||
if (res.code !== 0) {
|
||||
return
|
||||
}
|
||||
const resData: Device[] = res.data.list
|
||||
expandRows.value = []
|
||||
resData.forEach((val: any) => {
|
||||
if (val.children) {
|
||||
val.children = [val.children]
|
||||
}
|
||||
if (judgeCurrentType(EDeviceTypeName.Dock)) {
|
||||
expandRows.value.push(val.device_sn)
|
||||
}
|
||||
})
|
||||
data.device = resData
|
||||
paginationProp.total = res.data.pagination.total
|
||||
paginationProp.current = res.data.pagination.page
|
||||
paginationProp.pageSize = res.data.pagination.page_size
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function refreshData (page: Pagination) {
|
||||
paginationProp.current = page?.current!
|
||||
paginationProp.pageSize = page?.pageSize!
|
||||
getDevices(current.value[0])
|
||||
}
|
||||
|
||||
// 编辑
|
||||
function edit (record: Device) {
|
||||
editableData[record.device_sn] = record
|
||||
}
|
||||
|
||||
// 保存
|
||||
function save (record: Device) {
|
||||
delete editableData[record.device_sn]
|
||||
updateDevice({ nickname: record.nickname }, workspaceId, record.device_sn)
|
||||
}
|
||||
|
||||
// 删除
|
||||
function showDeleteTip (sn: any) {
|
||||
deleteTip.value = true
|
||||
}
|
||||
|
||||
// 解绑
|
||||
function unbind () {
|
||||
deleteTip.value = false
|
||||
unbindDevice(deleteSn.value?.toString()!).then(res => {
|
||||
if (res.code !== 0) {
|
||||
return
|
||||
}
|
||||
getDevices(current.value[0])
|
||||
})
|
||||
}
|
||||
|
||||
// 选择设备
|
||||
function select (item: any) {
|
||||
getDevices(item.key)
|
||||
}
|
||||
|
||||
const currentDevice = ref({} as Device)
|
||||
// 设备日志
|
||||
const deviceLogUploadRecordVisible = ref(false)
|
||||
function showDeviceLogUploadRecord (dock: Device) {
|
||||
deviceLogUploadRecordVisible.value = true
|
||||
currentDevice.value = dock
|
||||
}
|
||||
|
||||
// 健康状态
|
||||
const hmsVisible = ref<boolean>(false)
|
||||
|
||||
function showHms (dock: Device) {
|
||||
hmsVisible.value = true
|
||||
currentDevice.value = dock
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getDevices(current.value[0])
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.device-table-wrap{
|
||||
.editable-row-operations{
|
||||
div > span {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.table {
|
||||
background-color: white;
|
||||
margin: 20px;
|
||||
padding: 20px;
|
||||
height: 88vh;
|
||||
}
|
||||
.table-striped {
|
||||
background-color: #f7f9fa;
|
||||
}
|
||||
.ant-table {
|
||||
border-top: 1px solid rgb(0,0,0,0.06);
|
||||
border-bottom: 1px solid rgb(0,0,0,0.06);
|
||||
}
|
||||
.ant-table-tbody tr td {
|
||||
border: 0;
|
||||
}
|
||||
.ant-table td {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ant-table-thead tr th {
|
||||
background: white !important;
|
||||
border: 0;
|
||||
}
|
||||
th.ant-table-selection-column {
|
||||
background-color: white !important;
|
||||
}
|
||||
.ant-table-header {
|
||||
background-color: white !important;
|
||||
}
|
||||
.child-row {
|
||||
height: 70px;
|
||||
}
|
||||
.notice {
|
||||
background: $success;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
}
|
||||
.caution {
|
||||
background: orange;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
}
|
||||
.warn {
|
||||
background: red;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
+7
-8
@@ -57,13 +57,13 @@ interface MemberData {
|
||||
member: Member[]
|
||||
}
|
||||
const columns = [
|
||||
{ title: 'Account', dataIndex: 'username', width: 250, sorter: (a: Member, b: Member) => a.username.localeCompare(b.username), className: 'titleStyle' },
|
||||
{ title: 'User Type', dataIndex: 'user_type', width: 250, className: 'titleStyle' },
|
||||
{ title: 'Workspace Name', dataIndex: 'workspace_name', width: 250, className: 'titleStyle' },
|
||||
{ title: 'Mqtt Username', dataIndex: 'mqtt_username', width: 250, className: 'titleStyle', slots: { customRender: 'mqtt_username' } },
|
||||
{ title: 'Mqtt Password', dataIndex: 'mqtt_password', width: 250, className: 'titleStyle', slots: { customRender: 'mqtt_password' } },
|
||||
{ title: 'Joined', dataIndex: 'create_time', width: 250, sorter: (a: Member, b: Member) => a.create_time.localeCompare(b.create_time), className: 'titleStyle' },
|
||||
{ title: 'Action', dataIndex: 'action', className: 'titleStyle', slots: { customRender: 'action' } },
|
||||
{ title: 'Account', dataIndex: 'username', width: 150, sorter: (a: Member, b: Member) => a.username.localeCompare(b.username), className: 'titleStyle' },
|
||||
{ title: 'User Type', dataIndex: 'user_type', width: 150, className: 'titleStyle' },
|
||||
{ title: 'Workspace Name', dataIndex: 'workspace_name', width: 150, className: 'titleStyle' },
|
||||
{ title: 'Mqtt Username', dataIndex: 'mqtt_username', width: 150, className: 'titleStyle', slots: { customRender: 'mqtt_username' } },
|
||||
{ title: 'Mqtt Password', dataIndex: 'mqtt_password', width: 150, className: 'titleStyle', slots: { customRender: 'mqtt_password' } },
|
||||
{ title: 'Joined', dataIndex: 'create_time', width: 150, sorter: (a: Member, b: Member) => a.create_time.localeCompare(b.create_time), className: 'titleStyle' },
|
||||
{ title: 'Action', dataIndex: 'action', width: 100, className: 'titleStyle', slots: { customRender: 'action' } },
|
||||
]
|
||||
|
||||
const data = reactive<MemberData>({
|
||||
@@ -117,7 +117,6 @@ function getAllUsers (workspaceId: string, page: IPage) {
|
||||
data.member = userList
|
||||
paginationProp.total = res.data.pagination.total
|
||||
paginationProp.current = res.data.pagination.page
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<div class="mt5 flex-align-center flex-row flex-justify-between" style="background: #595959;">
|
||||
<div>
|
||||
<div class="flex-align-center flex-row">
|
||||
<span class="ml5 mr5"><RobotOutlined /></span>
|
||||
<span class="font-bold" :style="dockInfo[dock.gateway.sn] && dockInfo[dock.gateway.sn].mode_code !== EDockModeCode.Disconnected ? 'color: #00ee8b' : 'color: red;'">
|
||||
<span class="font-bold text-hidden" style="max-width: 80px;" :style="dockInfo[dock.gateway.sn] && dockInfo[dock.gateway.sn].mode_code !== EDockModeCode.Disconnected ? 'color: #00ee8b' : 'color: red;'">
|
||||
{{ dockInfo[dock.gateway.sn] ? EDockModeCode[dockInfo[dock.gateway.sn].mode_code] : EDockModeCode[EDockModeCode.Disconnected] }}
|
||||
</span>
|
||||
</div>
|
||||
+8
-2
@@ -8,6 +8,7 @@
|
||||
</a-row>
|
||||
</div>
|
||||
<div class="height-100">
|
||||
<a-spin :spinning="loading" :delay="1000" tip="downloading" size="large">
|
||||
<div class="scrollbar uranus-scrollbar" v-if="waylinesData.data.length !== 0" @scroll="onScroll">
|
||||
<div v-for="wayline in waylinesData.data" :key="wayline.id">
|
||||
<div class="wayline-panel" style="padding-top: 5px;" @click="selectRoute(wayline)">
|
||||
@@ -62,6 +63,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</a-modal>
|
||||
</a-spin>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -79,6 +81,7 @@ import { WaylineFile } from '/@/types/wayline'
|
||||
import { downloadFile } from '/@/utils/common'
|
||||
import { IPage } from '/@/api/http/type'
|
||||
|
||||
const loading = ref(false)
|
||||
const store = useMyStore()
|
||||
const pagination :IPage = {
|
||||
page: 1,
|
||||
@@ -154,12 +157,15 @@ function deleteWayline () {
|
||||
}
|
||||
|
||||
function downloadWayline (waylineId: string, fileName: string) {
|
||||
loading.value = true
|
||||
downloadWaylineFile(workspaceId, waylineId).then(res => {
|
||||
if (res.code && res.code !== 0) {
|
||||
if (!res) {
|
||||
return
|
||||
}
|
||||
const data = new Blob([res.data], { type: 'application/zip' })
|
||||
const data = new Blob([res], { type: 'application/zip' })
|
||||
downloadFile(data, fileName + '.kmz')
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
+29
-16
@@ -21,22 +21,23 @@
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
|
||||
import Sidebar from '../sidebar.vue'
|
||||
import Sidebar from '/@/components/common/sidebar.vue'
|
||||
import MediaPanel from '/@/components/MediaPanel.vue'
|
||||
import TaskPanel from '/@/components/TaskPanel.vue'
|
||||
import GMap from '/@/components/GMap.vue'
|
||||
import { EBizCode, ERouterName } from '/@/types'
|
||||
import { getRoot } from '/@/root'
|
||||
import { onMounted, onUnmounted, watch } from 'vue'
|
||||
import ReconnectingWebSocket from 'reconnecting-websocket'
|
||||
import { useMyStore } from '/@/store'
|
||||
import websocket from '/@/api/websocket'
|
||||
// import { enableAgoraLive, enableOthersLive } from '/@/pages/project-app/projects/livestream.vue'
|
||||
import { useConnectWebSocket } from '/@/hooks/use-connect-websocket'
|
||||
|
||||
const root = getRoot()
|
||||
const store = useMyStore()
|
||||
|
||||
const messageHandler = async (payload: any) => {
|
||||
if (!payload) {
|
||||
return
|
||||
}
|
||||
|
||||
const wsGetMsg = async (res: any) => {
|
||||
const payload = JSON.parse(res.data)
|
||||
switch (payload.biz_code) {
|
||||
case EBizCode.GatewayOsd: {
|
||||
store.commit('SET_GATEWAY_INFO', payload.data)
|
||||
@@ -78,21 +79,33 @@ const wsGetMsg = async (res: any) => {
|
||||
store.commit('SET_DEVICE_HMS_INFO', payload.data)
|
||||
break
|
||||
}
|
||||
case EBizCode.DeviceReboot:
|
||||
case EBizCode.DroneOpen:
|
||||
case EBizCode.DroneClose:
|
||||
case EBizCode.CoverOpen:
|
||||
case EBizCode.CoverClose:
|
||||
case EBizCode.PutterOpen:
|
||||
case EBizCode.PutterClose:
|
||||
case EBizCode.ChargeOpen:
|
||||
case EBizCode.ChargeClose:
|
||||
case EBizCode.DeviceFormat:
|
||||
case EBizCode.DroneFormat:
|
||||
{
|
||||
store.commit('SET_DEVICES_CMD_EXECUTE_INFO', {
|
||||
biz_code: payload.biz_code,
|
||||
timestamp: payload.timestamp,
|
||||
...payload.data,
|
||||
})
|
||||
break
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const store = useMyStore()
|
||||
// 监听ws 消息
|
||||
useConnectWebSocket(messageHandler)
|
||||
|
||||
let socket: ReconnectingWebSocket
|
||||
|
||||
onMounted(() => {
|
||||
socket = websocket.init(wsGetMsg)
|
||||
})
|
||||
onUnmounted(() => {
|
||||
socket.close()
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import '/@/styles/index.scss';
|
||||
@@ -1,518 +0,0 @@
|
||||
|
||||
<template>
|
||||
<a-menu v-model:selectedKeys="current" mode="horizontal" @select="select">
|
||||
<a-menu-item :key="EDeviceTypeName.Aircraft" class="ml20">
|
||||
Aircraft
|
||||
</a-menu-item>
|
||||
<a-menu-item :key="EDeviceTypeName.Dock">
|
||||
Dock
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
<div class="table flex-display flex-column">
|
||||
<a-table :columns="columns" :data-source="data.device" :pagination="paginationProp" @change="refreshData" row-key="device_sn" :expandedRowKeys="expandRows"
|
||||
:row-selection="rowSelection" :rowClassName="rowClassName" :scroll="{ x: '100%', y: 600 }"
|
||||
:expandIcon="expandIcon" :loading="loading">
|
||||
<template v-for="col in ['nickname']" #[col]="{ text, record }" :key="col">
|
||||
<div>
|
||||
<a-input
|
||||
v-if="editableData[record.device_sn]"
|
||||
v-model:value="editableData[record.device_sn][col]"
|
||||
style="margin: -5px 0"
|
||||
/>
|
||||
<template v-else>
|
||||
{{ text }}
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<template v-for="col in ['sn', 'workspace']" #[col]="{ text }" :key="col">
|
||||
<a-tooltip :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template #status="{ text }">
|
||||
<span v-if="text" class="flex-row flex-align-center">
|
||||
<span class="mr5" style="width: 12px; height: 12px; border-radius: 50%; background-color: green;" />
|
||||
<span>Online</span>
|
||||
</span>
|
||||
<span class="flex-row flex-align-center" v-else>
|
||||
<span class="mr5" style="width: 12px; height: 12px; border-radius: 50%; background-color: red;" />
|
||||
<span>Offline</span>
|
||||
</span>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<div class="editable-row-operations">
|
||||
<span v-if="editableData[record.device_sn]">
|
||||
<a-tooltip title="Confirm changes">
|
||||
<span @click="save(record)" style="color: #28d445;"><CheckOutlined /></span>
|
||||
</a-tooltip>
|
||||
<a-tooltip title="Modification canceled">
|
||||
<span @click="() => delete editableData[record.device_sn]" class="ml15" style="color: #e70102;"><CloseOutlined /></span>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
<span v-else class="flex-align-center flex-row" style="color: #2d8cf0">
|
||||
<a-tooltip v-if="current.indexOf(EDeviceTypeName.Dock) !== -1" title="Hms Info">
|
||||
<FileSearchOutlined @click="showHms(record)"/>
|
||||
</a-tooltip>
|
||||
<a-tooltip title="Edit">
|
||||
<EditOutlined @click="edit(record)" class="ml10" />
|
||||
</a-tooltip>
|
||||
<a-tooltip title="Delete">
|
||||
<DeleteOutlined @click="() => { deleteTip = true, deleteSn = record.device_sn }" class="ml15" />
|
||||
</a-tooltip>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</a-table>
|
||||
<a-modal v-model:visible="deleteTip" width="450px" :closable="false" centered :okButtonProps="{ danger: true }" @ok="unbind">
|
||||
<p class="pt10 pl20" style="height: 50px;">Delete device from workspace?</p>
|
||||
<template #title>
|
||||
<div class="flex-row flex-justify-center">
|
||||
<span>Delete devices</span>
|
||||
</div>
|
||||
</template>
|
||||
</a-modal>
|
||||
|
||||
<a-drawer
|
||||
title="Hms Info"
|
||||
placement="right"
|
||||
v-model:visible="hmsVisible"
|
||||
:width="800">
|
||||
<div class="flex-row flex-align-center">
|
||||
<div style="width: 240px;">
|
||||
<a-range-picker
|
||||
v-model:value="time"
|
||||
format="YYYY-MM-DD"
|
||||
:placeholder="['Start Time', 'End Time']"
|
||||
@change="onTimeChange"/>
|
||||
</div>
|
||||
<div class="ml5">
|
||||
<a-select
|
||||
style="width: 150px"
|
||||
v-model:value="param.level"
|
||||
@select="onLevelSelect">
|
||||
<a-select-option
|
||||
v-for="item in levels"
|
||||
:key="item.label"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</div>
|
||||
<div class="ml5">
|
||||
<a-select
|
||||
v-model:value="param.domain"
|
||||
:disabled="!param.children_sn || !param.device_sn"
|
||||
style="width: 150px"
|
||||
@select="onDeviceTypeSelect">
|
||||
<a-select-option
|
||||
v-for="item in deviceTypes"
|
||||
:key="item.label"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</div>
|
||||
<div class="ml5">
|
||||
<a-input-search
|
||||
v-model:value="param.message"
|
||||
placeholder="input search message"
|
||||
style="width: 200px"
|
||||
@search="getHms"/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<a-table :columns="hmsColumns" :scroll="{ x: '100%', y: 600 }" :data-source="hmsData.data" :pagination="hmsPaginationProp" @change="refreshHmsData" row-key="hms_id"
|
||||
:rowClassName="rowClassName" :loading="loading">
|
||||
<template #time="{ record }">
|
||||
<div>{{ record.create_time }}</div>
|
||||
<div :style="record.update_time ? '' : record.level === EHmsLevel.CAUTION ? 'color: orange;' :
|
||||
record.level === EHmsLevel.WARN ? 'color: red;' : 'color: #28d445;'">{{ record.update_time ?? 'It is happening...' }}</div>
|
||||
</template>
|
||||
<template #level="{ text }">
|
||||
<div class="flex-row flex-align-center">
|
||||
<div :class="text === EHmsLevel.CAUTION ? 'caution' : text === EHmsLevel.WARN ? 'warn' : 'notice'" style="width: 10px; height: 10px; border-radius: 50%;"></div>
|
||||
<div style="margin-left: 3px;">{{ EHmsLevel[text] }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-for="col in ['code', 'message']" #[col]="{ text }" :key="col">
|
||||
<a-tooltip :title="text">
|
||||
<span>{{ text }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</a-drawer>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { message } from 'ant-design-vue'
|
||||
import { ColumnProps, TableState } from 'ant-design-vue/lib/table/interface'
|
||||
import { h, onMounted, reactive, ref, UnwrapRef } from 'vue'
|
||||
import { IPage } from '/@/api/http/type'
|
||||
import { BindBody, bindDevice, getBindingDevices, getDeviceHms, HmsQueryBody, unbindDevice, updateDevice } from '/@/api/manage'
|
||||
import { EDeviceTypeName, EHmsLevel, ELocalStorageKey } from '/@/types'
|
||||
import { EditOutlined, CheckOutlined, CloseOutlined, DeleteOutlined, FileSearchOutlined } from '@ant-design/icons-vue'
|
||||
import { Device, DeviceHms } from '/@/types/device'
|
||||
import moment, { Moment } from 'moment'
|
||||
|
||||
interface DeviceData {
|
||||
device: Device[]
|
||||
}
|
||||
const loading = ref(true)
|
||||
const deleteTip = ref<boolean>(false)
|
||||
const deleteSn = ref<string>()
|
||||
const hmsVisible = ref<boolean>(false)
|
||||
const columns: ColumnProps[] = [
|
||||
{ title: 'Model', dataIndex: 'device_name', width: '10%', className: 'titleStyle' },
|
||||
{ title: 'SN', dataIndex: 'device_sn', width: '10%', className: 'titleStyle', ellipsis: true, slots: { customRender: 'sn' } },
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'nickname',
|
||||
width: '15%',
|
||||
sorter: (a: Device, b: Device) => a.nickname.localeCompare(b.nickname),
|
||||
className: 'titleStyle',
|
||||
ellipsis: true,
|
||||
slots: { customRender: 'nickname' }
|
||||
},
|
||||
{ title: 'Firmware Version', dataIndex: 'firmware_version', width: '10%', className: 'titleStyle' },
|
||||
{ title: 'Status', dataIndex: 'status', width: '100px', className: 'titleStyle', slots: { customRender: 'status' } },
|
||||
{
|
||||
title: 'Workspace',
|
||||
dataIndex: 'workspace_name',
|
||||
width: '10%',
|
||||
className: 'titleStyle',
|
||||
ellipsis: true,
|
||||
slots: { customRender: 'workspace' },
|
||||
customRender: ({ text, record, index }) => {
|
||||
const obj = {
|
||||
children: text,
|
||||
props: {} as any,
|
||||
}
|
||||
if (current.value.indexOf(EDeviceTypeName.Aircraft) !== -1 || (!record.child_device_sn && record.domain === EDeviceTypeName.Dock)) {
|
||||
return obj
|
||||
}
|
||||
|
||||
obj.props.rowSpan = record.domain === EDeviceTypeName.Dock ? 2 : 0
|
||||
return obj
|
||||
}
|
||||
},
|
||||
{ title: 'Joined', dataIndex: 'bound_time', width: '15%', sorter: (a: Device, b: Device) => a.bound_time.localeCompare(b.bound_time), className: 'titleStyle' },
|
||||
{ title: 'Last Online', dataIndex: 'login_time', width: '15%', sorter: (a: Device, b: Device) => a.login_time.localeCompare(b.login_time), className: 'titleStyle' },
|
||||
{
|
||||
title: 'Actions',
|
||||
dataIndex: 'actions',
|
||||
className: 'titleStyle',
|
||||
slots: { customRender: 'action' }
|
||||
},
|
||||
]
|
||||
const expandIcon = (props: any) => {
|
||||
if (judgeCurrentType(EDeviceTypeName.Dock) && !props.expanded) {
|
||||
return h('div',
|
||||
{
|
||||
style: 'border-left: 2px solid rgb(200,200,200); border-bottom: 2px solid rgb(200,200,200); height: 16px; width: 16px; float: left;',
|
||||
class: 'mt-5 ml0',
|
||||
})
|
||||
}
|
||||
}
|
||||
const rowClassName = (record: any, index: number) => {
|
||||
const className = []
|
||||
if ((index & 1) === 0) {
|
||||
className.push('table-striped')
|
||||
}
|
||||
if (record.domain !== EDeviceTypeName.Dock) {
|
||||
className.push('child-row')
|
||||
}
|
||||
return className.toString().replaceAll(',', ' ')
|
||||
}
|
||||
|
||||
const expandRows = ref<string[]>([])
|
||||
const data = reactive<DeviceData>({
|
||||
device: []
|
||||
})
|
||||
|
||||
const paginationProp = reactive({
|
||||
pageSizeOptions: ['20', '50', '100'],
|
||||
showQuickJumper: true,
|
||||
showSizeChanger: true,
|
||||
pageSize: 50,
|
||||
current: 1,
|
||||
total: 0
|
||||
})
|
||||
|
||||
const rowSelection = {
|
||||
onChange: (selectedRowKeys: (string | number)[], selectedRows: []) => {
|
||||
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows)
|
||||
},
|
||||
onSelect: (record: any, selected: boolean, selectedRows: []) => {
|
||||
console.log(record, selected, selectedRows)
|
||||
},
|
||||
onSelectAll: (selected: boolean, selectedRows: [], changeRows: []) => {
|
||||
console.log(selected, selectedRows, changeRows)
|
||||
},
|
||||
getCheckboxProps: (record: any) => ({
|
||||
disabled: judgeCurrentType(EDeviceTypeName.Dock) && record.domain !== EDeviceTypeName.Dock,
|
||||
style: judgeCurrentType(EDeviceTypeName.Dock) && record.domain !== EDeviceTypeName.Dock ? 'display: none' : ''
|
||||
}),
|
||||
}
|
||||
type Pagination = TableState['pagination']
|
||||
|
||||
const body: IPage = {
|
||||
page: 1,
|
||||
total: 0,
|
||||
page_size: 50
|
||||
}
|
||||
const workspaceId: string = localStorage.getItem(ELocalStorageKey.WorkspaceId)!
|
||||
|
||||
const editableData: UnwrapRef<Record<string, Device>> = reactive({})
|
||||
|
||||
const current = ref([EDeviceTypeName.Aircraft])
|
||||
|
||||
onMounted(() => {
|
||||
getDevices(workspaceId, body, current.value[0])
|
||||
})
|
||||
|
||||
function judgeCurrentType (type: EDeviceTypeName): boolean {
|
||||
return current.value.indexOf(type) !== -1
|
||||
}
|
||||
|
||||
function getDevices (workspaceId: string, body: IPage, domain: string) {
|
||||
loading.value = true
|
||||
getBindingDevices(workspaceId, body, domain).then(res => {
|
||||
if (res.code !== 0) {
|
||||
return
|
||||
}
|
||||
const resData: Device[] = res.data.list
|
||||
expandRows.value = []
|
||||
resData.forEach((val: any) => {
|
||||
if (val.children) {
|
||||
val.children = [val.children]
|
||||
}
|
||||
if (judgeCurrentType(EDeviceTypeName.Dock)) {
|
||||
expandRows.value.push(val.device_sn)
|
||||
}
|
||||
})
|
||||
data.device = resData
|
||||
paginationProp.total = res.data.pagination.total
|
||||
paginationProp.current = res.data.pagination.page
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function refreshData (page: Pagination) {
|
||||
body.page = page?.current!
|
||||
body.page_size = page?.pageSize!
|
||||
getDevices(workspaceId, body, current.value[0])
|
||||
}
|
||||
|
||||
function edit (record: Device) {
|
||||
editableData[record.device_sn] = record
|
||||
}
|
||||
|
||||
function save (record: Device) {
|
||||
delete editableData[record.device_sn]
|
||||
updateDevice({ nickname: record.nickname }, workspaceId, record.device_sn)
|
||||
}
|
||||
|
||||
function showDeleteTip (sn: any) {
|
||||
deleteTip.value = true
|
||||
}
|
||||
|
||||
function unbind () {
|
||||
deleteTip.value = false
|
||||
unbindDevice(deleteSn.value?.toString()!).then(res => {
|
||||
if (res.code !== 0) {
|
||||
return
|
||||
}
|
||||
getDevices(workspaceId, body, current.value[0])
|
||||
})
|
||||
}
|
||||
|
||||
function select (item: any) {
|
||||
getDevices(workspaceId, body, item.key)
|
||||
}
|
||||
|
||||
const hmsColumns: ColumnProps[] = [
|
||||
{ title: 'Alarm Begin | End Time', dataIndex: 'create_time', width: '25%', className: 'titleStyle', slots: { customRender: 'time' } },
|
||||
{ title: 'Level', dataIndex: 'level', width: '120px', className: 'titleStyle', slots: { customRender: 'level' } },
|
||||
{ title: 'Device', dataIndex: 'domain', width: '12%', className: 'titleStyle' },
|
||||
{ title: 'Error Code', dataIndex: 'key', width: '20%', className: 'titleStyle', slots: { customRender: 'code' } },
|
||||
{ title: 'Hms Message', dataIndex: 'message_en', className: 'titleStyle', ellipsis: true, slots: { customRender: 'message' } },
|
||||
]
|
||||
|
||||
interface DeviceHmsData {
|
||||
data: DeviceHms[]
|
||||
}
|
||||
const hmsData = reactive<DeviceHmsData>({
|
||||
data: []
|
||||
})
|
||||
|
||||
const hmsPaginationProp = reactive({
|
||||
pageSizeOptions: ['20', '50', '100'],
|
||||
showQuickJumper: true,
|
||||
showSizeChanger: true,
|
||||
pageSize: 50,
|
||||
current: 1,
|
||||
total: 0
|
||||
})
|
||||
|
||||
const hmsPage: IPage = {
|
||||
page: 1,
|
||||
total: 0,
|
||||
page_size: 50
|
||||
}
|
||||
|
||||
function showHms (dock: Device) {
|
||||
hmsVisible.value = true
|
||||
if (dock.domain === EDeviceTypeName.Dock) {
|
||||
param.domain = ''
|
||||
getDeviceHmsBySn(dock.device_sn, dock.children?.[0].device_sn ?? '')
|
||||
}
|
||||
if (dock.domain === EDeviceTypeName.Aircraft) {
|
||||
param.domain = EDeviceTypeName.Aircraft
|
||||
getDeviceHmsBySn('', dock.device_sn)
|
||||
}
|
||||
}
|
||||
|
||||
function refreshHmsData (page: Pagination) {
|
||||
hmsPage.page = page?.current!
|
||||
hmsPage.page_size = page?.pageSize!
|
||||
getHms()
|
||||
}
|
||||
|
||||
const param = reactive<HmsQueryBody>({
|
||||
sns: [],
|
||||
device_sn: '',
|
||||
children_sn: '',
|
||||
language: 'en',
|
||||
begin_time: new Date(new Date().setDate(new Date().getDate() - 7)).setHours(0, 0, 0, 0),
|
||||
end_time: new Date().setHours(23, 59, 59, 999),
|
||||
domain: '',
|
||||
level: '',
|
||||
message: ''
|
||||
})
|
||||
|
||||
const levels = [
|
||||
{
|
||||
label: 'All',
|
||||
value: ''
|
||||
}, {
|
||||
label: EHmsLevel[0],
|
||||
value: EHmsLevel.NOTICE
|
||||
}, {
|
||||
label: EHmsLevel[1],
|
||||
value: EHmsLevel.CAUTION
|
||||
}, {
|
||||
label: EHmsLevel[2],
|
||||
value: EHmsLevel.WARN
|
||||
}
|
||||
]
|
||||
|
||||
const deviceTypes = [
|
||||
{
|
||||
label: 'All',
|
||||
value: ''
|
||||
}, {
|
||||
label: EDeviceTypeName.Aircraft,
|
||||
value: EDeviceTypeName.Aircraft
|
||||
}, {
|
||||
label: EDeviceTypeName.Dock,
|
||||
value: EDeviceTypeName.Dock
|
||||
}
|
||||
]
|
||||
|
||||
const time = ref([moment(param.begin_time), moment(param.end_time)])
|
||||
|
||||
function getHms () {
|
||||
getDeviceHms(param, workspaceId, hmsPage)
|
||||
.then(res => {
|
||||
hmsPaginationProp.total = res.data.pagination.total
|
||||
hmsPaginationProp.current = res.data.pagination.page
|
||||
hmsData.data = res.data.list
|
||||
hmsData.data.forEach(hms => {
|
||||
hms.domain = hms.sn === param.children_sn ? EDeviceTypeName.Aircraft : EDeviceTypeName.Dock
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function getDeviceHmsBySn (sn: string, childSn: string) {
|
||||
param.device_sn = sn
|
||||
param.children_sn = childSn
|
||||
param.sns = [param.device_sn, param.children_sn]
|
||||
getHms()
|
||||
}
|
||||
|
||||
function onTimeChange (newTime: [Moment, Moment]) {
|
||||
param.begin_time = newTime[0].valueOf()
|
||||
param.end_time = newTime[1].valueOf()
|
||||
getHms()
|
||||
}
|
||||
|
||||
function onDeviceTypeSelect (val: string) {
|
||||
param.sns = [param.device_sn, param.children_sn]
|
||||
if (val === EDeviceTypeName.Dock) {
|
||||
param.sns = [param.device_sn, '']
|
||||
}
|
||||
if (val === EDeviceTypeName.Aircraft) {
|
||||
param.sns = ['', param.children_sn]
|
||||
}
|
||||
getHms()
|
||||
}
|
||||
|
||||
function onLevelSelect (val: number) {
|
||||
param.level = val
|
||||
getHms()
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
|
||||
.table {
|
||||
background-color: white;
|
||||
margin: 20px;
|
||||
padding: 20px;
|
||||
height: 88vh;
|
||||
}
|
||||
.table-striped {
|
||||
background-color: #f7f9fa;
|
||||
}
|
||||
.ant-table {
|
||||
border-top: 1px solid rgb(0,0,0,0.06);
|
||||
border-bottom: 1px solid rgb(0,0,0,0.06);
|
||||
}
|
||||
.ant-table-tbody tr td {
|
||||
border: 0;
|
||||
}
|
||||
.ant-table td {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ant-table-thead tr th {
|
||||
background: white !important;
|
||||
border: 0;
|
||||
}
|
||||
th.ant-table-selection-column {
|
||||
background-color: white !important;
|
||||
}
|
||||
.ant-table-header {
|
||||
background-color: white !important;
|
||||
}
|
||||
.child-row {
|
||||
height: 70px;
|
||||
}
|
||||
.notice {
|
||||
background: $success;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
}
|
||||
.caution {
|
||||
background: orange;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
}
|
||||
.warn {
|
||||
background: red;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import { createApp, ComponentCustomProperties, App as VueApp } from 'vue'
|
||||
declare module '@vue/runtime-core' {
|
||||
interface ComponentCustomProperties {
|
||||
$aMap: any
|
||||
$aMapObj: any
|
||||
$aMap: any // Map类
|
||||
$map: any // 地图对象
|
||||
$mouseTool: any
|
||||
}
|
||||
}
|
||||
|
||||
+18
-19
@@ -1,8 +1,8 @@
|
||||
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
|
||||
import { ERouterName } from '/@/types/index'
|
||||
import CreatePlan from '../pages/project-app/projects/create-plan.vue'
|
||||
import WaylinePanel from '/@/pages/project-app/projects/wayline.vue'
|
||||
import DockPanel from '/@/pages/project-app/projects/dock.vue'
|
||||
import CreatePlan from '../pages/page-web/projects/create-plan.vue'
|
||||
import WaylinePanel from '/@/pages/page-web/projects/wayline.vue'
|
||||
import DockPanel from '/@/pages/page-web/projects/dock.vue'
|
||||
import LiveAgora from '/@/components/livestream-agora.vue'
|
||||
import LiveOthers from '/@/components/livestream-others.vue'
|
||||
|
||||
@@ -11,38 +11,41 @@ const routes: Array<RouteRecordRaw> = [
|
||||
path: '/',
|
||||
redirect: '/' + ERouterName.PROJECT
|
||||
},
|
||||
// 首页
|
||||
{
|
||||
path: '/' + ERouterName.PROJECT,
|
||||
name: ERouterName.PROJECT,
|
||||
component: () => import('/@/pages/project-app/index.vue')
|
||||
component: () => import('/@/pages/page-web/index.vue')
|
||||
},
|
||||
// members, devices
|
||||
{
|
||||
path: '/' + ERouterName.HOME,
|
||||
name: ERouterName.HOME,
|
||||
component: () => import('/@/pages/project-app/home.vue'),
|
||||
component: () => import('/@/pages/page-web/home.vue'),
|
||||
children: [
|
||||
{
|
||||
path: '/' + ERouterName.MEMBERS,
|
||||
name: ERouterName.MEMBERS,
|
||||
component: () => import('/@/pages/project-app/projects/members.vue')
|
||||
component: () => import('/@/pages/page-web/projects/members.vue')
|
||||
},
|
||||
{
|
||||
path: '/' + ERouterName.DEVICES,
|
||||
name: ERouterName.DEVICES,
|
||||
component: () => import('/@/pages/project-app/projects/devices.vue')
|
||||
component: () => import('/@/pages/page-web/projects/devices.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
// workspace
|
||||
{
|
||||
path: '/' + ERouterName.WORKSPACE,
|
||||
name: ERouterName.WORKSPACE,
|
||||
component: () => import('/@/pages/project-app/projects/workspace.vue'),
|
||||
component: () => import('/@/pages/page-web/projects/workspace.vue'),
|
||||
redirect: '/' + ERouterName.TSA,
|
||||
children: [
|
||||
{
|
||||
path: '/' + ERouterName.LIVESTREAM,
|
||||
name: ERouterName.LIVESTREAM,
|
||||
component: () => import('/@/pages/project-app/projects/livestream.vue'),
|
||||
component: () => import('/@/pages/page-web/projects/livestream.vue'),
|
||||
children: [
|
||||
{
|
||||
path: ERouterName.LIVING,
|
||||
@@ -56,27 +59,27 @@ const routes: Array<RouteRecordRaw> = [
|
||||
},
|
||||
{
|
||||
path: '/' + ERouterName.TSA,
|
||||
component: () => import('/@/pages/project-app/projects/tsa.vue')
|
||||
component: () => import('/@/pages/page-web/projects/tsa.vue')
|
||||
},
|
||||
{
|
||||
path: '/' + ERouterName.LAYER,
|
||||
name: ERouterName.LAYER,
|
||||
component: () => import('/@/pages/project-app/projects/layer.vue')
|
||||
component: () => import('/@/pages/page-web/projects/layer.vue')
|
||||
},
|
||||
{
|
||||
path: '/' + ERouterName.MEDIA,
|
||||
name: ERouterName.MEDIA,
|
||||
component: () => import('/@/pages/project-app/projects/media.vue')
|
||||
component: () => import('/@/pages/page-web/projects/media.vue')
|
||||
},
|
||||
{
|
||||
path: '/' + ERouterName.WAYLINE,
|
||||
name: ERouterName.WAYLINE,
|
||||
component: () => import('/@/pages/project-app/projects/wayline.vue')
|
||||
component: () => import('/@/pages/page-web/projects/wayline.vue')
|
||||
},
|
||||
{
|
||||
path: '/' + ERouterName.TASK,
|
||||
name: ERouterName.TASK,
|
||||
component: () => import('/@/pages/project-app/projects/task.vue'),
|
||||
component: () => import('/@/pages/page-web/projects/task.vue'),
|
||||
children: [
|
||||
{
|
||||
path: ERouterName.CREATE_PLAN,
|
||||
@@ -98,6 +101,7 @@ const routes: Array<RouteRecordRaw> = [
|
||||
}
|
||||
]
|
||||
},
|
||||
// pilot
|
||||
{
|
||||
path: '/' + ERouterName.PILOT,
|
||||
name: ERouterName.PILOT,
|
||||
@@ -118,11 +122,6 @@ const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/' + ERouterName.PILOT_BIND,
|
||||
component: () => import('/@/pages/page-pilot/pilot-bind.vue')
|
||||
},
|
||||
{
|
||||
path: '/' + ERouterName.ELEMENT,
|
||||
name: ERouterName.ELEMENT,
|
||||
component: () => import('/@/pages/elements/elements.vue')
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
+24
-2
@@ -5,6 +5,7 @@ import { Device, DeviceHms, DeviceOsd, DeviceStatus, DockOsd, GatewayOsd, OSDVis
|
||||
import { getLayers } from '/@/api/layer'
|
||||
import { LayerType } from '/@/types/mapLayer'
|
||||
import { ETaskStatus, TaskInfo, WaylineFile } from '/@/types/wayline'
|
||||
import { DevicesCmdExecuteInfo } from '/@/types/device-cmd'
|
||||
|
||||
const initStateFunc = () => ({
|
||||
Layers: [
|
||||
@@ -87,7 +88,10 @@ const initStateFunc = () => ({
|
||||
},
|
||||
hmsInfo: {} as {
|
||||
[sn: string]: DeviceHms[]
|
||||
}
|
||||
},
|
||||
// 机场指令执行状态信息
|
||||
devicesCmdExecuteInfo: {
|
||||
} as DevicesCmdExecuteInfo
|
||||
})
|
||||
|
||||
export type RootStateType = ReturnType<typeof initStateFunc>
|
||||
@@ -144,7 +148,6 @@ const mutations: MutationTree<RootStateType> = {
|
||||
delete state.deviceState.deviceInfo[info.sn]
|
||||
delete state.deviceState.dockInfo[info.sn]
|
||||
delete state.hmsInfo[info.sn]
|
||||
|
||||
// delete state.markerInfo.coverMap[info.sn]
|
||||
// delete state.markerInfo.pathMap[info.sn]
|
||||
},
|
||||
@@ -171,6 +174,25 @@ const mutations: MutationTree<RootStateType> = {
|
||||
SET_DEVICE_HMS_INFO (state, info) {
|
||||
const hmsList: Array<DeviceHms> = state.hmsInfo[info.sn]
|
||||
state.hmsInfo[info.sn] = info.host.concat(hmsList ?? [])
|
||||
},
|
||||
SET_DEVICES_CMD_EXECUTE_INFO (state, info) { // 保存设备指令ws消息推送
|
||||
if (!info.sn) {
|
||||
return
|
||||
}
|
||||
if (state.devicesCmdExecuteInfo[info.sn]) {
|
||||
const index = state.devicesCmdExecuteInfo[info.sn].findIndex(cmdExecuteInfo => cmdExecuteInfo.biz_code === info.biz_code)
|
||||
if (index >= 0) {
|
||||
// 丢弃前面的消息
|
||||
if (state.devicesCmdExecuteInfo[info.sn][index].timestamp > info.timestamp) {
|
||||
return
|
||||
}
|
||||
state.devicesCmdExecuteInfo[info.sn][index] = info
|
||||
} else {
|
||||
state.devicesCmdExecuteInfo[info.sn].push(info)
|
||||
}
|
||||
} else {
|
||||
state.devicesCmdExecuteInfo[info.sn] = [info]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,18 @@
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.flex-1 {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.flex-shrink-0 {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.flex-shrink-1 {
|
||||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
//width
|
||||
.width-100vw {
|
||||
width: 100vw;
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// 机场存储容量:总容量(单位:KB)、已使用(单位:KB)
|
||||
export interface AirportStorage {
|
||||
total: number, // 单位:KB
|
||||
used: number
|
||||
}
|
||||
|
||||
// 舱盖状态
|
||||
export enum CoverStateEnum {
|
||||
Close = 0, // 关闭
|
||||
Open = 1, // 打开
|
||||
HalfOpen = 2, // 半打开
|
||||
Failed = 3 // 失败
|
||||
}
|
||||
|
||||
// 推杆状态
|
||||
export enum PutterStateEnum {
|
||||
Close = 0, // 关闭
|
||||
Open = 1, // 打开
|
||||
HalfOpen = 2, // 半打开
|
||||
Failed = 3 // 失败
|
||||
}
|
||||
|
||||
// 充电状态
|
||||
export enum ChargeStateEnum {
|
||||
NotCharge = 0, // 空闲
|
||||
Charge = 1, // 正在充电
|
||||
}
|
||||
|
||||
export interface DroneChargeState {
|
||||
state: ChargeStateEnum,
|
||||
capacity_percent: string,
|
||||
}
|
||||
|
||||
// 补光灯状态
|
||||
export enum SupplementLightStateEnum {
|
||||
Close = 0, // 关闭
|
||||
Open = 1, // 打开
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
// 机场指令集
|
||||
export enum DeviceCmd {
|
||||
// 简单指令
|
||||
DebugModeOpen = 'debug_mode_open', // 调试模式开启
|
||||
DebugModeClose = 'debug_mode_close', // 调试模式关闭
|
||||
SupplementLightOpen = 'supplement_light_open', // 打开补光灯
|
||||
SupplementLightClose = 'supplement_light_close', // 关闭补光灯
|
||||
ReturnHome = 'return_home', // 一键返航
|
||||
// 复杂指令
|
||||
DeviceReboot = 'device_reboot', // 机场重启
|
||||
DroneOpen = 'drone_open', // 飞行器开机
|
||||
DroneClose = 'drone_close', // 飞行器关机
|
||||
// DeviceCheck = 'device_check', // 一键排障(一键起飞自检)
|
||||
DeviceFormat = 'device_format', // 机场数据格式化
|
||||
DroneFormat = 'drone_format', // 飞行器数据格式化
|
||||
CoverOpen = 'cover_open', // 打开舱盖
|
||||
CoverClose = 'cover_close', // 关闭舱盖
|
||||
PutterOpen = 'putter_open', // 推杆展开
|
||||
PutterClose = 'putter_close', // 推杆闭合
|
||||
ChargeOpen = 'charge_open', // 打开充电
|
||||
ChargeClose = 'charge_close', // 关闭充电
|
||||
}
|
||||
|
||||
export interface DeviceCmdItem{
|
||||
label: string, // 标题
|
||||
status: string, // 当前状态
|
||||
operateText: string, // 按钮文字
|
||||
cmdKey: DeviceCmd, // 请求指令
|
||||
oppositeCmdKey?: DeviceCmd, // 相反状态指令
|
||||
func: string, // 处理函数
|
||||
loading: boolean // 按钮loading
|
||||
}
|
||||
|
||||
// 机场指令
|
||||
export const cmdList: DeviceCmdItem[] = [
|
||||
{
|
||||
// iconName: ,
|
||||
label: '机场系统',
|
||||
status: '工作中',
|
||||
operateText: '重启',
|
||||
cmdKey: DeviceCmd.DeviceReboot,
|
||||
func: 'deviceReboot',
|
||||
loading: false,
|
||||
// btnAnimationIconName: '',
|
||||
// operateTips: '',
|
||||
// statusColor: '',
|
||||
},
|
||||
{
|
||||
label: '飞行器',
|
||||
status: '关机',
|
||||
operateText: '开机',
|
||||
cmdKey: DeviceCmd.DroneOpen,
|
||||
oppositeCmdKey: DeviceCmd.DroneClose,
|
||||
func: 'droneStatus',
|
||||
loading: false,
|
||||
},
|
||||
{
|
||||
label: '舱盖',
|
||||
status: '关',
|
||||
operateText: '开启',
|
||||
cmdKey: DeviceCmd.CoverOpen,
|
||||
oppositeCmdKey: DeviceCmd.CoverClose,
|
||||
func: 'coverStatus',
|
||||
loading: false,
|
||||
},
|
||||
{
|
||||
label: '推杆',
|
||||
status: '闭合',
|
||||
operateText: '展开',
|
||||
cmdKey: DeviceCmd.PutterOpen,
|
||||
oppositeCmdKey: DeviceCmd.PutterClose,
|
||||
func: 'putterStatus',
|
||||
loading: false,
|
||||
},
|
||||
{
|
||||
label: '充电状态',
|
||||
status: '未充电',
|
||||
operateText: '充电',
|
||||
cmdKey: DeviceCmd.ChargeOpen,
|
||||
oppositeCmdKey: DeviceCmd.ChargeClose,
|
||||
func: 'chargeStatus',
|
||||
loading: false,
|
||||
},
|
||||
{
|
||||
label: '一键返航',
|
||||
status: '--',
|
||||
operateText: '返航',
|
||||
cmdKey: DeviceCmd.ReturnHome,
|
||||
func: 'returnHome',
|
||||
loading: false,
|
||||
},
|
||||
{
|
||||
label: '机场存储',
|
||||
status: '--',
|
||||
operateText: '格式化',
|
||||
cmdKey: DeviceCmd.DeviceFormat,
|
||||
func: 'deviceFormat',
|
||||
loading: false,
|
||||
},
|
||||
{
|
||||
label: '飞行器存储',
|
||||
status: '--',
|
||||
operateText: '格式化',
|
||||
cmdKey: DeviceCmd.DroneFormat,
|
||||
func: 'droneFormat',
|
||||
loading: false,
|
||||
},
|
||||
{
|
||||
label: '补光灯',
|
||||
status: '关',
|
||||
operateText: '打开',
|
||||
cmdKey: DeviceCmd.SupplementLightOpen,
|
||||
oppositeCmdKey: DeviceCmd.SupplementLightClose,
|
||||
func: 'supplementLightStatus',
|
||||
loading: false,
|
||||
},
|
||||
]
|
||||
|
||||
export enum DeviceCmdStatusText {
|
||||
DeviceRebootNormalText = '工作中',
|
||||
DeviceRebootInProgressText = '重启中...',
|
||||
DeviceRebootFailedText = '重启失败',
|
||||
|
||||
DroneStatusOpenNormalText = '开',
|
||||
DroneStatusOpenInProgressText = '开机中...',
|
||||
DroneStatusOpenFailedText = '关',
|
||||
DroneStatusOpenBtnText = '关机',
|
||||
|
||||
DroneStatusCloseNormalText = '关',
|
||||
DroneStatusCloseInProgressText = '关机中...',
|
||||
DroneStatusCloseFailedText = '开',
|
||||
DroneStatusCloseBtnText = '开机',
|
||||
|
||||
DeviceCoverOpenNormalText = '开',
|
||||
DeviceCoverOpenInProgressText = '开启中...',
|
||||
DeviceCoverOpenFailedText = '关',
|
||||
DeviceCoverOpenBtnText = '关闭',
|
||||
|
||||
DeviceCoverCloseNormalText = '关',
|
||||
DeviceCoverCloseInProgressText = '关闭中...',
|
||||
DeviceCoverCloseFailedText = '开',
|
||||
DeviceCoverCloseBtnText = '开启',
|
||||
|
||||
DevicePutterOpenNormalText = '展开',
|
||||
DevicePutterOpenBtnText = '闭合',
|
||||
DevicePutterOpenInProgressText = '推杆展开中',
|
||||
DevicePutterOpenFailedText = '闭合',
|
||||
|
||||
DevicePutterCloseNormalText = '闭合',
|
||||
DevicePutterCloseInProgressText = '推杆闭合中',
|
||||
DevicePutterCloseFailedText = '展开',
|
||||
DevicePutterCloseBtnText = '展开',
|
||||
|
||||
DeviceChargeOpenNormalText = '充电',
|
||||
DeviceChargeOpenInProgressText = '充电中...',
|
||||
DeviceChargeOpenFailedText = '未充电',
|
||||
DeviceChargeOpenBtnText = '断电',
|
||||
|
||||
DeviceChargeCloseNormalText = '断电',
|
||||
DeviceChargeCloseInProgressText = '断电中...',
|
||||
DeviceChargeCloseFailedText = '充电',
|
||||
DeviceChargeCloseBtnText = '充电',
|
||||
|
||||
DeviceFormatInProgressText = '格式化...',
|
||||
DeviceFormatFailedText = '格式化失败',
|
||||
|
||||
DroneFormatInProgressText = '格式化...',
|
||||
DroneFormatFailedText = '格式化失败',
|
||||
|
||||
DeviceSupplementLightOpenNormalText = '开',
|
||||
DeviceSupplementLightOpenInProgressText = '开启中...',
|
||||
DeviceSupplementLightOpenFailedText = '关',
|
||||
DeviceSupplementLightOpenBtnText = '关闭',
|
||||
|
||||
DeviceSupplementLightCloseNormalText = '关',
|
||||
DeviceSupplementLightCloseText = '关闭中...',
|
||||
DeviceSupplementLightCloseFailedText = '开',
|
||||
DeviceSupplementLightCloseBtnText = '打开',
|
||||
}
|
||||
|
||||
// cmd ws 消息状态
|
||||
export enum DeviceCmdExecuteStatus {
|
||||
Sent = 'sent', // 已下发
|
||||
InProgress = 'in_progress', // 执行中
|
||||
OK = 'ok', // 执行成功
|
||||
Failed = 'failed', // 失败
|
||||
Canceled = 'canceled', // 取消
|
||||
Timeout = 'timeout' // 超时
|
||||
}
|
||||
|
||||
export interface DeviceCmdExecuteInfo {
|
||||
biz_code: string,
|
||||
timestamp: number,
|
||||
sn: string,
|
||||
bid: string,
|
||||
output:{
|
||||
status: DeviceCmdExecuteStatus,
|
||||
progress?: {
|
||||
percent: number,
|
||||
step_key: string,
|
||||
step_result: number
|
||||
}
|
||||
}
|
||||
result: number,
|
||||
}
|
||||
|
||||
// 所有机场的指令执行状态
|
||||
export interface DevicesCmdExecuteInfo {
|
||||
[key: string]: DeviceCmdExecuteInfo[], // sn --- DeviceCmdExecuteInfo
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { DOMAIN } from '/@/types/device'
|
||||
import { commonColor } from '/@/utils/color'
|
||||
|
||||
// 日志上传状态
|
||||
export enum DeviceLogUploadStatusEnum {
|
||||
Uploading = 1, // 上传中
|
||||
Done = 2, // 完成
|
||||
Canceled = 3, // 取消
|
||||
Failed = 4, // 失败
|
||||
}
|
||||
|
||||
export const DeviceLogUploadStatusMap = {
|
||||
[DeviceLogUploadStatusEnum.Uploading]: '上传中',
|
||||
[DeviceLogUploadStatusEnum.Done]: '上传成功',
|
||||
[DeviceLogUploadStatusEnum.Canceled]: '取消上传',
|
||||
[DeviceLogUploadStatusEnum.Failed]: '上传失败',
|
||||
}
|
||||
|
||||
export const DeviceLogUploadStatusColor = {
|
||||
[DeviceLogUploadStatusEnum.Uploading]: commonColor.BLUE,
|
||||
[DeviceLogUploadStatusEnum.Done]: commonColor.NORMAL,
|
||||
[DeviceLogUploadStatusEnum.Canceled]: commonColor.WARN,
|
||||
[DeviceLogUploadStatusEnum.Failed]: commonColor.FAIL,
|
||||
}
|
||||
|
||||
// 设备日志上传 ws 消息状态
|
||||
export enum DeviceLogUploadStatus {
|
||||
FilePull = 'file_pull', // 拉取日志 可以作为 正在处理中
|
||||
FileZip = 'file_zip', // 拉取日志,日志压缩可以作为 正在处理中
|
||||
FileUploading = 'file_uploading', // 正在上传
|
||||
Canceled = 'canceled', // 取消
|
||||
Timeout = 'timeout', // 超时
|
||||
Failed = 'failed', // 失败
|
||||
OK = 'ok', // 上传成功
|
||||
// Paused = 'paused' // 暂停
|
||||
}
|
||||
|
||||
export interface DeviceLogUploadInfo {
|
||||
sn: string,
|
||||
bid: string,
|
||||
output:{
|
||||
logs_id: string
|
||||
status: DeviceLogUploadStatus,
|
||||
files: {
|
||||
device_sn: string,
|
||||
device_model_domain: DOMAIN,
|
||||
progress: number,
|
||||
result: number,
|
||||
upload_rate: number,
|
||||
status: DeviceLogUploadStatus
|
||||
}[]
|
||||
}
|
||||
result: number,
|
||||
}
|
||||
|
||||
// ws status => log status
|
||||
export const DeviceLogUploadWsStatusMap = {
|
||||
[DeviceLogUploadStatus.FilePull]: DeviceLogUploadStatusEnum.Uploading,
|
||||
[DeviceLogUploadStatus.FileZip]: DeviceLogUploadStatusEnum.Uploading,
|
||||
[DeviceLogUploadStatus.FileUploading]: DeviceLogUploadStatusEnum.Uploading,
|
||||
[DeviceLogUploadStatus.OK]: DeviceLogUploadStatusEnum.Done,
|
||||
[DeviceLogUploadStatus.Failed]: DeviceLogUploadStatusEnum.Failed,
|
||||
[DeviceLogUploadStatus.Canceled]: DeviceLogUploadStatusEnum.Canceled,
|
||||
[DeviceLogUploadStatus.Timeout]: DeviceLogUploadStatusEnum.Failed,
|
||||
}
|
||||
+185
-5
@@ -1,16 +1,188 @@
|
||||
import { EDeviceTypeName } from ".";
|
||||
import { commonColor } from '/@/utils/color'
|
||||
|
||||
export interface DeviceValue {
|
||||
key: string; // 'domain-type-subtype'
|
||||
domain: string; // 表示一个领域,作为一个命名空间,暂时分 飞机类-0, 负载类-1,RC类-2,机场类-3 4种
|
||||
type: number; // 设备类型枚举
|
||||
sub_type: number; // 设备类型枚举 负载一般表示镜头
|
||||
}
|
||||
|
||||
// domain
|
||||
export enum DOMAIN {
|
||||
DRONE = '0', // 飞行器
|
||||
PAYLOAD = '1', // 负载
|
||||
RC = '2', // 遥控
|
||||
DOCK = '3', // 机场
|
||||
}
|
||||
|
||||
// DJI飞机类型
|
||||
export enum DRONE_TYPE {
|
||||
M30 = 67,
|
||||
M300 = 60,
|
||||
Phantom4 = 11,
|
||||
Phantom4Pro = 18,
|
||||
Phantom4RTK = 59,
|
||||
Phantom4Advanced = 27,
|
||||
Mavic3EnterpriseAdvanced= 77,
|
||||
}
|
||||
|
||||
// DJI负载类型枚举值
|
||||
export enum PAYLOAD_TYPE {
|
||||
FPV = 39,
|
||||
H20 = 42,
|
||||
H20T = 43,
|
||||
H20N = 61,
|
||||
EP600 = 50,
|
||||
EP800 = 90742,
|
||||
M30D = 52,
|
||||
M30T = 53,
|
||||
XT2 = 26,
|
||||
XTS = 41,
|
||||
Z30 = 20,
|
||||
DockTopCamera = 165,
|
||||
|
||||
M3E = 66,
|
||||
M3T = 67,
|
||||
// UNKNOWN = 65535
|
||||
}
|
||||
|
||||
// RC type
|
||||
export enum RC_TYPE {
|
||||
RC = 56,
|
||||
RCPlus = 119,
|
||||
RC144 = 144,
|
||||
}
|
||||
|
||||
// DOCK type
|
||||
export enum DOCK_TYPE {
|
||||
Dock = 1,
|
||||
}
|
||||
|
||||
// 设备sub_type 从0升序
|
||||
export enum DEVICE_SUB_TYPE {
|
||||
ZERO,
|
||||
ONE,
|
||||
TWO,
|
||||
THREE,
|
||||
UNKNOWN = 65535,
|
||||
}
|
||||
|
||||
export const DEVICE_MODEL_KEY = {
|
||||
M30: `${DOMAIN.DRONE}-${DRONE_TYPE.M30}-${DEVICE_SUB_TYPE.ZERO}`,
|
||||
M30T: `${DOMAIN.DRONE}-${DRONE_TYPE.M30}-${DEVICE_SUB_TYPE.ONE}`,
|
||||
|
||||
M3E: `${DOMAIN.DRONE}-${DRONE_TYPE.Mavic3EnterpriseAdvanced}-${DEVICE_SUB_TYPE.ZERO}`,
|
||||
M3T: `${DOMAIN.DRONE}-${DRONE_TYPE.Mavic3EnterpriseAdvanced}-${DEVICE_SUB_TYPE.ONE}`,
|
||||
|
||||
M300: `${DOMAIN.DRONE}-${DRONE_TYPE.M300}-${DEVICE_SUB_TYPE.ZERO}`,
|
||||
Phantom4: `${DOMAIN.DRONE}-${DRONE_TYPE.Phantom4}-${DEVICE_SUB_TYPE.ZERO}`,
|
||||
Phantom4Pro: `${DOMAIN.DRONE}-${DRONE_TYPE.Phantom4Pro}-${DEVICE_SUB_TYPE.ZERO}`,
|
||||
Phantom4RTK: `${DOMAIN.DRONE}-${DRONE_TYPE.Phantom4RTK}-${DEVICE_SUB_TYPE.ZERO}`,
|
||||
Phantom4Advanced: `${DOMAIN.DRONE}-${DRONE_TYPE.Phantom4Advanced}-${DEVICE_SUB_TYPE.ZERO}`,
|
||||
|
||||
FPV: `${DOMAIN.PAYLOAD}-${PAYLOAD_TYPE.FPV}-${DEVICE_SUB_TYPE.ZERO}`,
|
||||
H20: `${DOMAIN.PAYLOAD}-${PAYLOAD_TYPE.H20}-${DEVICE_SUB_TYPE.ZERO}`,
|
||||
H20T: `${DOMAIN.PAYLOAD}-${PAYLOAD_TYPE.H20T}-${DEVICE_SUB_TYPE.ZERO}`,
|
||||
H20N: `${DOMAIN.PAYLOAD}-${PAYLOAD_TYPE.H20N}-${DEVICE_SUB_TYPE.ZERO}`,
|
||||
EP600: `${DOMAIN.PAYLOAD}-${PAYLOAD_TYPE.EP600}-${DEVICE_SUB_TYPE.UNKNOWN}`,
|
||||
EP800: `${DOMAIN.PAYLOAD}-${PAYLOAD_TYPE.EP800}-${DEVICE_SUB_TYPE.ZERO}`,
|
||||
M30Camera: `${DOMAIN.PAYLOAD}-${PAYLOAD_TYPE.M30D}-${DEVICE_SUB_TYPE.ZERO}`,
|
||||
M30TCamera: `${DOMAIN.PAYLOAD}-${PAYLOAD_TYPE.M30T}-${DEVICE_SUB_TYPE.ZERO}`,
|
||||
|
||||
M3ECamera: `${DOMAIN.PAYLOAD}-${PAYLOAD_TYPE.M3E}-${DEVICE_SUB_TYPE.ZERO}`,
|
||||
M3TCamera: `${DOMAIN.PAYLOAD}-${PAYLOAD_TYPE.M3T}-${DEVICE_SUB_TYPE.ZERO}`,
|
||||
// M3MCamera: `${DOMAIN.PAYLOAD}-${PAYLOAD_TYPE.M3M}-${DEVICE_SUB_TYPE.ZERO}`,
|
||||
|
||||
XT2: `${DOMAIN.PAYLOAD}-${PAYLOAD_TYPE.XT2}-${DEVICE_SUB_TYPE.ZERO}`,
|
||||
XTS: `${DOMAIN.PAYLOAD}-${PAYLOAD_TYPE.XTS}-${DEVICE_SUB_TYPE.ZERO}`,
|
||||
Z30: `${DOMAIN.PAYLOAD}-${PAYLOAD_TYPE.Z30}-${DEVICE_SUB_TYPE.ZERO}`,
|
||||
DockTopCamera: `${DOMAIN.PAYLOAD}-${PAYLOAD_TYPE.DockTopCamera}-${DEVICE_SUB_TYPE.ZERO}`,
|
||||
|
||||
RC: `${DOMAIN.RC}-${RC_TYPE.RC}-${DEVICE_SUB_TYPE.ZERO}`,
|
||||
RCPlus: `${DOMAIN.RC}-${RC_TYPE.RCPlus}-${DEVICE_SUB_TYPE.ZERO}`,
|
||||
|
||||
Dock: `${DOMAIN.DOCK}-${DOCK_TYPE.Dock}-${DEVICE_SUB_TYPE.ZERO}`,
|
||||
}
|
||||
|
||||
export const DEVICE_NAME = {
|
||||
// drone
|
||||
[DEVICE_MODEL_KEY.M30]: 'M30',
|
||||
[DEVICE_MODEL_KEY.M30T]: 'M30T',
|
||||
[DEVICE_MODEL_KEY.M3E]: 'Mavic 3E',
|
||||
[DEVICE_MODEL_KEY.M3T]: 'Mavic 3T',
|
||||
// [DEVICE_MODEL_KEY.M3M]: 'Mavic 3M',
|
||||
[DEVICE_MODEL_KEY.M300]: 'M300 RTK',
|
||||
[DEVICE_MODEL_KEY.Phantom4]: 'Phantom 4',
|
||||
[DEVICE_MODEL_KEY.Phantom4Pro]: 'Phantom 4 Pro',
|
||||
[DEVICE_MODEL_KEY.Phantom4RTK]: 'Phantom 4 RTK',
|
||||
[DEVICE_MODEL_KEY.Phantom4Advanced]: 'Phantom 4 Advanced',
|
||||
|
||||
// payload
|
||||
[DEVICE_MODEL_KEY.FPV]: 'FPV',
|
||||
[DEVICE_MODEL_KEY.H20]: 'H20',
|
||||
[DEVICE_MODEL_KEY.H20T]: 'H20T',
|
||||
[DEVICE_MODEL_KEY.H20N]: 'H20N',
|
||||
[DEVICE_MODEL_KEY.EP600]: 'P1',
|
||||
[DEVICE_MODEL_KEY.EP800]: 'L1',
|
||||
[DEVICE_MODEL_KEY.M30Camera]: 'M30 Camera',
|
||||
[DEVICE_MODEL_KEY.M30TCamera]: 'M30T Camera',
|
||||
[DEVICE_MODEL_KEY.M3ECamera]: 'Mavic 3E',
|
||||
[DEVICE_MODEL_KEY.M3TCamera]: 'Mavic 3T',
|
||||
// [DEVICE_MODEL_KEY.M3MCamera]: 'Mavic 3M',
|
||||
[DEVICE_MODEL_KEY.XT2]: 'XT2',
|
||||
[DEVICE_MODEL_KEY.XTS]: 'XTS',
|
||||
[DEVICE_MODEL_KEY.Z30]: 'Z30',
|
||||
[DEVICE_MODEL_KEY.DockTopCamera]: 'Dock Camera',
|
||||
|
||||
// rc
|
||||
[DEVICE_MODEL_KEY.RC]: 'RC',
|
||||
[DEVICE_MODEL_KEY.RCPlus]: 'RC Plus',
|
||||
|
||||
// dock
|
||||
[DEVICE_MODEL_KEY.Dock]: 'Dock',
|
||||
}
|
||||
|
||||
// 固件升级类型
|
||||
export enum DeviceFirmwareTypeEnum {
|
||||
ToUpgraded = 3, // 普通升级
|
||||
ConsistencyUpgrade =2, // 一致性升级
|
||||
}
|
||||
|
||||
// 固件升级状态
|
||||
export enum DeviceFirmwareStatusEnum {
|
||||
None = 1, // 无需升级
|
||||
ToUpgraded = 2, // 待升级
|
||||
ConsistencyUpgrade = 3, // 一致性升级
|
||||
DuringUpgrade = 4, // 升级中
|
||||
}
|
||||
|
||||
export const DeviceFirmwareStatus = {
|
||||
[DeviceFirmwareStatusEnum.None]: '',
|
||||
[DeviceFirmwareStatusEnum.ToUpgraded]: '待升级',
|
||||
[DeviceFirmwareStatusEnum.ConsistencyUpgrade]: '一致性升级',
|
||||
[DeviceFirmwareStatusEnum.DuringUpgrade]: '升级中',
|
||||
}
|
||||
|
||||
export const DeviceFirmwareStatusColor = {
|
||||
[DeviceFirmwareStatusEnum.None]: commonColor.WHITE,
|
||||
[DeviceFirmwareStatusEnum.ToUpgraded]: commonColor.BLUE,
|
||||
[DeviceFirmwareStatusEnum.ConsistencyUpgrade]: commonColor.WARN,
|
||||
[DeviceFirmwareStatusEnum.DuringUpgrade]: commonColor.NORMAL,
|
||||
}
|
||||
|
||||
export interface Device {
|
||||
device_name: string,
|
||||
device_sn: string,
|
||||
nickname: string,
|
||||
firmware_version: string,
|
||||
firmware_status: DeviceFirmwareStatusEnum,
|
||||
status: string,
|
||||
workspace_name: string,
|
||||
bound_time: string,
|
||||
login_time: string,
|
||||
children?: Device[]
|
||||
domain: string
|
||||
children?: Device[],
|
||||
domain: string,
|
||||
firmware_progress?: number, // 升级进度
|
||||
}
|
||||
|
||||
export interface DeviceStatus {
|
||||
@@ -78,7 +250,7 @@ export interface DockOsd {
|
||||
network_state: {
|
||||
type: number,
|
||||
quality: number,
|
||||
rate: number,
|
||||
rate: number,
|
||||
},
|
||||
drone_in_dock: number,
|
||||
drone_charge_state: {
|
||||
@@ -195,4 +367,12 @@ export interface DeviceHms {
|
||||
create_time: string,
|
||||
update_time: string,
|
||||
domain: string
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: 设备拓扑管理优化
|
||||
// 设备信息
|
||||
export interface DeviceInfoType {
|
||||
gateway: GatewayOsd, // 遥控器
|
||||
dock: DockOsd, // 机场
|
||||
device: DeviceOsd, // 飞机
|
||||
}
|
||||
|
||||
+20
-1
@@ -92,6 +92,25 @@ export enum EBizCode {
|
||||
DeviceOffline = 'device_offline',
|
||||
FlightTaskProgress = 'flighttask_progress',
|
||||
DeviceHms = 'device_hms',
|
||||
|
||||
// 设备指令
|
||||
DeviceReboot = 'device_reboot', // 机场重启
|
||||
DroneOpen = 'drone_open', // 飞行器开机
|
||||
DroneClose = 'drone_close', // 飞行器关机
|
||||
DeviceFormat = 'device_format', // 机场数据格式化
|
||||
DroneFormat = 'drone_format', // 飞行器数据格式化
|
||||
CoverOpen = 'cover_open', // 打开舱盖
|
||||
CoverClose = 'cover_close', // 关闭舱盖
|
||||
PutterOpen = 'putter_open', // 推杆展开
|
||||
PutterClose = 'putter_close', // 推杆闭合
|
||||
ChargeOpen = 'charge_open', // 打开充电
|
||||
ChargeClose = 'charge_close', // 关闭充电
|
||||
|
||||
// 设备升级
|
||||
DeviceUpgrade = 'ota_progress', // 设备升级
|
||||
|
||||
// 设备日志
|
||||
DeviceLogUploadProgress = 'fileupload_progress' // 设备日志上传上传
|
||||
}
|
||||
|
||||
export enum EDeviceTypeName {
|
||||
@@ -104,4 +123,4 @@ export enum EHmsLevel {
|
||||
NOTICE,
|
||||
CAUTION,
|
||||
WARN,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import { DEFAULT_PLACEHOLDER, SIZES as byteSizes, BYTE_SIZES } from './constants'
|
||||
|
||||
/**
|
||||
* 转换字节数为单位B,KB,GB...
|
||||
* 保留一位小数
|
||||
* @param bytes 字节数
|
||||
* @param holder 0字节占位符,默认 --
|
||||
* @returns
|
||||
*/
|
||||
export function bytesToSize (bytes: number, holder = DEFAULT_PLACEHOLDER, fix = 1, unit = false): string {
|
||||
if (isNaN(bytes) || bytes === 0) {
|
||||
return holder
|
||||
}
|
||||
// 兼容负数
|
||||
let prefix = ''
|
||||
if (bytes < 0) {
|
||||
bytes = 0 - bytes
|
||||
prefix = '-'
|
||||
}
|
||||
const k = 1024
|
||||
const sizes = unit ? BYTE_SIZES : byteSizes// ['B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||
return prefix + (bytes / Math.pow(k, i)).toFixed(fix) + '' + sizes[i]
|
||||
}
|
||||
|
||||
// 获取转化后数据及单位
|
||||
export function getBytesObject (bytes: number, holder = DEFAULT_PLACEHOLDER, fix = 1): {
|
||||
value: string,
|
||||
size: string
|
||||
index: number
|
||||
} {
|
||||
if (isNaN(bytes) || bytes === 0) {
|
||||
return {
|
||||
value: holder,
|
||||
size: '',
|
||||
index: -1,
|
||||
}
|
||||
}
|
||||
// 兼容负数
|
||||
let prefix = ''
|
||||
if (bytes < 0) {
|
||||
bytes = 0 - bytes
|
||||
prefix = '-'
|
||||
}
|
||||
const k = 1024
|
||||
const sizes = byteSizes// ['B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||
|
||||
return {
|
||||
value: prefix + (bytes / Math.pow(k, i)).toFixed(fix),
|
||||
size: sizes[i],
|
||||
index: i,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据最小单位返回文件大小
|
||||
* @param bytes
|
||||
* @param minUnit
|
||||
* @param fix
|
||||
* @returns
|
||||
*/
|
||||
export function bytesToSizeWithMinUnit (bytes: number, minUnit = 'B', fix = 1): string {
|
||||
const holder = `0${minUnit}`
|
||||
const sizes = byteSizes// ['B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']
|
||||
const k = 1024
|
||||
const findIndex = sizes.findIndex(item => item === minUnit)
|
||||
|
||||
const { value, size, index } = getBytesObject(bytes, holder, fix)
|
||||
// 0
|
||||
if (index === -1) {
|
||||
return holder
|
||||
}
|
||||
// 转换后单位小于传入的最小单位
|
||||
if (index < findIndex) {
|
||||
const sizeToMinUint = parseFloat(value) / (Math.pow(k, findIndex - index))
|
||||
return sizeToMinUint.toFixed(fix) + minUnit
|
||||
}
|
||||
// 其他
|
||||
return value + size
|
||||
}
|
||||
// console.log('size', bytesToSizeWithMinUnit(0))
|
||||
// console.log('size', bytesToSizeWithMinUnit(1023))
|
||||
// console.log('size', bytesToSizeWithMinUnit(1024))
|
||||
// console.log('size', bytesToSizeWithMinUnit(1000 * 1024, 'MB', 2))
|
||||
// console.log('size', bytesToSizeWithMinUnit(1024 * 1024, 'MB', 2))
|
||||
@@ -0,0 +1,8 @@
|
||||
export const commonColor = {
|
||||
WARN: '#FF9900', // 黄色
|
||||
FAIL: '#E02020', // 红色
|
||||
WHITE: '#FFFFFF', // 白色
|
||||
NORMAL: '#19BE6B', // 绿色
|
||||
BLUE: '#2B85E4', // 蓝色
|
||||
PINK: '#F7C0BA', // 粉
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
* @param data
|
||||
* @param fileName
|
||||
*/
|
||||
export function downloadFile (data: Blob, fileName: string) {
|
||||
const lable = document.createElement('a')
|
||||
lable.href = window.URL.createObjectURL(data)
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
export const DEFAULT_PLACEHOLDER = '--' // 默认占位符
|
||||
|
||||
// 全局日期格式
|
||||
export const DATE_FORMAT = 'YYYY-MM-DD HH:mm:ss'
|
||||
export const DATE_FORMAT_MINUTE = 'YYYY-MM-DD HH:mm'
|
||||
export const DATE_FORMAT_DAY = 'YYYY-MM-DD'
|
||||
export const TIME_FORMAT = 'HH:mm:ss'
|
||||
export const TIME_FORMAT_MINUTE = 'HH:mm'
|
||||
export const DATE_FORMAT_MM = 'MM-DD HH:mm'
|
||||
|
||||
export const SIZES = ['B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']
|
||||
export const BYTE_SIZES = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
||||
export const PAGE_SIZE_OPTIONS = ['20', '50', '100']
|
||||
export const PAGE_SIZE = 50
|
||||
@@ -0,0 +1,350 @@
|
||||
import { DeviceInfoType } from '/@/types/device'
|
||||
import { DeviceCmd, DeviceCmdItem, DeviceCmdExecuteInfo, DeviceCmdStatusText, DeviceCmdExecuteStatus } from '/@/types/device-cmd'
|
||||
import { AirportStorage, CoverStateEnum, PutterStateEnum, ChargeStateEnum, SupplementLightStateEnum } from '/@/types/airport-tsa'
|
||||
import { getBytesObject } from './bytes'
|
||||
import { DEFAULT_PLACEHOLDER } from './constants'
|
||||
|
||||
/**
|
||||
* 根据osd 更新信息
|
||||
* @param cmdList
|
||||
* @param deviceInfo
|
||||
* @returns
|
||||
*/
|
||||
export function updateDeviceCmdInfoByOsd (cmdList: DeviceCmdItem[], deviceInfo: DeviceInfoType) {
|
||||
const { device, dock, gateway } = deviceInfo || {}
|
||||
if (!cmdList || cmdList.length < 1) {
|
||||
return
|
||||
}
|
||||
cmdList.forEach(cmdItem => {
|
||||
if (cmdItem.loading) {
|
||||
return
|
||||
}
|
||||
if (cmdItem.cmdKey === DeviceCmd.DeviceReboot) { // 重启
|
||||
// console.log('DeviceReboot')
|
||||
} else if (cmdItem.cmdKey === DeviceCmd.DroneOpen || cmdItem.cmdKey === DeviceCmd.DroneClose) { // 飞行器开关机
|
||||
getDroneState(cmdItem, device)
|
||||
} else if (cmdItem.cmdKey === DeviceCmd.CoverOpen || cmdItem.cmdKey === DeviceCmd.CoverClose) { // 舱盖开关
|
||||
getCoverState(cmdItem, dock)
|
||||
} else if (cmdItem.cmdKey === DeviceCmd.PutterOpen || cmdItem.cmdKey === DeviceCmd.PutterClose) { // 推杆闭合展开
|
||||
getPutterState(cmdItem, dock)
|
||||
} else if (cmdItem.cmdKey === DeviceCmd.ChargeOpen || cmdItem.cmdKey === DeviceCmd.ChargeClose) { // 充电状态
|
||||
getChargeState(cmdItem, dock)
|
||||
} else if (cmdItem.cmdKey === DeviceCmd.DeviceFormat) { // 机场存储
|
||||
deviceFormat(cmdItem, dock)
|
||||
} else if (cmdItem.cmdKey === DeviceCmd.DroneFormat) { // 飞行器存储
|
||||
droneFormat(cmdItem, device)
|
||||
} else if (cmdItem.cmdKey === DeviceCmd.SupplementLightOpen || cmdItem.cmdKey === DeviceCmd.SupplementLightClose) { // 补光灯开关
|
||||
getSupplementLightState(cmdItem, dock)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 飞行器开关机
|
||||
function getDroneState (cmdItem: DeviceCmdItem, droneProperties: any) {
|
||||
if (!droneProperties) {
|
||||
cmdItem.status = DeviceCmdStatusText.DroneStatusCloseNormalText
|
||||
cmdItem.operateText = DeviceCmdStatusText.DroneStatusCloseBtnText
|
||||
if (cmdItem.cmdKey !== DeviceCmd.DroneOpen) {
|
||||
exchangeDeviceCmd(cmdItem)
|
||||
}
|
||||
} else {
|
||||
cmdItem.status = DeviceCmdStatusText.DroneStatusOpenNormalText
|
||||
cmdItem.operateText = DeviceCmdStatusText.DroneStatusOpenBtnText
|
||||
if (cmdItem.cmdKey !== DeviceCmd.DroneClose) {
|
||||
exchangeDeviceCmd(cmdItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 舱盖开关
|
||||
function getCoverState (cmdItem: DeviceCmdItem, airportProperties: any) {
|
||||
const coverState = airportProperties?.cover_state as CoverStateEnum
|
||||
|
||||
if (coverState === CoverStateEnum.Close || coverState === CoverStateEnum.Failed) {
|
||||
cmdItem.status = DeviceCmdStatusText.DeviceCoverCloseNormalText
|
||||
cmdItem.operateText = DeviceCmdStatusText.DeviceCoverCloseBtnText
|
||||
if (cmdItem.cmdKey !== DeviceCmd.CoverOpen) {
|
||||
exchangeDeviceCmd(cmdItem)
|
||||
}
|
||||
} else if (coverState === CoverStateEnum.Open || coverState === CoverStateEnum.HalfOpen) {
|
||||
cmdItem.status = DeviceCmdStatusText.DeviceCoverOpenNormalText
|
||||
cmdItem.operateText = DeviceCmdStatusText.DeviceCoverOpenBtnText
|
||||
if (cmdItem.cmdKey !== DeviceCmd.CoverClose) {
|
||||
exchangeDeviceCmd(cmdItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 推杆状态
|
||||
function getPutterState (cmdItem: DeviceCmdItem, airportProperties: any) {
|
||||
const putterState = airportProperties?.putter_state as PutterStateEnum
|
||||
if (putterState === PutterStateEnum.Close || putterState === PutterStateEnum.Failed) {
|
||||
cmdItem.status = DeviceCmdStatusText.DevicePutterCloseNormalText
|
||||
cmdItem.operateText = DeviceCmdStatusText.DevicePutterCloseBtnText
|
||||
if (cmdItem.cmdKey !== DeviceCmd.PutterOpen) {
|
||||
exchangeDeviceCmd(cmdItem)
|
||||
}
|
||||
} else if (putterState === PutterStateEnum.Open || putterState === PutterStateEnum.HalfOpen) {
|
||||
cmdItem.status = DeviceCmdStatusText.DevicePutterOpenNormalText
|
||||
cmdItem.operateText = DeviceCmdStatusText.DevicePutterOpenBtnText
|
||||
if (cmdItem.cmdKey !== DeviceCmd.PutterClose) {
|
||||
exchangeDeviceCmd(cmdItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 充电状态
|
||||
function getChargeState (cmdItem: DeviceCmdItem, airportProperties: any) {
|
||||
const chargeState = airportProperties?.drone_charge_state
|
||||
const state = chargeState?.state as ChargeStateEnum
|
||||
if (!state) return
|
||||
if (state === ChargeStateEnum.Charge) {
|
||||
cmdItem.status = DeviceCmdStatusText.DeviceChargeOpenNormalText
|
||||
cmdItem.operateText = DeviceCmdStatusText.DeviceChargeOpenBtnText
|
||||
if (cmdItem.cmdKey !== DeviceCmd.ChargeClose) {
|
||||
exchangeDeviceCmd(cmdItem)
|
||||
}
|
||||
} else if (state === ChargeStateEnum.NotCharge) {
|
||||
cmdItem.status = DeviceCmdStatusText.DeviceChargeCloseNormalText
|
||||
cmdItem.operateText = DeviceCmdStatusText.DeviceChargeCloseBtnText
|
||||
if (cmdItem.cmdKey !== DeviceCmd.ChargeOpen) {
|
||||
exchangeDeviceCmd(cmdItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 机场存储格式化
|
||||
function deviceFormat (cmdItem: DeviceCmdItem, airportProperties: any) {
|
||||
const airportStorage = airportProperties?.storage
|
||||
const value = getAirportStorage(airportStorage)
|
||||
cmdItem.status = value
|
||||
}
|
||||
|
||||
// 机场存储格式化
|
||||
function droneFormat (cmdItem: DeviceCmdItem, droneProperties: any) {
|
||||
const droneStorage = droneProperties?.storage
|
||||
const value = getAirportStorage(droneStorage)
|
||||
cmdItem.status = value
|
||||
}
|
||||
|
||||
// 获取机场存储容量
|
||||
// {
|
||||
// "total": 10000, // 单位:KB
|
||||
// "used": 500
|
||||
// }
|
||||
export function getAirportStorage (storage: AirportStorage) {
|
||||
if (!storage) {
|
||||
return DEFAULT_PLACEHOLDER
|
||||
}
|
||||
const total = storage.total
|
||||
const used = storage.used
|
||||
const byteObj = getBytesObject(total * 1024)
|
||||
const _total = byteObj.value
|
||||
const _used = getBytes(used * 1024, byteObj.index)
|
||||
return `${_used}/${_total} ${byteObj.size}`
|
||||
}
|
||||
|
||||
function getBytes (bytes: number, index: number, fixed = 1) {
|
||||
return (bytes / Math.pow(1024, index)).toFixed(fixed)
|
||||
}
|
||||
|
||||
// 补光灯状态
|
||||
function getSupplementLightState (cmdItem: DeviceCmdItem, airportProperties: any) {
|
||||
const supplementLightState = airportProperties?.supplement_light_state
|
||||
if (supplementLightState === SupplementLightStateEnum.Close) {
|
||||
cmdItem.operateText = DeviceCmdStatusText.DeviceSupplementLightCloseBtnText
|
||||
cmdItem.status = DeviceCmdStatusText.DeviceSupplementLightCloseNormalText
|
||||
if (cmdItem.cmdKey !== DeviceCmd.SupplementLightOpen) {
|
||||
exchangeDeviceCmd(cmdItem)
|
||||
}
|
||||
} else if (supplementLightState === SupplementLightStateEnum.Open) {
|
||||
cmdItem.operateText = DeviceCmdStatusText.DeviceSupplementLightOpenBtnText
|
||||
cmdItem.status = DeviceCmdStatusText.DeviceSupplementLightOpenNormalText
|
||||
if (cmdItem.cmdKey !== DeviceCmd.SupplementLightClose) {
|
||||
exchangeDeviceCmd(cmdItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 交换指令
|
||||
* @param cmd
|
||||
*/
|
||||
function exchangeDeviceCmd (cmdItem: DeviceCmdItem) {
|
||||
if (cmdItem.oppositeCmdKey) {
|
||||
const oppositeCmdKey = cmdItem.oppositeCmdKey
|
||||
cmdItem.oppositeCmdKey = cmdItem.cmdKey
|
||||
cmdItem.cmdKey = oppositeCmdKey
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 更新简单指令发送情况更新信息
|
||||
// * @param cmd
|
||||
// */
|
||||
// export function updateDeviceSingleCmdInfo (cmdItem: DeviceCmdItem) {
|
||||
// // 补光灯
|
||||
// if (cmdItem.cmdKey === DeviceCmd.SupplementLightOpen) {
|
||||
// cmdItem.status = DeviceCmdStatusText.DeviceSupplementLightOpenNormalText
|
||||
// cmdItem.operateText = DeviceCmdStatusText.DeviceSupplementLightOpenBtnText
|
||||
// exchangeDeviceCmd(cmdItem)
|
||||
// } else if (cmdItem.cmdKey === DeviceCmd.SupplementLightClose) {
|
||||
// cmdItem.status = DeviceCmdStatusText.DeviceSupplementLightCloseNormalText
|
||||
// cmdItem.operateText = DeviceCmdStatusText.DeviceSupplementLightCloseBtnText
|
||||
// exchangeDeviceCmd(cmdItem)
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 根据指令执行消息更新信息
|
||||
* @param cmd
|
||||
* @param deviceCmdExecuteInfo
|
||||
* @returns
|
||||
*/
|
||||
export function updateDeviceCmdInfoByExecuteInfo (cmdList: DeviceCmdItem[], deviceCmdExecuteInfos?: DeviceCmdExecuteInfo[]) {
|
||||
if (!deviceCmdExecuteInfos || !cmdList) {
|
||||
return
|
||||
}
|
||||
cmdList.forEach(cmdItem => {
|
||||
// 获取当前设备相应指令信息
|
||||
const deviceCmdExecuteInfo = deviceCmdExecuteInfos.find(cmdExecuteInfo => cmdExecuteInfo.biz_code === cmdItem.cmdKey)
|
||||
if (deviceCmdExecuteInfo) {
|
||||
if (cmdItem.cmdKey === DeviceCmd.DeviceReboot) { // 重启
|
||||
if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.InProgress) {
|
||||
cmdItem.status = DeviceCmdStatusText.DeviceRebootInProgressText
|
||||
cmdItem.loading = true
|
||||
} else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.Failed) {
|
||||
cmdItem.status = DeviceCmdStatusText.DeviceRebootFailedText
|
||||
cmdItem.loading = false
|
||||
} else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.OK) {
|
||||
cmdItem.status = DeviceCmdStatusText.DeviceRebootNormalText
|
||||
cmdItem.loading = false
|
||||
}
|
||||
} else if (cmdItem.cmdKey === DeviceCmd.DroneOpen) { // 飞行器开关机
|
||||
if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.InProgress) {
|
||||
cmdItem.status = DeviceCmdStatusText.DroneStatusOpenInProgressText
|
||||
cmdItem.loading = true
|
||||
} else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.Failed) {
|
||||
cmdItem.status = DeviceCmdStatusText.DroneStatusOpenFailedText
|
||||
cmdItem.loading = false
|
||||
} else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.OK) {
|
||||
cmdItem.status = DeviceCmdStatusText.DroneStatusOpenNormalText
|
||||
cmdItem.operateText = DeviceCmdStatusText.DroneStatusOpenBtnText
|
||||
exchangeDeviceCmd(cmdItem)
|
||||
cmdItem.loading = false
|
||||
}
|
||||
} else if (cmdItem.cmdKey === DeviceCmd.DroneClose) {
|
||||
if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.InProgress) {
|
||||
cmdItem.status = DeviceCmdStatusText.DroneStatusCloseInProgressText
|
||||
cmdItem.loading = true
|
||||
} else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.Failed) {
|
||||
cmdItem.status = DeviceCmdStatusText.DroneStatusCloseFailedText
|
||||
cmdItem.loading = false
|
||||
} else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.OK) {
|
||||
cmdItem.status = DeviceCmdStatusText.DroneStatusCloseNormalText
|
||||
cmdItem.operateText = DeviceCmdStatusText.DroneStatusCloseBtnText
|
||||
exchangeDeviceCmd(cmdItem)
|
||||
cmdItem.loading = false
|
||||
}
|
||||
} else if (cmdItem.cmdKey === DeviceCmd.CoverOpen) { // 舱盖开关
|
||||
if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.InProgress) {
|
||||
cmdItem.status = DeviceCmdStatusText.DeviceCoverOpenInProgressText
|
||||
cmdItem.loading = true
|
||||
} else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.Failed) {
|
||||
cmdItem.status = DeviceCmdStatusText.DeviceCoverOpenFailedText
|
||||
cmdItem.loading = false
|
||||
} else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.OK) {
|
||||
cmdItem.status = DeviceCmdStatusText.DeviceCoverOpenNormalText
|
||||
cmdItem.operateText = DeviceCmdStatusText.DeviceCoverOpenBtnText
|
||||
exchangeDeviceCmd(cmdItem)
|
||||
cmdItem.loading = false
|
||||
}
|
||||
} else if (cmdItem.cmdKey === DeviceCmd.CoverClose) {
|
||||
if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.InProgress) {
|
||||
cmdItem.status = DeviceCmdStatusText.DeviceCoverCloseInProgressText
|
||||
cmdItem.loading = true
|
||||
} else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.Failed) {
|
||||
cmdItem.status = DeviceCmdStatusText.DeviceCoverCloseFailedText
|
||||
cmdItem.loading = false
|
||||
} else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.OK) {
|
||||
cmdItem.status = DeviceCmdStatusText.DeviceCoverCloseNormalText
|
||||
cmdItem.operateText = DeviceCmdStatusText.DeviceCoverCloseBtnText
|
||||
exchangeDeviceCmd(cmdItem)
|
||||
cmdItem.loading = false
|
||||
}
|
||||
} else if (cmdItem.cmdKey === DeviceCmd.PutterOpen) { // 推杆闭合展开
|
||||
if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.InProgress) {
|
||||
cmdItem.status = DeviceCmdStatusText.DevicePutterOpenInProgressText
|
||||
cmdItem.loading = true
|
||||
} else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.Failed) {
|
||||
cmdItem.status = DeviceCmdStatusText.DevicePutterOpenFailedText
|
||||
cmdItem.loading = false
|
||||
} else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.OK) {
|
||||
cmdItem.status = DeviceCmdStatusText.DevicePutterOpenNormalText
|
||||
cmdItem.operateText = DeviceCmdStatusText.DevicePutterOpenBtnText
|
||||
exchangeDeviceCmd(cmdItem)
|
||||
cmdItem.loading = false
|
||||
}
|
||||
} else if (cmdItem.cmdKey === DeviceCmd.PutterClose) {
|
||||
if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.InProgress) {
|
||||
cmdItem.status = DeviceCmdStatusText.DevicePutterCloseInProgressText
|
||||
cmdItem.loading = true
|
||||
} else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.Failed) {
|
||||
cmdItem.status = DeviceCmdStatusText.DevicePutterCloseFailedText
|
||||
cmdItem.loading = false
|
||||
} else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.OK) {
|
||||
cmdItem.status = DeviceCmdStatusText.DevicePutterCloseNormalText
|
||||
cmdItem.operateText = DeviceCmdStatusText.DevicePutterCloseBtnText
|
||||
exchangeDeviceCmd(cmdItem)
|
||||
cmdItem.loading = false
|
||||
}
|
||||
} else if (cmdItem.cmdKey === DeviceCmd.ChargeOpen) { // 充电状态
|
||||
if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.InProgress) {
|
||||
cmdItem.status = DeviceCmdStatusText.DeviceChargeOpenInProgressText
|
||||
cmdItem.loading = true
|
||||
} else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.Failed) {
|
||||
cmdItem.status = DeviceCmdStatusText.DeviceChargeOpenFailedText
|
||||
cmdItem.loading = false
|
||||
} else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.OK) {
|
||||
cmdItem.status = DeviceCmdStatusText.DeviceChargeOpenNormalText
|
||||
cmdItem.operateText = DeviceCmdStatusText.DeviceChargeOpenBtnText
|
||||
exchangeDeviceCmd(cmdItem)
|
||||
cmdItem.loading = false
|
||||
}
|
||||
} else if (cmdItem.cmdKey === DeviceCmd.ChargeClose) {
|
||||
if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.InProgress) {
|
||||
cmdItem.status = DeviceCmdStatusText.DeviceChargeCloseInProgressText
|
||||
cmdItem.loading = true
|
||||
} else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.Failed) {
|
||||
cmdItem.status = DeviceCmdStatusText.DeviceChargeCloseFailedText
|
||||
cmdItem.loading = false
|
||||
} else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.OK) {
|
||||
cmdItem.status = DeviceCmdStatusText.DeviceChargeCloseNormalText
|
||||
cmdItem.operateText = DeviceCmdStatusText.DeviceChargeCloseBtnText
|
||||
exchangeDeviceCmd(cmdItem)
|
||||
cmdItem.loading = false
|
||||
}
|
||||
} else if (cmdItem.cmdKey === DeviceCmd.DeviceFormat) { // 机场存储
|
||||
if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.InProgress) {
|
||||
cmdItem.status = DeviceCmdStatusText.DeviceFormatInProgressText
|
||||
cmdItem.loading = true
|
||||
} else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.Failed) {
|
||||
cmdItem.status = DeviceCmdStatusText.DeviceFormatFailedText
|
||||
cmdItem.loading = false
|
||||
} else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.OK) {
|
||||
cmdItem.loading = false
|
||||
}
|
||||
} else if (cmdItem.cmdKey === DeviceCmd.DroneFormat) { // 飞行器存储
|
||||
if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.InProgress) {
|
||||
cmdItem.status = DeviceCmdStatusText.DroneFormatInProgressText
|
||||
cmdItem.loading = true
|
||||
} else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.Failed) {
|
||||
cmdItem.status = DeviceCmdStatusText.DroneFormatFailedText
|
||||
cmdItem.loading = false
|
||||
} else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.OK) {
|
||||
cmdItem.loading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* 加载图片
|
||||
* @param url
|
||||
* @returns
|
||||
*/
|
||||
export function urlToImage (url: string) {
|
||||
return new Promise<HTMLImageElement>((resolve, reject) => {
|
||||
const image = new Image()
|
||||
image.src = url
|
||||
image.onload = () => { resolve(image) }
|
||||
image.onerror = () => { reject(new Error('image load error')) }
|
||||
})
|
||||
}
|
||||
|
||||
export interface CompressImageData {
|
||||
blob: Blob | null;
|
||||
imageData: ImageData;
|
||||
}
|
||||
export function compressImage (imgToCompress: HTMLImageElement, targetWidth: number, targetHeight: number): Promise<CompressImageData> | undefined {
|
||||
// resizing the image
|
||||
const canvas = document.createElement('canvas')
|
||||
const context = canvas.getContext('2d')
|
||||
if (context) {
|
||||
const iWidth = imgToCompress.width
|
||||
const iHeight = imgToCompress.height
|
||||
const iRatio = iWidth / iHeight // 图像宽高比
|
||||
const tRatio = targetWidth / targetHeight // 目标宽高比
|
||||
let dw = targetWidth
|
||||
let dh = targetHeight
|
||||
let dx = 0
|
||||
let dy = 0
|
||||
if (iRatio > tRatio) {
|
||||
// 如果图像宽高比比目标宽高比要大,说明图像比目标尺寸更宽,这时候我们应该按照高度缩放比来进行缩放宽度
|
||||
dw = (targetHeight / iHeight) * iWidth
|
||||
// 宽度溢出,应该放在中间
|
||||
dx = -(dw - targetWidth) / 2
|
||||
} else {
|
||||
// 否则说明图像比目标尺寸更高,按照宽度缩放比来缩放高度
|
||||
dh = (targetWidth / iWidth) * iHeight
|
||||
// 高度溢出,应该放在中间
|
||||
dy = -(dh - targetHeight) / 2
|
||||
}
|
||||
|
||||
canvas.width = targetWidth
|
||||
canvas.height = targetHeight
|
||||
|
||||
context.drawImage(
|
||||
imgToCompress,
|
||||
dx,
|
||||
dy,
|
||||
dw,
|
||||
dh,
|
||||
)
|
||||
|
||||
return new Promise<CompressImageData>((resolve) => {
|
||||
const imageData = context.getImageData(0, 0, canvas.width, canvas.height)
|
||||
|
||||
canvas.toBlob(blob => resolve({
|
||||
blob,
|
||||
imageData,
|
||||
}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据资源url下载文件
|
||||
* @param url
|
||||
* @param fileName
|
||||
*/
|
||||
export function download (url: string, fileName = ''): void {
|
||||
const aLink = document.createElement('a')
|
||||
aLink.style.display = 'none'
|
||||
aLink.download = fileName
|
||||
aLink.href = url
|
||||
document.body.appendChild(aLink)
|
||||
// 避免新开页面,闪烁
|
||||
// aLink.target = '_blank'
|
||||
aLink.click()
|
||||
document.body.removeChild(aLink)
|
||||
// aLink.remove()
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import {
|
||||
DATE_FORMAT,
|
||||
DEFAULT_PLACEHOLDER
|
||||
} from '/@/utils/constants'
|
||||
import moment, { Moment } from 'moment'
|
||||
|
||||
// 时间字符串 或者 Unix 时间戳(毫秒数)
|
||||
export function formatDateTime (time: string | number, format = DATE_FORMAT) {
|
||||
return time ? moment(time, format) : DEFAULT_PLACEHOLDER
|
||||
}
|
||||
|
||||
// Unix 时间戳 (秒)
|
||||
export function formatUnixTime (time: number, format = DATE_FORMAT): string {
|
||||
return time ? moment.unix(time).format(format) : DEFAULT_PLACEHOLDER
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import { message } from 'ant-design-vue'
|
||||
import ReconnectingWebSocket from 'reconnecting-websocket'
|
||||
|
||||
interface WebSocketOptions {
|
||||
data: any
|
||||
cache?: boolean | string
|
||||
destroyCache?: string
|
||||
}
|
||||
|
||||
export interface MessageHandler {
|
||||
(data : {[key: string]: any}): void
|
||||
}
|
||||
|
||||
/**
|
||||
* ConnectWebSocket 类
|
||||
* TODO: 优化messageHandler: EventEmitter。暂时传入回调函数
|
||||
*/
|
||||
class ConnectWebSocket {
|
||||
_url: string
|
||||
_socket: ReconnectingWebSocket | null
|
||||
_hasInit: boolean
|
||||
_messageHandler: MessageHandler | null
|
||||
|
||||
constructor (url: string) {
|
||||
this._url = url
|
||||
this._socket = null
|
||||
this._hasInit = false
|
||||
this._messageHandler = null
|
||||
}
|
||||
|
||||
initSocket () {
|
||||
if (this._hasInit) {
|
||||
return
|
||||
}
|
||||
if (!this._url) {
|
||||
return
|
||||
}
|
||||
|
||||
// 会自动重连,无需处理重连逻辑
|
||||
this._socket = new ReconnectingWebSocket(this._url, [], {
|
||||
maxReconnectionDelay: 20000, // 断开后最大的重连时间: 20s,每多一次重连,会增加 1.3 倍,5 * 1.3 * 1.3 * 1.3...
|
||||
minReconnectionDelay: 5000, // 断开后最短的重连时间: 5s
|
||||
maxRetries: 5
|
||||
})
|
||||
|
||||
this._hasInit = true
|
||||
|
||||
this._socket.addEventListener('open', this._onOpen.bind(this))
|
||||
this._socket.addEventListener('close', this._onClose.bind(this))
|
||||
this._socket.addEventListener('error', this._onError.bind(this))
|
||||
this._socket.addEventListener('message', this._onMessage.bind(this))
|
||||
}
|
||||
|
||||
_onOpen () {
|
||||
console.log('连接成功')
|
||||
}
|
||||
|
||||
_onClose () {
|
||||
console.log('连接已断开')
|
||||
}
|
||||
|
||||
_onError () {
|
||||
console.log('连接 error')
|
||||
}
|
||||
|
||||
registerMessageHandler (messageHandler: MessageHandler) {
|
||||
this._messageHandler = messageHandler
|
||||
}
|
||||
|
||||
_onMessage (msg: MessageEvent) {
|
||||
const data = JSON.parse(msg.data)
|
||||
this._messageHandler && this._messageHandler(data)
|
||||
// console.log('接受消息', message)
|
||||
}
|
||||
|
||||
sendMessage = (message: WebSocketOptions): void => {
|
||||
this._socket?.send(JSON.stringify(message.data))
|
||||
}
|
||||
|
||||
close () {
|
||||
this._socket?.close()
|
||||
}
|
||||
}
|
||||
|
||||
export default ConnectWebSocket
|
||||
@@ -0,0 +1,8 @@
|
||||
import { ELocalStorageKey } from '/@/types/enums'
|
||||
import { CURRENT_CONFIG } from '/@/api/http/config'
|
||||
|
||||
export function getWebsocketUrl () {
|
||||
const token: string = localStorage.getItem(ELocalStorageKey.Token) || '' as string
|
||||
const url = CURRENT_CONFIG.websocketURL + '?x-auth-token=' + encodeURI(token)
|
||||
return url
|
||||
}
|
||||
+2
-2
@@ -25,6 +25,6 @@
|
||||
"src/**/*.d.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"src/vendors/coordtransform.js"
|
||||
]
|
||||
"src/vendors/coordtransform.js"
|
||||
]
|
||||
}
|
||||
+2901
-2919
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
Referência em uma Nova Issue
Bloquear um usuário