323 lines
9.9 KiB
323 lines
9.9 KiB
|
|
<template> |
|
<div class="ml20 mt20 mr20 flex-row flex-align-center flex-justify-between"> |
|
<div class="flex-row"> |
|
<a-button type="primary" @click="sVisible = true"> |
|
Click to Upload |
|
</a-button> |
|
<a-modal :visible="sVisible" |
|
title="Import Firmware File" |
|
:closable="false" |
|
@cancel="onCancel" |
|
@ok="uploadFile" |
|
centered> |
|
<a-form :rules="rules" ref="formRef" :model="uploadParam" :label-col="{ span: 6 }"> |
|
<a-form-item name="status" label="Avaliable" required> |
|
<a-switch v-model:checked="uploadParam.status" /> |
|
</a-form-item> |
|
<a-form-item name="device_name" label="Device Name" required> |
|
<a-select |
|
style="width: 220px" |
|
mode="multiple" |
|
placeholder="can choose multiple" |
|
v-model:value="uploadParam.device_name"> |
|
<a-select-option |
|
v-for="k in DeviceNameEnum" |
|
:key="k" |
|
:value="k" |
|
> |
|
{{ k }} |
|
</a-select-option> |
|
</a-select> |
|
</a-form-item> |
|
<a-form-item name="release_note" label="Release Note" required> |
|
<a-textarea v-model:value="uploadParam.release_note" showCount :maxlength="300" /> |
|
</a-form-item> |
|
<a-form-item label="File" required> |
|
<a-upload |
|
:multiple="false" |
|
:before-upload="beforeUpload" |
|
:show-upload-list="true" |
|
:file-list="fileList" |
|
:remove="removeFile" |
|
> |
|
<a-button type="primary"> |
|
<UploadOutlined /> |
|
Import Firmware File |
|
</a-button> |
|
</a-upload> |
|
</a-form-item> |
|
</a-form> |
|
</a-modal> |
|
</div> |
|
<div class="flex-row"> |
|
<div class="ml5"> |
|
<a-select |
|
style="width: 150px" |
|
v-model:value="param.firmware_status" |
|
@select="getAllFirmwares(pageParam)"> |
|
<a-select-option |
|
v-for="(key, value) in FirmwareStatusEnum" |
|
:key="key" |
|
:value="value" |
|
> |
|
{{ key }} |
|
</a-select-option> |
|
</a-select> |
|
</div> |
|
<div class="ml5"> |
|
<a-select |
|
style="width: 150px" |
|
v-model:value="param.device_name" |
|
@select="getAllFirmwares(pageParam)"> |
|
<a-select-option |
|
v-for="item in deviceNameList" |
|
:key="item.label" |
|
:value="item.value" |
|
> |
|
{{ item.label }} |
|
</a-select-option> |
|
</a-select> |
|
</div> |
|
<div class="ml5"> |
|
<a-input-search |
|
:enter-button="true" |
|
v-model:value="param.product_version" |
|
placeholder="input search verison" |
|
style="width: 250px" |
|
@search="getAllFirmwares(pageParam)"/> |
|
</div> |
|
</div> |
|
</div> |
|
<div class="table flex-display flex-column"> |
|
<a-table :columns="columns" :data-source="data.firmware" :pagination="paginationProp" @change="refreshData" row-key="firmware_id" |
|
:rowClassName="(record, index) => ((index % 2) === 0 ? 'table-striped' : null)" :scroll="{ x: '100%', y: 600 }"> |
|
<template #device_name="{ record }"> |
|
<div v-for="text in record.device_name" :key="text"> |
|
{{ text }} |
|
</div> |
|
</template> |
|
<template #file_size="{ record }"> |
|
<div>{{ bytesToSize(record.file_size) }}</div> |
|
</template> |
|
<template #firmware_status="{ record }"> |
|
<DeviceFirmwareStatus :firmware="record" /> |
|
</template> |
|
<template v-for="col in ['file_name', 'release_note']" #[col]="{ text }" :key="col"> |
|
<a-tooltip :title="text"> |
|
<span>{{ text }}</span> |
|
</a-tooltip> |
|
</template> |
|
|
|
</a-table> |
|
</div> |
|
</template> |
|
<script lang="ts" setup> |
|
import { message, notification, PaginationProps } from 'ant-design-vue' |
|
import { TableState } from 'ant-design-vue/lib/table/interface' |
|
import { onMounted, reactive, Ref, ref, UnwrapRef } from 'vue' |
|
import { IPage } from '/@/api/http/type' |
|
import { getFirmwares, importFirmareFile } from '/@/api/manage' |
|
import DeviceFirmwareStatus from '/@/components/devices/DeviceFirmwareStatus.vue' |
|
import { ELocalStorageKey } from '/@/types' |
|
import { UploadOutlined } from '@ant-design/icons-vue' |
|
import { Firmware, FirmwareQueryParam, FirmwareStatusEnum, DeviceNameEnum, FirmwareUploadParam } from '/@/types/device-firmware' |
|
import { commonColor } from '/@/utils/color' |
|
import { bytesToSize } from '/@/utils/bytes' |
|
import moment from 'moment' |
|
|
|
interface FirmwareData { |
|
firmware: Firmware[] |
|
} |
|
const columns = [ |
|
{ title: 'Model', dataIndex: 'device_name', width: 120, ellipsis: true, className: 'titleStyle', slots: { customRender: 'device_name' } }, |
|
{ title: 'File Name', dataIndex: 'file_name', width: 220, ellipsis: true, className: 'titleStyle', slots: { customRender: 'file_name' } }, |
|
{ title: 'Firmware Version', dataIndex: 'product_version', width: 180, className: 'titleStyle' }, |
|
{ title: 'File Size', dataIndex: 'file_size', width: 150, className: 'titleStyle', slots: { customRender: 'file_size' } }, |
|
{ title: 'Creator', dataIndex: 'username', width: 100, className: 'titleStyle' }, |
|
{ title: 'Release Date', dataIndex: 'released_time', width: 160, sorter: (a: Firmware, b: Firmware) => a.released_time.localeCompare(b.released_time), className: 'titleStyle' }, |
|
{ title: 'Release Note', dataIndex: 'release_note', width: 300, ellipsis: true, className: 'titleStyle', slots: { customRender: 'release_note' } }, |
|
{ title: 'Status', dataIndex: 'firmware_status', width: 100, className: 'titleStyle', slots: { customRender: 'firmware_status' } }, |
|
] |
|
|
|
const data = reactive<FirmwareData>({ |
|
firmware: [] |
|
}) |
|
|
|
const paginationProp = reactive({ |
|
pageSizeOptions: ['20', '50', '100'], |
|
showQuickJumper: true, |
|
showSizeChanger: true, |
|
pageSize: 50, |
|
current: 1, |
|
total: 0 |
|
}) |
|
|
|
const deviceNameList = ref<any[]>([{ label: 'All', value: '' }]) |
|
|
|
type Pagination = TableState['pagination'] |
|
|
|
const pageParam: IPage = { |
|
page: 1, |
|
total: 0, |
|
page_size: 50 |
|
} |
|
const workspaceId: string = localStorage.getItem(ELocalStorageKey.WorkspaceId)! |
|
|
|
const param = reactive<FirmwareQueryParam>({ |
|
product_version: '', |
|
device_name: '', |
|
firmware_status: FirmwareStatusEnum.NONE |
|
}) |
|
|
|
onMounted(() => { |
|
getAllFirmwares(pageParam) |
|
for (const key in DeviceNameEnum) { |
|
const value = DeviceNameEnum[key] |
|
deviceNameList.value.push({ label: value, value: value }) |
|
} |
|
}) |
|
|
|
function refreshData (page: Pagination) { |
|
pageParam.page = page?.current! |
|
pageParam.page_size = page?.pageSize! |
|
getAllFirmwares(pageParam) |
|
} |
|
|
|
function getAllFirmwares (page: IPage) { |
|
getFirmwares(workspaceId, page, param).then(res => { |
|
const firmwareList: Firmware[] = res.data.list |
|
data.firmware = firmwareList |
|
paginationProp.total = res.data.pagination.total |
|
paginationProp.current = res.data.pagination.page |
|
}) |
|
} |
|
|
|
const sVisible = ref(false) |
|
const uploadParam = reactive<FirmwareUploadParam>({ |
|
device_name: [], |
|
release_note: '', |
|
status: true |
|
}) |
|
|
|
const rules = { |
|
status: [{ required: true }], |
|
release_note: [{ required: true, message: 'Please input release note.' }], |
|
device_name: [{ required: true, message: 'Please select which models this firmware belongs to.' }] |
|
} |
|
interface FileItem { |
|
uid: string; |
|
name?: string; |
|
status?: string; |
|
response?: string; |
|
url?: string; |
|
} |
|
|
|
interface FileInfo { |
|
file: FileItem; |
|
fileList: FileItem[]; |
|
} |
|
const fileList = ref<FileItem[]>([]) |
|
|
|
function beforeUpload (file: FileItem) { |
|
if (!file.name || !file.name?.endsWith('.zip')) { |
|
message.error('Format error. Please select zip file.') |
|
return false |
|
} |
|
fileList.value = [file] |
|
return false |
|
} |
|
|
|
const formRef = ref() |
|
function removeFile (file: FileItem) { |
|
fileList.value = [] |
|
} |
|
function onCancel () { |
|
formRef.value.resetFields() |
|
fileList.value = [] |
|
sVisible.value = false |
|
} |
|
|
|
const uploadFile = async () => { |
|
if (fileList.value.length === 0) { |
|
message.error('Please select at least one file.') |
|
} |
|
const uploading: string = 'uploading' |
|
formRef.value.validate().then(async () => { |
|
const file: FileItem = fileList.value[0] |
|
const fileData = new FormData() |
|
fileData.append('file', file as any, file.name) |
|
Object.keys(uploadParam).forEach((key) => { |
|
const val = uploadParam[key as keyof FirmwareUploadParam] |
|
if (val instanceof Array) { |
|
val.forEach((value) => { |
|
fileData.append(key, value) |
|
}) |
|
} else { |
|
fileData.append(key, val.toString()) |
|
} |
|
}) |
|
notification.open({ |
|
key: uploading, |
|
message: `Uploading ${moment().format()}`, |
|
description: `[${file.name}] is uploading... `, |
|
duration: null |
|
}) |
|
importFirmareFile(workspaceId, fileData).then((res) => { |
|
if (res.code === 0) { |
|
notification.success({ |
|
message: `Uploaded ${moment().format()}`, |
|
description: `[${file.name}] file uploaded successfully.`, |
|
duration: null |
|
}) |
|
getAllFirmwares(pageParam) |
|
} else { |
|
notification.error({ |
|
message: `Failed to upload [${file.name}]. Check and try again.`, |
|
description: `Error message: ${res.message} ${moment().format()}`, |
|
style: { color: commonColor.FAIL }, |
|
duration: null, |
|
}) |
|
} |
|
}).finally(() => { |
|
notification.close(uploading) |
|
}) |
|
fileList.value = [] |
|
formRef.value.resetFields() |
|
sVisible.value = false |
|
}) |
|
} |
|
|
|
</script> |
|
<style> |
|
|
|
.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; |
|
} |
|
</style>
|
|
|