31 changed files with 2362 additions and 249 deletions
@ -0,0 +1,34 @@ |
|||||||
|
import { eamServer } from '../../index' |
||||||
|
|
||||||
|
export function getFirmwareList(params: PageParam) { |
||||||
|
return eamServer({ |
||||||
|
url: '/firmware/page', |
||||||
|
method: 'get', |
||||||
|
params, |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
export function deleteFirmware(id: string) { |
||||||
|
return eamServer({ |
||||||
|
url: '/firmware/delete', |
||||||
|
method: 'post', |
||||||
|
data: { id }, |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
export function createFirmware(data: any) { |
||||||
|
return eamServer({ |
||||||
|
url: '/firmware/upload', |
||||||
|
method: 'post', |
||||||
|
data, |
||||||
|
headers: { 'Content-Type': 'multipart/form-data' }, |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
export function getSimpleFirmwareList(params: { type: number }) { |
||||||
|
return eamServer({ |
||||||
|
url: '/firmware/simple-list', |
||||||
|
method: 'get', |
||||||
|
params, |
||||||
|
}) |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
import { eamServer } from '../../index' |
||||||
|
|
||||||
|
interface IMaintenanceOV { |
||||||
|
id?: number |
||||||
|
content: string |
||||||
|
maintainer?: string |
||||||
|
maintenanceTime: string |
||||||
|
status: number |
||||||
|
} |
||||||
|
export const createdMaintenance = (data: IMaintenanceOV) => |
||||||
|
eamServer({ |
||||||
|
url: '/maintenance/create', |
||||||
|
method: 'post', |
||||||
|
data, |
||||||
|
}) |
||||||
|
|
||||||
|
export const updateMaintenance = (data: IMaintenanceOV) => |
||||||
|
eamServer({ |
||||||
|
url: '/maintenance/update', |
||||||
|
method: 'post', |
||||||
|
data, |
||||||
|
}) |
||||||
|
export const getMaintenanceList = (params: any) => |
||||||
|
eamServer({ |
||||||
|
url: '/maintenance/get-by-device-id', |
||||||
|
method: 'get', |
||||||
|
params, |
||||||
|
}) |
@ -0,0 +1,24 @@ |
|||||||
|
import { eamServer } from '../../index' |
||||||
|
|
||||||
|
export interface IStorageOutParam { |
||||||
|
id?: string |
||||||
|
customerId: string |
||||||
|
name: string |
||||||
|
description: string |
||||||
|
deviceId: number |
||||||
|
price: string |
||||||
|
} |
||||||
|
|
||||||
|
export const storageOutDevice = (params: IStorageOutParam) => |
||||||
|
eamServer({ |
||||||
|
url: `/device/out-storage`, |
||||||
|
method: 'post', |
||||||
|
data: params, |
||||||
|
}) |
||||||
|
|
||||||
|
export const getStorageOutByDeviceId = (deviceId: number) => |
||||||
|
eamServer({ |
||||||
|
url: `/out-storage/get-by-device-id`, |
||||||
|
method: 'get', |
||||||
|
params: { deviceId }, |
||||||
|
}) |
@ -0,0 +1,25 @@ |
|||||||
|
import { eamServer } from '../../index' |
||||||
|
|
||||||
|
export function getTaskList(params: PageParam) { |
||||||
|
return eamServer({ |
||||||
|
url: '/device/task/page', |
||||||
|
method: 'get', |
||||||
|
params, |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
export function getTaskDetails(id: string) { |
||||||
|
return eamServer({ |
||||||
|
url: '/device/task/details', |
||||||
|
method: 'get', |
||||||
|
params: { id }, |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
export function createTask(data: any) { |
||||||
|
return eamServer({ |
||||||
|
url: '/device/firmware/remote-update', |
||||||
|
method: 'post', |
||||||
|
data, |
||||||
|
}) |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
export enum DeviceStatus { |
||||||
|
已注册, |
||||||
|
测试中, |
||||||
|
已入库, |
||||||
|
已出库, |
||||||
|
} |
||||||
|
export const deviceTableCol = [ |
||||||
|
{ label: '设备编号', prop: 'serialNo', minWidth: '16%' }, |
||||||
|
{ label: '设备名称', prop: 'name', minWidth: '10%' }, |
||||||
|
{ label: '设备类型', prop: 'categoryId', minWidth: '10%' }, |
||||||
|
{ label: '设备状态', prop: 'status', minWidth: '10%' }, |
||||||
|
{ label: '设备sn', prop: 'sn', minWidth: '10%' }, |
||||||
|
{ label: '创建时间', prop: 'createTime', minWidth: '10%' }, |
||||||
|
] |
||||||
|
|
||||||
|
export type OptAction = 'create' | 'update' | 'view' |
||||||
|
export const collapses = [ |
||||||
|
{ title: '基础信息', name: 'info' }, |
||||||
|
{ title: '测试信息', name: 'test' }, |
||||||
|
{ title: '出库信息', name: 'delivery' }, |
||||||
|
{ title: '维修信息', name: 'maintain' }, |
||||||
|
] |
||||||
|
|
||||||
|
export const openNotMsg = (msg: string) => { |
||||||
|
ElNotification({ |
||||||
|
title: 'Warning', |
||||||
|
message: msg, |
||||||
|
type: 'warning', |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
export const maintainStatus = [ |
||||||
|
{ label: '待维修', value: 0 }, |
||||||
|
{ label: '维修中', value: 1 }, |
||||||
|
{ label: '已维修', value: 2 }, |
||||||
|
] |
||||||
|
export const testSheetStatus = [ |
||||||
|
{ label: '未测试', value: 0 }, |
||||||
|
{ label: '通过', value: 1 }, |
||||||
|
{ label: '未通过', value: 2 }, |
||||||
|
] |
||||||
|
|
||||||
|
export const testTableCol = [ |
||||||
|
{ label: '测试内容', prop: 'content', minWidth: '10%' }, |
||||||
|
{ label: '预计结果', prop: 'expectResult', minWidth: '10%' }, |
||||||
|
{ label: '测试结果', prop: 'actualResult', minWidth: '20%' }, |
||||||
|
{ label: '备注', prop: 'description', minWidth: '16%' }, |
||||||
|
] |
@ -0,0 +1,215 @@ |
|||||||
|
<template> |
||||||
|
<EdfsDialog |
||||||
|
@on-close="onClone" |
||||||
|
@on-save="onSave" |
||||||
|
title="新建固件" |
||||||
|
:is-show="isShow" |
||||||
|
width="44%" |
||||||
|
class="create-firmware-dlg" |
||||||
|
> |
||||||
|
<div class="dlg-body"> |
||||||
|
<el-row> |
||||||
|
<FormItemInput label="固件名称" v-model="formData.name" require /> |
||||||
|
</el-row> |
||||||
|
<el-row> |
||||||
|
<FormItemInput label="固件版本" v-model="formData.version" require /> |
||||||
|
</el-row> |
||||||
|
<el-row> |
||||||
|
<FormItemSelect |
||||||
|
label="固件类型" |
||||||
|
v-model="formData.type" |
||||||
|
require |
||||||
|
:options="getIntDictOptions('device_entity_type')" |
||||||
|
keyTag="value" |
||||||
|
valueTag="value" |
||||||
|
labelTag="label" |
||||||
|
/> |
||||||
|
</el-row> |
||||||
|
<!-- 固件文件 --> |
||||||
|
<el-row class="upload"> |
||||||
|
<span class="label" |
||||||
|
><span data-v-fbd7b238="" class="require">*</span>固件文件:</span |
||||||
|
> |
||||||
|
<el-upload |
||||||
|
v-model:fileList="fileList" |
||||||
|
drag |
||||||
|
action="" |
||||||
|
:limit="1" |
||||||
|
:on-exceed="handleExceed" |
||||||
|
:auto-upload="false" |
||||||
|
ref="upload" |
||||||
|
class="upload-container" |
||||||
|
> |
||||||
|
<el-icon class="upload-icon"> |
||||||
|
<IEpUploadFilled /> |
||||||
|
</el-icon> |
||||||
|
<div class="text">拖拽文件或者 <em>点击上传</em></div> |
||||||
|
<template #tip v-if="!fileList.length"> |
||||||
|
<div class="el-upload__tip">上传限制一个文件,新文件会覆盖旧文件</div> |
||||||
|
</template> |
||||||
|
</el-upload> |
||||||
|
</el-row> |
||||||
|
<el-row> |
||||||
|
<FormItemInput label="固件描述" type="textarea" v-model="formData.description" /> |
||||||
|
</el-row> |
||||||
|
</div> |
||||||
|
</EdfsDialog> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup lang="ts"> |
||||||
|
import { createFirmware } from '@/api/module/eam/device/firmware' |
||||||
|
import { getTenant } from '@/api/module/system/tenant' |
||||||
|
import EdfsDialog from '@/components/dashboard/Edfs-dialog.vue' |
||||||
|
import FormItemInput from '@/components/dashboard/FormItemInput.vue' |
||||||
|
import { isResError, useMessage } from '@/hooks/useMessage' |
||||||
|
import FormItemSelect from '@/components/dashboard/FormItemSelect.vue' |
||||||
|
import { getTenantId } from '@/utils/auth' |
||||||
|
import { getIntDictOptions } from '@/utils/dict' |
||||||
|
|
||||||
|
const message = useMessage() |
||||||
|
|
||||||
|
import type { |
||||||
|
UploadInstance, |
||||||
|
UploadProps, |
||||||
|
UploadRawFile, |
||||||
|
UploadUserFile, |
||||||
|
} from 'element-plus' |
||||||
|
import { cloneDeep } from 'lodash' |
||||||
|
|
||||||
|
interface Props { |
||||||
|
isShow: boolean |
||||||
|
// action: operantAction |
||||||
|
} |
||||||
|
// const title = computed(() => |
||||||
|
// props.action === 'create' ? '' : '' |
||||||
|
// ) |
||||||
|
const emits = defineEmits(['on-save', 'on-close']) |
||||||
|
const props = withDefaults(defineProps<Props>(), { |
||||||
|
isShow: false, |
||||||
|
}) |
||||||
|
|
||||||
|
const fileList = ref<UploadUserFile[]>([]) |
||||||
|
const data = { |
||||||
|
name: '', |
||||||
|
version: '', |
||||||
|
description: '', |
||||||
|
type: '', |
||||||
|
} |
||||||
|
const formData = ref(cloneDeep(data)) |
||||||
|
const upload = ref<UploadInstance>() |
||||||
|
const handleExceed: UploadProps['onExceed'] = files => { |
||||||
|
upload.value!.clearFiles() |
||||||
|
const file = files[0] as UploadRawFile |
||||||
|
|
||||||
|
upload.value!.handleStart(file) |
||||||
|
} |
||||||
|
|
||||||
|
function validate() { |
||||||
|
if (!formData.value.name) { |
||||||
|
message.error('请输入固件名称') |
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
if (!formData.value.version) { |
||||||
|
message.error('请输入固件版本') |
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
if (!formData.value.type) { |
||||||
|
message.error('请选择固件类型') |
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
if (!fileList.value.length) { |
||||||
|
message.error('请上传固件文件') |
||||||
|
return true |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
async function onSave() { |
||||||
|
if (validate()) return |
||||||
|
const data = new FormData() |
||||||
|
data.append('path', getTenantId()) |
||||||
|
data.append('file', fileList.value[0].raw as File) |
||||||
|
data.append('name', formData.value.name) |
||||||
|
data.append('version', formData.value.version) |
||||||
|
data.append('type', formData.value.type) |
||||||
|
data.append('description', formData.value.description) |
||||||
|
const res = await createFirmware(data) |
||||||
|
if (isResError(res)) return |
||||||
|
emits('on-save') |
||||||
|
clearData() |
||||||
|
} |
||||||
|
|
||||||
|
function clearData() { |
||||||
|
formData.value = cloneDeep(data) |
||||||
|
fileList.value = [] |
||||||
|
} |
||||||
|
function onClone() { |
||||||
|
clearData() |
||||||
|
emits('on-close') |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped lang="scss"> |
||||||
|
.create-firmware-dlg { |
||||||
|
.dlg-body { |
||||||
|
height: 470px; |
||||||
|
} |
||||||
|
.el-row { |
||||||
|
height: 32px; |
||||||
|
margin-bottom: 20px; |
||||||
|
:deep(.label) { |
||||||
|
margin-right: 10px; |
||||||
|
} |
||||||
|
span { |
||||||
|
width: 110px; |
||||||
|
height: 32px; |
||||||
|
line-height: 32px; |
||||||
|
text-align: right; |
||||||
|
} |
||||||
|
:deep(.el-radio-group) { |
||||||
|
height: 100%; |
||||||
|
} |
||||||
|
:deep(.el-radio) { |
||||||
|
height: 100%; |
||||||
|
} |
||||||
|
} |
||||||
|
.require { |
||||||
|
color: red; |
||||||
|
margin-right: 4px; |
||||||
|
} |
||||||
|
.upload { |
||||||
|
height: 156px; |
||||||
|
:deep(.upload-container) { |
||||||
|
width: calc(100% - 210px); |
||||||
|
height: calc(100% - 20px); |
||||||
|
} |
||||||
|
:deep(.el-upload) { |
||||||
|
height: 100%; |
||||||
|
width: 100%; |
||||||
|
} |
||||||
|
:deep(.el-upload-dragger) { |
||||||
|
height: 100%; |
||||||
|
width: 100%; |
||||||
|
padding-top: 30px; |
||||||
|
box-sizing: border-box; |
||||||
|
} |
||||||
|
|
||||||
|
.upload-icon { |
||||||
|
font-size: 54px; |
||||||
|
color: #c0c4cc; |
||||||
|
} |
||||||
|
.text { |
||||||
|
color: #6c6c6c; |
||||||
|
font-size: 14px; |
||||||
|
text-align: center; |
||||||
|
} |
||||||
|
em { |
||||||
|
color: #409eff; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,183 @@ |
|||||||
|
<template> |
||||||
|
<div class="firmware-management-wrap"> |
||||||
|
<EdfsWrap class="firmware-table" title="固件列表"> |
||||||
|
<template #title-right> |
||||||
|
<div class="firmware-table-title"> |
||||||
|
<EdfsButton |
||||||
|
type="success" |
||||||
|
inner-text="新增固件" |
||||||
|
:icon="createIcon" |
||||||
|
@click="addFirmware" |
||||||
|
/> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
<edfs-table |
||||||
|
:data="dataList" |
||||||
|
:highlight-current-row="true" |
||||||
|
ref="tableRef" |
||||||
|
row-class-name="row" |
||||||
|
:page-size="pageSize" |
||||||
|
:page-total="pageTotal" |
||||||
|
class="table" |
||||||
|
:current-page="pageIndex" |
||||||
|
@pageCurrentChange="handleJump" |
||||||
|
> |
||||||
|
<template v-for="(col, idx) in tableColumns" :key="idx"> |
||||||
|
<!-- <el-table-column |
||||||
|
v-if="col.prop == 'status'" |
||||||
|
:label="col.label" |
||||||
|
:prop="col.prop" |
||||||
|
:min-width="col.minWidth" |
||||||
|
> |
||||||
|
<template #default="scope"> |
||||||
|
<div style="display: flex; align-items: center; column-gap: 4px"> |
||||||
|
<img |
||||||
|
:src="deviceStatusImgMap[scope.row.status]" |
||||||
|
alt="" |
||||||
|
width="16" |
||||||
|
height="16" |
||||||
|
/> |
||||||
|
{{ getDictObj('device_entity_status', scope.row.status)?.label }} |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
</el-table-column> --> |
||||||
|
|
||||||
|
<el-table-column |
||||||
|
v-if="col.prop == 'createTime'" |
||||||
|
:label="col.label" |
||||||
|
:prop="col.prop" |
||||||
|
:min-width="col.width" |
||||||
|
> |
||||||
|
<template #default="scope"> |
||||||
|
{{ dayjs(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }} |
||||||
|
</template> |
||||||
|
</el-table-column> |
||||||
|
<el-table-column |
||||||
|
v-else |
||||||
|
:label="col.label" |
||||||
|
:prop="col.prop" |
||||||
|
:min-width="col.width" |
||||||
|
/> |
||||||
|
</template> |
||||||
|
|
||||||
|
<el-table-column label="操作" min-width="14%"> |
||||||
|
<template #default="scope"> |
||||||
|
<!-- <EdfsButton |
||||||
|
type="success" |
||||||
|
link |
||||||
|
inner-text="下载固件" |
||||||
|
@click="onDownload(scope.row)" |
||||||
|
/> --> |
||||||
|
<EdfsButton |
||||||
|
type="danger" |
||||||
|
link |
||||||
|
inner-text="删除" |
||||||
|
@click="onDelete(scope.row.id)" |
||||||
|
/> |
||||||
|
</template> |
||||||
|
</el-table-column> |
||||||
|
</edfs-table> |
||||||
|
</EdfsWrap> |
||||||
|
<createFirmwareDlg :is-show="isShowDlg" @on-close="onClose" @on-save="onSave" /> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup lang="ts"> |
||||||
|
import { tableColumns } from './utils' |
||||||
|
import EdfsButton from '@/components/dashboard/Edfs-button/index.vue' |
||||||
|
import EdfsTable from '@/components/dashboard/Edfs-table/index.vue' |
||||||
|
import EdfsWrap from '@/components/dashboard/Edfs-wrap.vue' |
||||||
|
import { useIcon } from '@/utils/useIcon' |
||||||
|
import createFirmwareDlg from './components/create-firmware-dlg.vue' |
||||||
|
import { deleteFirmware, getFirmwareList } from '@/api/module/eam/device/firmware' |
||||||
|
import { isResError, useMessage } from '@/hooks/useMessage.js' |
||||||
|
import dayjs from 'dayjs' |
||||||
|
import download from '@/utils/download' |
||||||
|
|
||||||
|
const message = useMessage() |
||||||
|
|
||||||
|
const createIcon = useIcon({ icon: 'gravity-ui:plus' }) |
||||||
|
const tableRef = ref<any>(null) |
||||||
|
const dataList = ref([]) |
||||||
|
const pageIndex = ref(1) |
||||||
|
const pageSize = ref() |
||||||
|
const pageTotal = ref(0) |
||||||
|
const pageJump = ref(1) |
||||||
|
|
||||||
|
async function loadData() { |
||||||
|
if (!pageSize.value) { |
||||||
|
pageSize.value = tableRef.value.getSize() |
||||||
|
} |
||||||
|
const res = await getFirmwareList({ |
||||||
|
pageNo: pageIndex.value, |
||||||
|
pageSize: pageSize.value, |
||||||
|
}) |
||||||
|
if (isResError(res)) return |
||||||
|
const { list, total } = res.data |
||||||
|
dataList.value = list |
||||||
|
pageTotal.value = total |
||||||
|
} |
||||||
|
watch(pageIndex, () => { |
||||||
|
pageJump.value = pageIndex.value |
||||||
|
loadData() |
||||||
|
}) |
||||||
|
|
||||||
|
function handleJump(page: number) { |
||||||
|
pageIndex.value = page |
||||||
|
} |
||||||
|
|
||||||
|
async function onDelete(id: string) { |
||||||
|
try { |
||||||
|
await message.delConfirm() |
||||||
|
const res = await deleteFirmware(id) |
||||||
|
if (isResError(res)) return |
||||||
|
message.success('删除成功') |
||||||
|
loadData() |
||||||
|
} catch (error) {} |
||||||
|
} |
||||||
|
|
||||||
|
function onDownload(row: any) { |
||||||
|
// download.word(row.downloadPath, row.name) |
||||||
|
// const downA = document.createElement('a') |
||||||
|
// downA.href = row.downloadPath |
||||||
|
// downA.download = row.name |
||||||
|
// downA.click() |
||||||
|
// downA.remove() |
||||||
|
} |
||||||
|
|
||||||
|
const isShowDlg = ref(false) |
||||||
|
function addFirmware() { |
||||||
|
isShowDlg.value = true |
||||||
|
} |
||||||
|
|
||||||
|
function onClose() { |
||||||
|
isShowDlg.value = false |
||||||
|
} |
||||||
|
|
||||||
|
function onSave() { |
||||||
|
isShowDlg.value = false |
||||||
|
loadData() |
||||||
|
} |
||||||
|
|
||||||
|
onMounted(() => { |
||||||
|
loadData() |
||||||
|
}) |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped lang="scss"> |
||||||
|
.firmware-management-wrap { |
||||||
|
width: 100%; |
||||||
|
height: 100%; |
||||||
|
.firmware-table { |
||||||
|
height: 100%; |
||||||
|
box-sizing: border-box; |
||||||
|
|
||||||
|
background: #fff; |
||||||
|
padding-bottom: 0; |
||||||
|
.table { |
||||||
|
height: 100%; |
||||||
|
width: 100%; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,32 @@ |
|||||||
|
export const tableColumns = [ |
||||||
|
{ |
||||||
|
label: '固件名称', |
||||||
|
prop: 'name', |
||||||
|
width: '10%', |
||||||
|
}, |
||||||
|
{ |
||||||
|
label: '固件版本', |
||||||
|
prop: 'version', |
||||||
|
width: '10%', |
||||||
|
}, |
||||||
|
{ |
||||||
|
label: '下载路径', |
||||||
|
prop: 'downloadPath', |
||||||
|
width: '30%', |
||||||
|
}, |
||||||
|
{ |
||||||
|
label: '创建时间', |
||||||
|
prop: 'createTime', |
||||||
|
width: '20%', |
||||||
|
}, |
||||||
|
{ |
||||||
|
label: '固件状态', |
||||||
|
prop: 'status', |
||||||
|
width: '10%', |
||||||
|
}, |
||||||
|
{ |
||||||
|
label: '描述', |
||||||
|
prop: 'description', |
||||||
|
width: '20%', |
||||||
|
}, |
||||||
|
] |
@ -0,0 +1,308 @@ |
|||||||
|
<template> |
||||||
|
<EdfsDialog |
||||||
|
@on-close="onClone" |
||||||
|
@on-save="onSave" |
||||||
|
title="新建升级任务" |
||||||
|
:is-show="isShow" |
||||||
|
width="44%" |
||||||
|
class="create-task-dlg" |
||||||
|
> |
||||||
|
<div class="dlg-body"> |
||||||
|
<el-row> |
||||||
|
<FormItemInput label="任务名称" v-model="formData.name" require /> |
||||||
|
</el-row> |
||||||
|
<el-row> |
||||||
|
<span class="label">固件选择:</span> |
||||||
|
<el-cascader |
||||||
|
:props="cascaderFirmware" |
||||||
|
v-model="firmwareSelect" |
||||||
|
ref="firmwareCascaderRef" |
||||||
|
:show-all-levels="false" |
||||||
|
placeholder="请选择" |
||||||
|
/> |
||||||
|
</el-row> |
||||||
|
<el-row v-if="isShowDeviceSelect"> |
||||||
|
<!-- <span class="label">设备选择</span> |
||||||
|
<el-cascader |
||||||
|
style="flex: 1" |
||||||
|
v-model="deviceSelect" |
||||||
|
:props="cascaderDevice" |
||||||
|
@change="devChange" |
||||||
|
ref="deviceCascaderRef" |
||||||
|
:show-all-levels="false" |
||||||
|
popper-class="task-device-select-cascader" |
||||||
|
placeholder="请选择" |
||||||
|
collapse-tags |
||||||
|
:max-collapse-tags="4" |
||||||
|
collapse-tags-tooltip |
||||||
|
@expand-change="changeSourceType" |
||||||
|
/> --> |
||||||
|
</el-row> |
||||||
|
<el-row> |
||||||
|
<span class="label">是否立即执行</span> |
||||||
|
<el-switch |
||||||
|
v-model="formData.executeNow" |
||||||
|
style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949" |
||||||
|
/> |
||||||
|
</el-row> |
||||||
|
<el-row v-if="!formData.executeNow"> |
||||||
|
<span class="label">任务执行时间</span> |
||||||
|
<el-date-picker |
||||||
|
v-model="formData.executeTime" |
||||||
|
type="datetime" |
||||||
|
value-format="YYYY-MM-DD HH:mm" |
||||||
|
format="YYYY-MM-DD HH:mm" |
||||||
|
placeholder="Pick a day" |
||||||
|
:disabled-date="disabledExecuteDate" |
||||||
|
/> |
||||||
|
</el-row> |
||||||
|
<el-row> |
||||||
|
<EdfsNumberInput labelName="监控超时" v-model="formData.monitorTimeout" require /> |
||||||
|
</el-row> |
||||||
|
<el-row> |
||||||
|
<EdfsNumberInput labelName="重试次数" v-model="formData.retryCount" require /> |
||||||
|
</el-row> |
||||||
|
<el-row> |
||||||
|
<EdfsNumberInput labelName="重试间隔" v-model="formData.retryInterval" require /> |
||||||
|
</el-row> |
||||||
|
</div> |
||||||
|
</EdfsDialog> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup lang="ts"> |
||||||
|
import EdfsDialog from '@/components/dashboard/Edfs-dialog.vue' |
||||||
|
import FormItemInput from '@/components/dashboard/FormItemInput.vue' |
||||||
|
import { isResError, useMessage } from '@/hooks/useMessage' |
||||||
|
import EdfsNumberInput from '@/components/dashboard/Edfs-number-item-input.vue' |
||||||
|
import FormItemSelect from '@/components/dashboard/FormItemSelect.vue' |
||||||
|
import type { CascaderProps, CascaderValue, ElCascader } from 'element-plus' |
||||||
|
// import { getSimpleStationList } from '@/api/module/app/station' |
||||||
|
// import { getSimpleDeviceList } from '@/api/module/app/device' |
||||||
|
import { getIntDictOptions } from '@/utils/dict' |
||||||
|
import { getSimpleFirmwareList } from '@/api/module/eam/device/firmware' |
||||||
|
import { isEmpty } from '@/utils/is' |
||||||
|
import { clone, cloneDeep } from 'lodash' |
||||||
|
import { createTask } from '@/api/module/eam/device/task' |
||||||
|
|
||||||
|
const message = useMessage() |
||||||
|
interface Props { |
||||||
|
isShow: boolean |
||||||
|
} |
||||||
|
|
||||||
|
const emits = defineEmits(['on-save', 'on-close']) |
||||||
|
const props = withDefaults(defineProps<Props>(), { |
||||||
|
isShow: false, |
||||||
|
}) |
||||||
|
|
||||||
|
const firmwareSelect = ref<CascaderValue>([]) |
||||||
|
const firmwareCascaderRef = ref<InstanceType<typeof ElCascader>>() |
||||||
|
const cascaderFirmware: CascaderProps = { |
||||||
|
lazy: true, |
||||||
|
async lazyLoad(node, resolve) { |
||||||
|
const { level } = node |
||||||
|
|
||||||
|
if (level === 0) { |
||||||
|
const deviceTypes = getIntDictOptions('device_entity_type').map(item => ({ |
||||||
|
value: item.value, |
||||||
|
label: item.label, |
||||||
|
isLeaf: false, |
||||||
|
})) |
||||||
|
resolve(deviceTypes) |
||||||
|
} |
||||||
|
if (level === 1) { |
||||||
|
const res = await getSimpleFirmwareList({ |
||||||
|
type: node.data?.value as number, |
||||||
|
}) |
||||||
|
const data = Array.isArray(res?.data) ? res.data : [] |
||||||
|
const firmware = data.map((item: any) => ({ |
||||||
|
value: item.id, |
||||||
|
label: item.name, |
||||||
|
isLeaf: true, |
||||||
|
leaf: true, |
||||||
|
})) |
||||||
|
resolve(firmware) |
||||||
|
} |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
const selectFirmwareType = ref() |
||||||
|
watch(firmwareSelect, (value: any) => { |
||||||
|
selectFirmwareType.value = value[0] ?? '' |
||||||
|
formData.value.firmwareId = String(value[1]) ?? '' |
||||||
|
}) |
||||||
|
|
||||||
|
const isShowDeviceSelect = ref(false) |
||||||
|
watch( |
||||||
|
() => selectFirmwareType.value, |
||||||
|
() => { |
||||||
|
isShowDeviceSelect.value = false |
||||||
|
nextTick(() => { |
||||||
|
isShowDeviceSelect.value = |
||||||
|
!isEmpty(selectFirmwareType.value) && |
||||||
|
typeof selectFirmwareType.value !== 'undefined' |
||||||
|
}) |
||||||
|
} |
||||||
|
) |
||||||
|
|
||||||
|
// const deviceCascaderRef = ref<InstanceType<typeof ElCascader>>() |
||||||
|
// const cascaderDevice: CascaderProps = { |
||||||
|
// lazy: true, |
||||||
|
// multiple: true, |
||||||
|
// async lazyLoad(node, resolve) { |
||||||
|
// const { level } = node |
||||||
|
// if (level === 0) { |
||||||
|
// const res = await getSimpleStationList() |
||||||
|
// if (isResError(res)) return |
||||||
|
// const data = Array.isArray(res?.data) ? res.data : [] |
||||||
|
// const nodes = data.map((item: any) => ({ |
||||||
|
// value: item.id, |
||||||
|
// label: item.name, |
||||||
|
// isLeaf: false, |
||||||
|
// })) |
||||||
|
// resolve(nodes) |
||||||
|
// } |
||||||
|
// if (level === 1) { |
||||||
|
// const res = await getSimpleDeviceList({ |
||||||
|
// siteId: node.data?.value as number, |
||||||
|
// type: selectFirmwareType.value, |
||||||
|
// }) |
||||||
|
|
||||||
|
// const data = Array.isArray(res?.data) ? res.data : [] |
||||||
|
// const devices = data.map((item: any) => ({ |
||||||
|
// value: item.sn, |
||||||
|
// label: item.name, |
||||||
|
// isLeaf: true, |
||||||
|
// leaf: true, |
||||||
|
// })) |
||||||
|
// resolve(devices) |
||||||
|
// } |
||||||
|
// }, |
||||||
|
// } |
||||||
|
|
||||||
|
const deviceSelect = ref<CascaderValue>([]) |
||||||
|
watch(deviceSelect, (value: any) => { |
||||||
|
formData.value.deviceSns = value.map((item: any) => item[1]) |
||||||
|
}) |
||||||
|
|
||||||
|
function devChange() {} |
||||||
|
|
||||||
|
const cascaderTag = ref<any[]>([]) |
||||||
|
function changeSourceType(selectItem: any) { |
||||||
|
if (!cascaderTag.value.includes(selectItem[0])) { |
||||||
|
deviceSelect.value = [] |
||||||
|
} |
||||||
|
cascaderTag.value = selectItem |
||||||
|
} |
||||||
|
|
||||||
|
const form = { |
||||||
|
name: '', |
||||||
|
executeNow: false, // 立即执行 |
||||||
|
executeTime: '', // 执行时间 |
||||||
|
retryCount: 3, // 重试次数 |
||||||
|
retryInterval: 1000, // 重试间隔 |
||||||
|
monitorTimeout: 1000, // 监控超时 |
||||||
|
firmwareId: '', // 固件ID |
||||||
|
deviceSns: [], // 设备SN |
||||||
|
} |
||||||
|
const formData = ref(cloneDeep(form)) |
||||||
|
|
||||||
|
function validate() { |
||||||
|
if (!formData.value.name) { |
||||||
|
message.error('请输入任务名称') |
||||||
|
return true |
||||||
|
} |
||||||
|
if (!formData.value.firmwareId) { |
||||||
|
message.error('请选择固件') |
||||||
|
return true |
||||||
|
} |
||||||
|
if (!formData.value.deviceSns.length) { |
||||||
|
message.error('请选择设备') |
||||||
|
return true |
||||||
|
} |
||||||
|
if (!formData.value.executeNow && !formData.value.executeTime) { |
||||||
|
message.error('请选择执行时间') |
||||||
|
return true |
||||||
|
} |
||||||
|
if (!formData.value.monitorTimeout) { |
||||||
|
message.error('请输入监控超时') |
||||||
|
return true |
||||||
|
} |
||||||
|
if (!formData.value.retryCount) { |
||||||
|
message.error('请输入重试次数') |
||||||
|
return true |
||||||
|
} |
||||||
|
if (!formData.value.retryInterval) { |
||||||
|
message.error('请输入重试间隔') |
||||||
|
return true |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
function disabledExecuteDate(time: Date) { |
||||||
|
return time.getTime() < Date.now() - 8.64e7 |
||||||
|
} |
||||||
|
async function onSave() { |
||||||
|
if (validate()) return |
||||||
|
const res = await createTask(formData.value) |
||||||
|
if (isResError(res)) return |
||||||
|
emits('on-save') |
||||||
|
clearData() |
||||||
|
} |
||||||
|
function onClone() { |
||||||
|
clearData() |
||||||
|
emits('on-close') |
||||||
|
} |
||||||
|
|
||||||
|
function clearData() { |
||||||
|
formData.value = cloneDeep(form) |
||||||
|
firmwareSelect.value = [] |
||||||
|
cascaderTag.value = [] |
||||||
|
deviceSelect.value = [] |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style lang="scss"> |
||||||
|
.task-device-select-cascader { |
||||||
|
.el-cascader-panel { |
||||||
|
.el-cascader-menu:first-child { |
||||||
|
.el-cascader-node { |
||||||
|
.el-checkbox { |
||||||
|
display: none !important; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
||||||
|
|
||||||
|
<style scoped lang="scss"> |
||||||
|
.create-task-dlg { |
||||||
|
.dlg-body { |
||||||
|
max-height: 400px; |
||||||
|
} |
||||||
|
.el-row { |
||||||
|
height: 32px; |
||||||
|
width: 74%; |
||||||
|
margin-bottom: 20px; |
||||||
|
:deep(.label) { |
||||||
|
margin-right: 10px; |
||||||
|
} |
||||||
|
span { |
||||||
|
width: 110px; |
||||||
|
height: 32px; |
||||||
|
line-height: 32px; |
||||||
|
text-align: right; |
||||||
|
} |
||||||
|
:deep(.el-radio-group) { |
||||||
|
height: 100%; |
||||||
|
} |
||||||
|
:deep(.el-radio) { |
||||||
|
height: 100%; |
||||||
|
} |
||||||
|
} |
||||||
|
.require { |
||||||
|
color: red; |
||||||
|
margin-right: 4px; |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,244 @@ |
|||||||
|
<template> |
||||||
|
<div class="details-drawer"> |
||||||
|
<el-drawer |
||||||
|
v-model="isShow" |
||||||
|
:title="`${props.rowData.taskName}任务详情`" |
||||||
|
direction="rtl" |
||||||
|
size="64%" |
||||||
|
> |
||||||
|
<main class="drawer-box"> |
||||||
|
<el-scrollbar> |
||||||
|
<div class="device-status"> |
||||||
|
<template v-for="(item, idx) in deviceDetails" :key="idx"> |
||||||
|
<div class="device-info"> |
||||||
|
<div class="title">站点名称:{{ item.siteName }}</div> |
||||||
|
<div class="body"> |
||||||
|
<div class="info"> |
||||||
|
<div class="item"> |
||||||
|
<span class="label">设备:</span><span>{{ item.deviceName }}</span> |
||||||
|
</div> |
||||||
|
<div class="item"> |
||||||
|
<span class="label">状态:</span |
||||||
|
><span>{{ TaskStatus[item.node] ?? '未开始' }}</span> |
||||||
|
</div> |
||||||
|
<div class="item" v-if="item.node === 1"> |
||||||
|
<span class="label">原因:</span><span>{{ item.reason }}</span> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div class="progress" v-if="!isEmpty(item.node)"> |
||||||
|
<el-progress |
||||||
|
type="circle" |
||||||
|
v-if="item.node === 0" |
||||||
|
:percentage="100" |
||||||
|
:stroke-width="10" |
||||||
|
color="#16a34a" |
||||||
|
> |
||||||
|
<div class="progress-content" style="color: #16a34a"> |
||||||
|
<div> |
||||||
|
<Icon icon="mingcute:check-fill" :size="20" /> |
||||||
|
</div> |
||||||
|
<span class="percentage-label">升级成功</span> |
||||||
|
</div> |
||||||
|
</el-progress> |
||||||
|
<el-progress |
||||||
|
type="circle" |
||||||
|
v-if="item.node === 1" |
||||||
|
:percentage="100" |
||||||
|
:stroke-width="10" |
||||||
|
color="#ef4444" |
||||||
|
> |
||||||
|
<div class="progress-content" style="color: #ef4444"> |
||||||
|
<div> |
||||||
|
<Icon icon="mingcute:close-fill" :size="20" /> |
||||||
|
</div> |
||||||
|
<span class="percentage-label">升级失败</span> |
||||||
|
</div> |
||||||
|
</el-progress> |
||||||
|
<el-progress |
||||||
|
type="circle" |
||||||
|
v-if="item.node === 2" |
||||||
|
:percentage="item.subNode" |
||||||
|
:stroke-width="10" |
||||||
|
color="#3b82f6" |
||||||
|
/> |
||||||
|
<el-progress |
||||||
|
type="circle" |
||||||
|
v-if="item.node === 3" |
||||||
|
:percentage="item.subNode" |
||||||
|
:stroke-width="10" |
||||||
|
color="#f97316" |
||||||
|
:indeterminate="true" |
||||||
|
/> |
||||||
|
<el-progress |
||||||
|
type="circle" |
||||||
|
v-if="item.node === 4" |
||||||
|
:percentage="item.subNode" |
||||||
|
:stroke-width="10" |
||||||
|
color="#10b981" |
||||||
|
:duration="6" |
||||||
|
/> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
</div> |
||||||
|
</el-scrollbar> |
||||||
|
</main> |
||||||
|
</el-drawer> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup lang="ts"> |
||||||
|
import { isResError } from '@/hooks/useMessage' |
||||||
|
import { getTaskDetails } from '@/api/module/eam/device/task' |
||||||
|
import { isEmpty } from '@/utils/is' |
||||||
|
import Icon from '@/components/dashboard/Icon/src/Icon.vue' |
||||||
|
enum TaskStatus { |
||||||
|
'升级成功', |
||||||
|
'升级失败', |
||||||
|
'接收中...', |
||||||
|
'校验中...', |
||||||
|
'升级中...', |
||||||
|
} |
||||||
|
interface Props { |
||||||
|
rowData: any |
||||||
|
taskMap: any |
||||||
|
} |
||||||
|
const props = withDefaults(defineProps<Props>(), { |
||||||
|
taskMap: new Map(), |
||||||
|
}) |
||||||
|
const isShow = defineModel<boolean>() |
||||||
|
|
||||||
|
const deviceDetails = ref<any>([]) |
||||||
|
async function loadData() { |
||||||
|
const res = await getTaskDetails(props.rowData.id) |
||||||
|
if (!isResError(res)) { |
||||||
|
deviceDetails.value = Array.isArray(res.data) ? res.data : [] |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function updateData(data: any[]) { |
||||||
|
console.log('object', data) |
||||||
|
for (const d of data) { |
||||||
|
const find = deviceDetails.value.find((item: any) => item.deviceSn == d.deviceSn) |
||||||
|
if (find) { |
||||||
|
find.node = d.node |
||||||
|
find.subNode = d.subNode |
||||||
|
find.reason = d.reason |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
onMounted(async () => { |
||||||
|
await loadData() |
||||||
|
const taskId = props.rowData.id |
||||||
|
let data = props.taskMap.get(taskId) |
||||||
|
data = data ? Array.from(data.values()) : [] |
||||||
|
updateData(data) |
||||||
|
}) |
||||||
|
|
||||||
|
defineExpose({ |
||||||
|
updateData, |
||||||
|
}) |
||||||
|
</script> |
||||||
|
|
||||||
|
<style lang="scss" scoped> |
||||||
|
.details-drawer { |
||||||
|
font-size: 16px; |
||||||
|
:deep(.el-overlay) { |
||||||
|
// background-color: transparent !important; |
||||||
|
} |
||||||
|
// :deep(.el-drawer) { |
||||||
|
// width: calc(100% - 280px) !important; |
||||||
|
// width: 70%; |
||||||
|
// } |
||||||
|
:deep(.el-drawer__header) { |
||||||
|
margin-bottom: 10px; |
||||||
|
padding: 10px; |
||||||
|
border-bottom: 1px solid #ccc; |
||||||
|
} |
||||||
|
:deep(.el-drawer__body) { |
||||||
|
padding: 20px; |
||||||
|
padding-top: 0; |
||||||
|
} |
||||||
|
:deep(.el-drawer__title) { |
||||||
|
font-family: Alibaba-PuHuiTi-M; |
||||||
|
font-size: 16px; |
||||||
|
color: #4d4d4d; |
||||||
|
line-height: 24px; |
||||||
|
font-weight: 500; |
||||||
|
} |
||||||
|
.drawer-box { |
||||||
|
width: 100%; |
||||||
|
height: 100%; |
||||||
|
.device-status { |
||||||
|
width: 100%; |
||||||
|
height: 100%; |
||||||
|
display: flex; |
||||||
|
flex-wrap: wrap; |
||||||
|
column-gap: 20px; |
||||||
|
row-gap: 24px; |
||||||
|
.device-info { |
||||||
|
width: 280px; |
||||||
|
height: 192px; |
||||||
|
background: #ffffff; |
||||||
|
border: 1px solid #e9e9e9; |
||||||
|
box-shadow: 0px 2px 8px 0px rgba(0, 0, 0, 0.09); |
||||||
|
border-radius: 4px; |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
box-sizing: border-box; |
||||||
|
.title { |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
height: 48px; |
||||||
|
width: 100%; |
||||||
|
background-color: #f1f1f1; |
||||||
|
box-sizing: border-box; |
||||||
|
font-family: Alibaba-PuHuiTi-M; |
||||||
|
font-size: 16px; |
||||||
|
color: #030303; |
||||||
|
font-weight: 500; |
||||||
|
justify-content: space-between; |
||||||
|
padding: 0 12px; |
||||||
|
} |
||||||
|
.body { |
||||||
|
padding: 10px; |
||||||
|
display: flex; |
||||||
|
column-gap: 14px; |
||||||
|
flex: 1; |
||||||
|
.info { |
||||||
|
width: 130px; |
||||||
|
} |
||||||
|
.progress { |
||||||
|
flex: 1; |
||||||
|
.progress-content { |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
align-items: center; |
||||||
|
row-gap: 8px; |
||||||
|
} |
||||||
|
} |
||||||
|
.item { |
||||||
|
display: flex; |
||||||
|
min-height: 32px; |
||||||
|
.label { |
||||||
|
color: #6a6a6a; |
||||||
|
font-size: 14px; |
||||||
|
flex: 0.5; |
||||||
|
} |
||||||
|
span { |
||||||
|
word-wrap: break-word; |
||||||
|
word-break: break-all; |
||||||
|
white-space: normal; |
||||||
|
font-size: 14px; |
||||||
|
flex: 1; |
||||||
|
color: #3c3c3c; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,372 @@ |
|||||||
|
<template> |
||||||
|
<div class="task-management-wrap"> |
||||||
|
<EdfsWrap class="task-table" title="任务列表"> |
||||||
|
<template #title-right> |
||||||
|
<div class="task-table-title"> |
||||||
|
<EdfsButton |
||||||
|
type="success" |
||||||
|
inner-text="创建升级任务" |
||||||
|
:icon="createIcon" |
||||||
|
@click="addTask" |
||||||
|
/> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
<edfs-table |
||||||
|
:data="dataList" |
||||||
|
:highlight-current-row="true" |
||||||
|
ref="tableRef" |
||||||
|
row-class-name="row" |
||||||
|
:page-size="pageSize" |
||||||
|
:page-total="pageTotal" |
||||||
|
class="table" |
||||||
|
:current-page="pageIndex" |
||||||
|
@pageCurrentChange="handleJump" |
||||||
|
> |
||||||
|
<template v-for="(col, idx) in tableColumns" :key="idx"> |
||||||
|
<el-table-column |
||||||
|
v-if="col.prop === 'createTime'" |
||||||
|
:label="col.label" |
||||||
|
:prop="col.prop" |
||||||
|
:min-width="col.width" |
||||||
|
> |
||||||
|
<template #default="scope"> |
||||||
|
{{ dayjs(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }} |
||||||
|
</template> |
||||||
|
</el-table-column> |
||||||
|
<el-table-column |
||||||
|
v-else-if="col.prop === 'status'" |
||||||
|
:label="col.label" |
||||||
|
:prop="col.prop" |
||||||
|
:min-width="col.width" |
||||||
|
> |
||||||
|
<template #default="scope"> |
||||||
|
{{ taskStatusMap[scope.row.status] }} |
||||||
|
</template> |
||||||
|
</el-table-column> |
||||||
|
<el-table-column |
||||||
|
v-else-if="col.prop === 'executeTime'" |
||||||
|
:label="col.label" |
||||||
|
:prop="col.prop" |
||||||
|
:min-width="col.width" |
||||||
|
> |
||||||
|
<template #default="scope"> |
||||||
|
{{ dayjs(scope.row.executeTime).format('YYYY-MM-DD HH:mm:ss') }} |
||||||
|
</template> |
||||||
|
</el-table-column> |
||||||
|
<el-table-column |
||||||
|
v-else-if="col.prop === 'deviceUpgradeDetail'" |
||||||
|
:label="col.label" |
||||||
|
:prop="col.prop" |
||||||
|
:min-width="col.width" |
||||||
|
> |
||||||
|
<template #default="scope"> |
||||||
|
<div> |
||||||
|
<div>升级中:{{ scope.row.processingCount }}</div> |
||||||
|
<div>失败数量: {{ scope.row.failCount }}</div> |
||||||
|
<div>升级完成: {{ scope.row.successCount }}</div> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
</el-table-column> |
||||||
|
<el-table-column |
||||||
|
v-else |
||||||
|
:label="col.label" |
||||||
|
:prop="col.prop" |
||||||
|
:min-width="col.width" |
||||||
|
/> |
||||||
|
</template> |
||||||
|
|
||||||
|
<el-table-column label="操作" min-width="8%"> |
||||||
|
<template #default="scope"> |
||||||
|
<EdfsButton |
||||||
|
type="success" |
||||||
|
link |
||||||
|
v-if="scope.row.status === 1 || scope.row.status === 2" |
||||||
|
inner-text="详情" |
||||||
|
@click="onDetails(scope.row)" |
||||||
|
/> |
||||||
|
</template> |
||||||
|
</el-table-column> |
||||||
|
</edfs-table> |
||||||
|
</EdfsWrap> |
||||||
|
<createTaskDlg :isShow="isShowDlg" @on-close="onClose" @on-save="onSave" /> |
||||||
|
<detailsDrawer |
||||||
|
ref="detailsRef" |
||||||
|
:rowData="rowData" |
||||||
|
:taskMap="taskMap" |
||||||
|
v-model="isShowDetails" |
||||||
|
v-if="isShowDetails" |
||||||
|
/> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
<script setup lang="ts"> |
||||||
|
import { |
||||||
|
addListener, |
||||||
|
removeListener, |
||||||
|
type SocketMsgListener, |
||||||
|
} from '@/tools/event/socketEvent' |
||||||
|
import { tableColumns, taskStatusMap } from './utils' |
||||||
|
import EdfsButton from '@/components/dashboard/Edfs-button/index.vue' |
||||||
|
import EdfsTable from '@/components/dashboard/Edfs-table/index.vue' |
||||||
|
import EdfsWrap from '@/components/dashboard/Edfs-wrap.vue' |
||||||
|
import { useIcon } from '@/utils/useIcon' |
||||||
|
import createTaskDlg from './components/create-task-dlg.vue' |
||||||
|
import detailsDrawer from './components/details-drawer.vue' |
||||||
|
import { isResError, useMessage } from '@/hooks/useMessage.js' |
||||||
|
import dayjs from 'dayjs' |
||||||
|
import { getTaskList } from '@/api/module/eam/device/task' |
||||||
|
|
||||||
|
const message = useMessage() |
||||||
|
|
||||||
|
const createIcon = useIcon({ icon: 'gravity-ui:plus' }) |
||||||
|
const tableRef = ref<any>(null) |
||||||
|
const dataList = ref<any>([]) |
||||||
|
const pageIndex = ref(1) |
||||||
|
const pageSize = ref() |
||||||
|
const pageTotal = ref(0) |
||||||
|
const pageJump = ref(1) |
||||||
|
|
||||||
|
type DeviceMap = Map<number, any> |
||||||
|
type TaskMap = Map<number, DeviceMap> |
||||||
|
|
||||||
|
const taskMap: TaskMap = new Map<number, DeviceMap>() |
||||||
|
async function loadData() { |
||||||
|
if (!pageSize.value) { |
||||||
|
pageSize.value = tableRef.value.getSize() |
||||||
|
} |
||||||
|
const res = await getTaskList({ |
||||||
|
pageNo: pageIndex.value, |
||||||
|
pageSize: pageSize.value, |
||||||
|
}) |
||||||
|
if (isResError(res)) return |
||||||
|
const { list, total } = res.data |
||||||
|
dataList.value = list |
||||||
|
pageTotal.value = total |
||||||
|
|
||||||
|
updateTaskData() |
||||||
|
} |
||||||
|
|
||||||
|
watch(pageIndex, () => { |
||||||
|
pageJump.value = pageIndex.value |
||||||
|
loadData() |
||||||
|
}) |
||||||
|
|
||||||
|
function handleJump(page: number) { |
||||||
|
pageIndex.value = page |
||||||
|
} |
||||||
|
|
||||||
|
const isShowDlg = ref(false) |
||||||
|
function addTask() { |
||||||
|
isShowDlg.value = true |
||||||
|
} |
||||||
|
|
||||||
|
function onClose() { |
||||||
|
isShowDlg.value = false |
||||||
|
} |
||||||
|
|
||||||
|
function onSave() { |
||||||
|
isShowDlg.value = false |
||||||
|
loadData() |
||||||
|
} |
||||||
|
|
||||||
|
const rowData = ref<any>({}) |
||||||
|
const isShowDetails = ref(false) |
||||||
|
async function onDetails(row: any) { |
||||||
|
rowData.value = row |
||||||
|
isShowDetails.value = true |
||||||
|
} |
||||||
|
|
||||||
|
onMounted(async () => { |
||||||
|
await loadData() |
||||||
|
addListener(socketMsgListener) |
||||||
|
}) |
||||||
|
|
||||||
|
onBeforeUnmount(() => { |
||||||
|
removeListener(socketMsgListener) |
||||||
|
}) |
||||||
|
const detailsRef = ref<any>(null) |
||||||
|
|
||||||
|
const tasksStatusMap = new Map() |
||||||
|
const socketMsgListener: SocketMsgListener = { |
||||||
|
onCmdPost: function (type: string, stationId: number, data?: any, time?: string) { |
||||||
|
if (type === 'upgrade') { |
||||||
|
const { deviceSn, taskId } = data |
||||||
|
if (!taskId || !deviceSn) return |
||||||
|
let deviceMap = taskMap.get(taskId) |
||||||
|
if (!deviceMap) { |
||||||
|
deviceMap = new Map<number, any>() |
||||||
|
taskMap.set(taskId, deviceMap) |
||||||
|
} |
||||||
|
deviceMap.set(deviceSn, data) |
||||||
|
|
||||||
|
if (detailsRef.value && rowData.value?.id) { |
||||||
|
const taskId = rowData.value?.id |
||||||
|
let taskData = taskMap.get(taskId) |
||||||
|
const dataArray = taskData ? Array.from(taskData.values()) : [] |
||||||
|
detailsRef.value.updateData(dataArray) |
||||||
|
} |
||||||
|
} |
||||||
|
if (type === 'upgrade-task' && data.id) { |
||||||
|
tasksStatusMap.set(data.id, data) |
||||||
|
updateTaskData() |
||||||
|
} |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
function updateTaskData() { |
||||||
|
for (const data of dataList.value) { |
||||||
|
const taskData = tasksStatusMap.get(data.id) |
||||||
|
if (!taskData) continue |
||||||
|
data.status = taskData.status |
||||||
|
data.processingCount = taskData.processingCount |
||||||
|
data.failCount = taskData.failCount |
||||||
|
data.successCount = taskData.successCount |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// let a = [ |
||||||
|
// { |
||||||
|
// type: 'upgrade', |
||||||
|
// stationId: 1, |
||||||
|
// time: 1726129158000, |
||||||
|
// data: { |
||||||
|
// id: 1, |
||||||
|
// taskId: 32, |
||||||
|
// deviceId: 1, |
||||||
|
// node: 0, |
||||||
|
// subNode: 10, |
||||||
|
// reason: null, |
||||||
|
// deviceSn: '4', |
||||||
|
// tenantId: 162, |
||||||
|
// createTime: 1725533993000, |
||||||
|
// updateTime: 1725533993000, |
||||||
|
// creator: '145', |
||||||
|
// updater: '145', |
||||||
|
// deleted: false, |
||||||
|
// }, |
||||||
|
// }, |
||||||
|
// { |
||||||
|
// type: 'upgrade', |
||||||
|
// stationId: 1, |
||||||
|
// time: 1726129158000, |
||||||
|
// data: { |
||||||
|
// id: 1, |
||||||
|
// taskId: 32, |
||||||
|
// deviceId: 1, |
||||||
|
// node: 1, |
||||||
|
// subNode: 10, |
||||||
|
// reason: 'null', |
||||||
|
// deviceSn: '5', |
||||||
|
// tenantId: 162, |
||||||
|
// createTime: 1725533993000, |
||||||
|
// updateTime: 1725533993000, |
||||||
|
// creator: '145', |
||||||
|
// updater: '145', |
||||||
|
// deleted: false, |
||||||
|
// }, |
||||||
|
// }, |
||||||
|
// { |
||||||
|
// type: 'upgrade', |
||||||
|
// stationId: 1, |
||||||
|
// time: 1726129158000, |
||||||
|
// data: { |
||||||
|
// id: 1, |
||||||
|
// taskId: 32, |
||||||
|
// deviceId: 1, |
||||||
|
// node: 2, |
||||||
|
// subNode: 10, |
||||||
|
// reason: 'null', |
||||||
|
// deviceSn: '6', |
||||||
|
// tenantId: 162, |
||||||
|
// createTime: 1725533993000, |
||||||
|
// updateTime: 1725533993000, |
||||||
|
// creator: '145', |
||||||
|
// updater: '145', |
||||||
|
// deleted: false, |
||||||
|
// }, |
||||||
|
// }, |
||||||
|
// { |
||||||
|
// type: 'upgrade', |
||||||
|
// stationId: 1, |
||||||
|
// time: 1726129158000, |
||||||
|
// data: { |
||||||
|
// id: 1, |
||||||
|
// taskId: 1, |
||||||
|
// deviceId: 1, |
||||||
|
// node: 3, |
||||||
|
// subNode: 10, |
||||||
|
// reason: null, |
||||||
|
// deviceSn: '6', |
||||||
|
// tenantId: 162, |
||||||
|
// createTime: 1725533993000, |
||||||
|
// updateTime: 1725533993000, |
||||||
|
// creator: '145', |
||||||
|
// updater: '145', |
||||||
|
// deleted: false, |
||||||
|
// }, |
||||||
|
// }, |
||||||
|
// { |
||||||
|
// type: 'upgrade', |
||||||
|
// stationId: 1, |
||||||
|
// time: 1726129158000, |
||||||
|
// data: { |
||||||
|
// id: 1, |
||||||
|
// taskId: 1, |
||||||
|
// deviceId: 1, |
||||||
|
// node: 1, |
||||||
|
// subNode: 10, |
||||||
|
// reason: null, |
||||||
|
// deviceSn: '6', |
||||||
|
// tenantId: 162, |
||||||
|
// createTime: 1725533993000, |
||||||
|
// updateTime: 1725533993000, |
||||||
|
// creator: '145', |
||||||
|
// updater: '145', |
||||||
|
// deleted: false, |
||||||
|
// }, |
||||||
|
// }, |
||||||
|
// ] |
||||||
|
// function test(type: string, stationId: number, data?: any, time?: number) { |
||||||
|
// if (type === 'upgrade') { |
||||||
|
// const { deviceSn, taskId } = data |
||||||
|
// if (!taskId || !deviceSn) return |
||||||
|
// // 存储设备状态 |
||||||
|
// let deviceMap = taskMap.get(taskId) |
||||||
|
// if (!deviceMap) { |
||||||
|
// deviceMap = new Map<number, any>() |
||||||
|
// taskMap.set(taskId, deviceMap) |
||||||
|
// } |
||||||
|
// deviceMap.set(deviceSn, data) |
||||||
|
|
||||||
|
// if (detailsRef.value && rowData.value?.id) { |
||||||
|
// const taskId = rowData.value?.id |
||||||
|
// let taskData = taskMap.get(taskId) |
||||||
|
// const dataArray = taskData ? Array.from(taskData.values()) : [] |
||||||
|
// detailsRef.value.updateData(dataArray) |
||||||
|
// } |
||||||
|
// } |
||||||
|
// } |
||||||
|
|
||||||
|
// setInterval(() => { |
||||||
|
// for (const item of a) { |
||||||
|
// test(item.type, item.stationId, item.data, item.time) |
||||||
|
// } |
||||||
|
// }, 1000) |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped lang="scss"> |
||||||
|
.task-management-wrap { |
||||||
|
width: 100%; |
||||||
|
height: 100%; |
||||||
|
.task-table { |
||||||
|
height: 100%; |
||||||
|
box-sizing: border-box; |
||||||
|
|
||||||
|
background: #fff; |
||||||
|
padding-bottom: 0; |
||||||
|
.table { |
||||||
|
height: 100%; |
||||||
|
width: 100%; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,44 @@ |
|||||||
|
export const tableColumns = [ |
||||||
|
{ |
||||||
|
label: '任务名称', |
||||||
|
prop: 'taskName', |
||||||
|
width: '10%', |
||||||
|
}, |
||||||
|
{ |
||||||
|
label: '固件名称', |
||||||
|
prop: 'name', |
||||||
|
width: '8%', |
||||||
|
}, |
||||||
|
{ |
||||||
|
label: '固件版本', |
||||||
|
prop: 'version', |
||||||
|
width: '6%', |
||||||
|
}, |
||||||
|
|
||||||
|
{ |
||||||
|
label: '创建时间', |
||||||
|
prop: 'createTime', |
||||||
|
width: '18%', |
||||||
|
}, |
||||||
|
{ |
||||||
|
label: '任务执行时间', |
||||||
|
prop: 'executeTime', |
||||||
|
width: '18%', |
||||||
|
}, |
||||||
|
{ |
||||||
|
label: '升级详情', |
||||||
|
prop: 'deviceUpgradeDetail', |
||||||
|
width: '10%', |
||||||
|
}, |
||||||
|
{ |
||||||
|
label: '任务状态', |
||||||
|
prop: 'status', |
||||||
|
width: '8%', |
||||||
|
}, |
||||||
|
] |
||||||
|
|
||||||
|
export const taskStatusMap = { |
||||||
|
0: '未开始', |
||||||
|
1: '进行中', |
||||||
|
2: '已完成', |
||||||
|
} |
@ -0,0 +1,87 @@ |
|||||||
|
<template> |
||||||
|
<div class="device-dispose-warp"> |
||||||
|
<div class="top-filter"> |
||||||
|
<FormItemSelect |
||||||
|
:options="[]" |
||||||
|
placeholder="请选择" |
||||||
|
keyTag="value" |
||||||
|
labelTag="label" |
||||||
|
:isShowLabel="false" |
||||||
|
class="filter-select" |
||||||
|
/> |
||||||
|
<el-input class="filter-input" placeholder="请输入" /> |
||||||
|
</div> |
||||||
|
<div class="content-box"> |
||||||
|
<div class="item-info"> |
||||||
|
<div class="info"> |
||||||
|
<div class="type"> |
||||||
|
<div class="reason">处置原因</div> |
||||||
|
<div class="name">报废</div> |
||||||
|
</div> |
||||||
|
<div class="describe"> |
||||||
|
<div>添加人: 向向</div> |
||||||
|
<div>添加时间: 2022-10-31 18:00:00</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div class="operation"> |
||||||
|
<EdfsButton link type="primary" :icon="editIcon" /> |
||||||
|
<EdfsButton link type="danger" :icon="delIcon" style="margin-left: 0px" /> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup lang="ts"> |
||||||
|
import FormItemSelect from '@/components/dashboard/FormItemSelect.vue' |
||||||
|
import { useIcon } from '@/utils/useIcon' |
||||||
|
const editIcon = useIcon({ icon: 'fluent:edit-16-regular' }) |
||||||
|
const delIcon = useIcon({ icon: 'fluent:delete-16-regular' }) |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped lang="scss"> |
||||||
|
.device-dispose-warp { |
||||||
|
.top-filter { |
||||||
|
display: flex; |
||||||
|
height: 32px; |
||||||
|
.filter-select { |
||||||
|
flex: 0.7; |
||||||
|
margin-right: 4px; |
||||||
|
} |
||||||
|
.filter-input { |
||||||
|
flex: 1; |
||||||
|
} |
||||||
|
} |
||||||
|
.content-box { |
||||||
|
width: 100%; |
||||||
|
height: calc(100% - 32px); |
||||||
|
.item-info { |
||||||
|
border-bottom: 1px solid var(--el-border-color); |
||||||
|
padding: 10px 0; |
||||||
|
display: flex; |
||||||
|
.info { |
||||||
|
.type { |
||||||
|
display: flex; |
||||||
|
column-gap: 6px; |
||||||
|
} |
||||||
|
.describe { |
||||||
|
display: flex; |
||||||
|
column-gap: 6px; |
||||||
|
color: var(--text-desc); |
||||||
|
} |
||||||
|
border-left: 2px solid #048b87; |
||||||
|
padding-left: 6px; |
||||||
|
} |
||||||
|
.operation { |
||||||
|
flex: 1; |
||||||
|
display: flex; |
||||||
|
justify-content: end; |
||||||
|
:deep(.el-button) { |
||||||
|
margin-top: auto; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
Loading…
Reference in new issue