sean.zhou
2 years ago
69 changed files with 7059 additions and 4216 deletions
@ -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 |
||||||
|
} |
@ -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() |
|
||||||
} |
|
||||||
} |
|
@ -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() |
||||||
|
}) |
||||||
|
} |
@ -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> |
|
@ -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> |
@ -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> |
|
@ -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, |
||||||
|
} |
@ -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', // 粉
|
||||||
|
} |
@ -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 |
||||||
|
} |
Loading…
Reference in new issue