|
|
@@ -1,75 +1,64 @@
|
|
|
<template>
|
|
|
<div class="deviceList">
|
|
|
- <Search :onClickSearch="async () => { }" :onClickReset="async () => { }" />
|
|
|
+ <Search :selectedRowKeys="state.selectedRowKeys" :onClickDelete="onClickBatchDelete"
|
|
|
+ :onClickSearch="async () => { }" :onClickReset="async () => { }" />
|
|
|
<div class="deviceList-table">
|
|
|
- <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: 500 }" :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]" />
|
|
|
- <template v-else>
|
|
|
- <a-tooltip :title="text">
|
|
|
- {{ text }}
|
|
|
- </a-tooltip>
|
|
|
- </template>
|
|
|
- </div>
|
|
|
+ <a-table :scroll="{ x: '100%', y: 500 }" :childrenColumnName="null" rowKey="device_sn"
|
|
|
+ :loading="state.listLoading" :columns="columns" :dataSource="state.list" @change="refreshData"
|
|
|
+ :rowClassName="rowClassName" :pagination="paginationConfig" :rowSelection="rowSelection">
|
|
|
+ <!-- 设备型号 -->
|
|
|
+ <template #device_name="{ record }">
|
|
|
+ <CustomCell :record="record" fieldName="device_name" :showIcon="true" />
|
|
|
+ </template>
|
|
|
+ <!-- 设备SN -->
|
|
|
+ <template #device_sn="{ record }">
|
|
|
+ <CustomCell :record="record" fieldName="device_sn" />
|
|
|
</template>
|
|
|
- <template v-for="col in ['sn', 'workspace']" #[col]="{ text }" :key="col">
|
|
|
- <a-tooltip :title="text">
|
|
|
- <span>{{ text }}</span>
|
|
|
- </a-tooltip>
|
|
|
+ <!-- 设备名称 -->
|
|
|
+ <template #nickname="{ record }">
|
|
|
+ <CustomCell :record="record" fieldName="nickname" :isEdit="!!state.editableData[record.device_sn]" />
|
|
|
</template>
|
|
|
<!-- 固件版本 -->
|
|
|
<template #firmware_version="{ record }">
|
|
|
- <span v-if="record.domin === 3">
|
|
|
- <DeviceFirmwareUpgrade :device="record" class="table-flex-col" @device-upgrade="onDeviceUpgrade" />
|
|
|
- </span>
|
|
|
- <span v-else>
|
|
|
- {{ record.firmware_version }}
|
|
|
- </span>
|
|
|
+ <CustomCell :record="record" fieldName="firmware_version" />
|
|
|
</template>
|
|
|
<!-- 固件升级 -->
|
|
|
- <template #firmware_status="{ text }">
|
|
|
- <div v-if="text === -1">
|
|
|
- 不支持
|
|
|
- </div>
|
|
|
- <div v-else>
|
|
|
- {{ DeviceFirmwareStatus[text] }}
|
|
|
- </div>
|
|
|
+ <template #firmware_status="{ record }">
|
|
|
+ <DeviceFirmwareUpgrade :device="record" />
|
|
|
</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>在线</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>离线</span>
|
|
|
- </span>
|
|
|
+ <!-- 当前状态 -->
|
|
|
+ <template #status="{ record }">
|
|
|
+ <CustomCell :record="record" fieldName="status" />
|
|
|
+ </template>
|
|
|
+ <!-- 加入项目时间 -->
|
|
|
+ <template #bound_time="{ record }">
|
|
|
+ <CustomCell :record="record" fieldName="bound_time" />
|
|
|
+ </template>
|
|
|
+ <!-- 最后在线时间 -->
|
|
|
+ <template #login_time="{ record }">
|
|
|
+ <CustomCell :record="record" fieldName="login_time" />
|
|
|
</template>
|
|
|
<!-- 操作 -->
|
|
|
<template #action="{ record }">
|
|
|
<!-- 编辑态操作 -->
|
|
|
- <div v-if="editableData[record.device_sn]">
|
|
|
+ <div v-if="state.editableData[record.device_sn]">
|
|
|
<a-tooltip title="确定">
|
|
|
- <CheckOutlined style="color: #28d445;margin-right: 10px;" @click="save(record)" />
|
|
|
+ <CheckOutlined style="color: #28d445;margin-right: 10px;" @click="onClickSave(record)" />
|
|
|
</a-tooltip>
|
|
|
<a-tooltip title="取消">
|
|
|
- <CloseOutlined style="color: #e70102;" @click="() => delete editableData[record.device_sn]" />
|
|
|
+ <CloseOutlined style="color: #e70102;" @click="() => delete state.editableData[record.device_sn]" />
|
|
|
</a-tooltip>
|
|
|
</div>
|
|
|
<!-- 非编辑态操作 -->
|
|
|
<div v-else class="flex-align-center flex-row" style="color: #2d8cf0">
|
|
|
<a-tooltip title="设备日志">
|
|
|
- <CloudServerOutlined style="margin-right: 10px;" @click="showDeviceLogUploadRecord(record)" />
|
|
|
+ <CloudServerOutlined style="margin-right: 10px;" @click="onClickDeviceLog(record)" />
|
|
|
</a-tooltip>
|
|
|
<a-tooltip title="告警信息">
|
|
|
- <FileSearchOutlined style="margin-right: 10px;" @click="showHms(record)" />
|
|
|
+ <FileSearchOutlined style="margin-right: 10px;" @click="onClickDeviceHms(record)" />
|
|
|
</a-tooltip>
|
|
|
<a-tooltip title="编辑">
|
|
|
- <EditOutlined style="margin-right: 10px;" @click="edit(record)" />
|
|
|
+ <EditOutlined style="margin-right: 10px;" @click="onClickEdit(record)" />
|
|
|
</a-tooltip>
|
|
|
<a-tooltip title="删除">
|
|
|
<DeleteOutlined @click="onClickDelete(record)" />
|
|
|
@@ -77,35 +66,76 @@
|
|
|
</div>
|
|
|
</template>
|
|
|
</a-table>
|
|
|
- <!-- 设备升级 -->
|
|
|
- <DeviceFirmwareUpgradeModal title="设备升级" v-model:visible="deviceFirmwareUpgradeModalVisible"
|
|
|
- :device="selectedDevice" @ok="onUpgradeDeviceOk" />
|
|
|
- <!-- 设备日志上传记录 -->
|
|
|
- <DeviceLogUploadRecordDrawer v-model:visible="deviceLogUploadRecordVisible" :device="currentDevice" />
|
|
|
- <!-- hms 信息 -->
|
|
|
- <DeviceHmsDrawer v-model:visible="hmsVisible" :device="currentDevice" />
|
|
|
</div>
|
|
|
</div>
|
|
|
+ <!-- 设备日志 -->
|
|
|
+ <DeviceLogUploadRecordDrawer v-model:visible="state.deviceLogDrawerVisible" :device="state.currentDevice" />
|
|
|
+ <!-- 告警信息 -->
|
|
|
+ <DeviceHmsDrawer v-model:visible="state.deviceHmsDrawerVisible" :device="state.currentDevice" />
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
-import { h, onMounted, reactive, ref, UnwrapRef } from 'vue'
|
|
|
-import { Modal, notification } from 'ant-design-vue'
|
|
|
-import { EditOutlined, CheckOutlined, CloseOutlined, DeleteOutlined, FileSearchOutlined, CloudServerOutlined } from '@ant-design/icons-vue'
|
|
|
-import Search from './components/Search.vue'
|
|
|
-import { getBindingDevices, unbindDevice, updateDevice } from '/@/api/manage'
|
|
|
-import { Device, DeviceFirmwareStatus, 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 { IPage } from '/@/api/http/type'
|
|
|
-import { EDeviceTypeName, ELocalStorageKey } from '/@/types'
|
|
|
+import { reactive, onMounted } from 'vue';
|
|
|
+import { Modal } from 'ant-design-vue';
|
|
|
+import { EditOutlined, CheckOutlined, CloseOutlined, DeleteOutlined, FileSearchOutlined, CloudServerOutlined } from '@ant-design/icons-vue';
|
|
|
+import Search from './components/Search.vue';
|
|
|
+import CustomCell from './components/CustomCell.vue';
|
|
|
+import DeviceFirmwareUpgrade from '/@/components/devices/device-upgrade/DeviceFirmwareUpgrade.vue';
|
|
|
+import DeviceLogUploadRecordDrawer from '/@/components/devices/device-log/DeviceLogUploadRecordDrawer.vue';
|
|
|
+import DeviceHmsDrawer from '/@/components/devices/device-hms/DeviceHmsDrawer.vue';
|
|
|
+import { getBindingDevices, updateDevice, unbindDevice } from '/@/api/manage';
|
|
|
+
|
|
|
+interface State {
|
|
|
+ workspaceId: string,
|
|
|
+ listLoading: boolean,
|
|
|
+ list: any[],
|
|
|
+ selectedRowKeys: string[],
|
|
|
+ currentDevice: any,
|
|
|
+ editableData: {
|
|
|
+ [key: string]: string,
|
|
|
+ },
|
|
|
+ deviceLogDrawerVisible: boolean,
|
|
|
+ deviceHmsDrawerVisible: boolean,
|
|
|
+};
|
|
|
+
|
|
|
+const state: State = reactive({
|
|
|
+ workspaceId: localStorage.getItem('workspace_id') || '',
|
|
|
+ listLoading: false,
|
|
|
+ list: [],
|
|
|
+ selectedRowKeys: [],
|
|
|
+ currentDevice: {},
|
|
|
+ editableData: {},
|
|
|
+ deviceLogDrawerVisible: false,
|
|
|
+ deviceHmsDrawerVisible: false,
|
|
|
+})
|
|
|
+
|
|
|
+const paginationConfig = reactive({
|
|
|
+ pageSizeOptions: ['20', '50', '100'],
|
|
|
+ showQuickJumper: true,
|
|
|
+ showSizeChanger: true,
|
|
|
+ pageSize: 20,
|
|
|
+ current: 1,
|
|
|
+ total: 0
|
|
|
+})
|
|
|
+
|
|
|
+const fetchList = async () => {
|
|
|
+ state.listLoading = true;
|
|
|
+ try {
|
|
|
+ const res = await getBindingDevices(state.workspaceId, { page: paginationConfig.current, page_size: paginationConfig.pageSize });
|
|
|
+ state.list = res.data.list;
|
|
|
+ paginationConfig.total = res.data.pagination.total
|
|
|
+ paginationConfig.current = res.data.pagination.page
|
|
|
+ paginationConfig.pageSize = res.data.pagination.page_size
|
|
|
+ } catch (e) {
|
|
|
+ console.error(e);
|
|
|
+ } finally {
|
|
|
+ state.listLoading = false;
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
-const loading = ref(true)
|
|
|
+onMounted(async () => {
|
|
|
+ await fetchList();
|
|
|
+})
|
|
|
|
|
|
const columns = [
|
|
|
{
|
|
|
@@ -114,13 +144,14 @@ const columns = [
|
|
|
width: 150,
|
|
|
ellipsis: true,
|
|
|
sorter: (a: any, b: any) => a.device_name.localeCompare(b.device_name),
|
|
|
+ slots: { customRender: 'device_name' }
|
|
|
},
|
|
|
{
|
|
|
title: '设备SN',
|
|
|
dataIndex: 'device_sn',
|
|
|
- width: 200,
|
|
|
+ width: 250,
|
|
|
ellipsis: true,
|
|
|
- slots: { customRender: 'sn' }
|
|
|
+ slots: { customRender: 'device_sn' }
|
|
|
},
|
|
|
{
|
|
|
title: '设备名称',
|
|
|
@@ -156,12 +187,14 @@ const columns = [
|
|
|
dataIndex: 'bound_time',
|
|
|
width: 200,
|
|
|
sorter: (a: any, b: any) => a.bound_time.localeCompare(b.bound_time),
|
|
|
+ slots: { customRender: 'bound_time' },
|
|
|
},
|
|
|
{
|
|
|
title: '最后在线时间',
|
|
|
dataIndex: 'login_time',
|
|
|
width: 200,
|
|
|
sorter: (a: any, b: any) => a.login_time.localeCompare(b.login_time),
|
|
|
+ slots: { customRender: 'login_time' },
|
|
|
},
|
|
|
{
|
|
|
title: '操作',
|
|
|
@@ -172,16 +205,6 @@ const columns = [
|
|
|
},
|
|
|
]
|
|
|
|
|
|
-const expandIcon = (props: any) => {
|
|
|
- if (!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) {
|
|
|
@@ -190,172 +213,63 @@ const rowClassName = (record: any, index: number) => {
|
|
|
return className.toString().replaceAll(',', ' ')
|
|
|
}
|
|
|
|
|
|
-const expandRows = ref<string[]>([])
|
|
|
-
|
|
|
-const data: {
|
|
|
- device: Device[]
|
|
|
-} = reactive({
|
|
|
- device: []
|
|
|
-})
|
|
|
-
|
|
|
-const paginationProp = reactive({
|
|
|
- pageSizeOptions: ['20', '50', '100'],
|
|
|
- showQuickJumper: true,
|
|
|
- showSizeChanger: true,
|
|
|
- pageSize: 20,
|
|
|
- current: 1,
|
|
|
- total: 0
|
|
|
-})
|
|
|
-
|
|
|
-// 获取分页信息
|
|
|
-function getPaginationBody() {
|
|
|
- return {
|
|
|
- page: paginationProp.current,
|
|
|
- page_size: paginationProp.pageSize
|
|
|
- } as IPage
|
|
|
+const refreshData = async (page: any) => {
|
|
|
+ paginationConfig.current = page?.current!
|
|
|
+ paginationConfig.pageSize = page?.pageSize!
|
|
|
+ await fetchList();
|
|
|
}
|
|
|
|
|
|
const rowSelection = {
|
|
|
- onChange: (selectedRowKeys: (string | number)[], selectedRows: []) => {
|
|
|
- console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows)
|
|
|
- },
|
|
|
- onSelect: (record: any, selected: boolean, selectedRows: []) => {
|
|
|
- console.log(record, selected, selectedRows)
|
|
|
+ onChange: (selectedRowKeys: string[]) => {
|
|
|
+ state.selectedRowKeys = selectedRowKeys;
|
|
|
},
|
|
|
- onSelectAll: (selected: boolean, selectedRows: [], changeRows: []) => {
|
|
|
- console.log(selected, selectedRows, changeRows)
|
|
|
- },
|
|
|
- getCheckboxProps: (record: any) => ({
|
|
|
- disabled: record.domain !== EDeviceTypeName.Dock,
|
|
|
- }),
|
|
|
}
|
|
|
|
|
|
-const workspaceId: string = localStorage.getItem(ELocalStorageKey.WorkspaceId) || ''
|
|
|
-
|
|
|
-const editableData: UnwrapRef<Record<string, Device>> = reactive({})
|
|
|
-
|
|
|
-// 设备升级
|
|
|
-const {
|
|
|
- deviceFirmwareUpgradeModalVisible,
|
|
|
- selectedDevice,
|
|
|
- onDeviceUpgrade,
|
|
|
- onUpgradeDeviceOk
|
|
|
-} = useDeviceFirmwareUpgrade(workspaceId)
|
|
|
-
|
|
|
-function onDeviceUpgradeWs(payload: DeviceCmdExecuteInfo) {
|
|
|
- updateDevicesByWs(data.device, payload)
|
|
|
+// 点击批量删除
|
|
|
+const onClickBatchDelete = async () => {
|
|
|
+ console.log(state.selectedRowKeys, '点击批量删除');
|
|
|
}
|
|
|
|
|
|
-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, ext } = payload.output
|
|
|
- if (status === DeviceCmdExecuteStatus.Sent || status === DeviceCmdExecuteStatus.InProgress) { // 升级中
|
|
|
- const rate = ext?.rate ? (ext.rate / 1024).toFixed(2) + 'kb/s' : ''
|
|
|
- devices[i].firmware_status = DeviceFirmwareStatusEnum.DuringUpgrade
|
|
|
- devices[i].firmware_progress = (progress?.percent || 0) + '% ' + rate
|
|
|
- } else { // 终态:成功,失败,超时
|
|
|
- if (status === DeviceCmdExecuteStatus.Failed || status === DeviceCmdExecuteStatus.Timeout) {
|
|
|
- notification.error({
|
|
|
- message: `(${payload.sn}) Upgrade failed`,
|
|
|
- description: `Error Code: ${payload.result}`,
|
|
|
- duration: null
|
|
|
- })
|
|
|
- }
|
|
|
- // 拉取列表
|
|
|
- getDevices(true)
|
|
|
- }
|
|
|
- return
|
|
|
- }
|
|
|
- if (devices[i].children) {
|
|
|
- updateDevicesByWs(devices[i].children || [], payload)
|
|
|
- }
|
|
|
- }
|
|
|
+// 点击设备日志
|
|
|
+const onClickDeviceLog = (record: any) => {
|
|
|
+ state.deviceLogDrawerVisible = true;
|
|
|
+ state.currentDevice = record;
|
|
|
}
|
|
|
|
|
|
-useDeviceUpgradeEvent(onDeviceUpgradeWs)
|
|
|
-
|
|
|
-// 获取设备列表信息
|
|
|
-function getDevices(closeLoading?: boolean) {
|
|
|
- if (!closeLoading) {
|
|
|
- loading.value = true
|
|
|
- }
|
|
|
- getBindingDevices(workspaceId, getPaginationBody()).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]
|
|
|
- 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
|
|
|
- })
|
|
|
+// 点击告警信息
|
|
|
+const onClickDeviceHms = (record: any) => {
|
|
|
+ state.deviceHmsDrawerVisible = true;
|
|
|
+ state.currentDevice = record;
|
|
|
}
|
|
|
|
|
|
-function refreshData(page: any) {
|
|
|
- paginationProp.current = page?.current!
|
|
|
- paginationProp.pageSize = page?.pageSize!
|
|
|
- getDevices()
|
|
|
+// 点击编辑
|
|
|
+const onClickEdit = (record: any) => {
|
|
|
+ state.editableData[record.device_sn] = record;
|
|
|
}
|
|
|
|
|
|
-// 编辑
|
|
|
-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)
|
|
|
+// 点击保存
|
|
|
+const onClickSave = async (record: any) => {
|
|
|
+ delete state.editableData[record.device_sn];
|
|
|
+ await updateDevice({ nickname: record.nickname }, state.workspaceId, record.device_sn)
|
|
|
+ if (record.children) {
|
|
|
+ await updateDevice({ nickname: record.children.nickname }, state.workspaceId, record.children.device_sn)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// 点击删除
|
|
|
-const onClickDelete = (record: Device) => {
|
|
|
+const onClickDelete = (record: any) => {
|
|
|
Modal.confirm({
|
|
|
title: '提示',
|
|
|
content: `确定删除${record.device_name}吗?`,
|
|
|
onOk: async () => {
|
|
|
- unbindDevice(record.device_sn).then(res => {
|
|
|
- if (res.code !== 0) {
|
|
|
- return
|
|
|
- }
|
|
|
- getDevices()
|
|
|
- })
|
|
|
+ const res = await unbindDevice(record.device_sn);
|
|
|
+ if (res.code !== 0) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ await fetchList();
|
|
|
},
|
|
|
});
|
|
|
}
|
|
|
-
|
|
|
-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()
|
|
|
-})
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss">
|