设备固件管理
This commit is contained in:
parent
d672ea04aa
commit
c09ca5eab0
@ -4377,7 +4377,7 @@ export const FileObjectDtoSchema = {
|
||||
description: '文件名称',
|
||||
nullable: true
|
||||
},
|
||||
mD5Hash: {
|
||||
md5Hash: {
|
||||
type: 'string',
|
||||
description: '文件MD5',
|
||||
nullable: true
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -2406,7 +2406,7 @@ export type FileObjectDto = {
|
||||
/**
|
||||
* 文件MD5
|
||||
*/
|
||||
mD5Hash?: (string) | null;
|
||||
md5Hash?: (string) | null;
|
||||
};
|
||||
|
||||
export type FileQoSOptions = {
|
||||
@ -6597,6 +6597,16 @@ export type PostFirmwareInfoFindByIdAsyncResponse = (DeviceFirmwareInfoDto);
|
||||
|
||||
export type PostFirmwareInfoFindByIdAsyncError = unknown;
|
||||
|
||||
export type PostFirmwareInfoUpdateStatusByIdAsyncData = {
|
||||
query?: {
|
||||
input?: IdInput;
|
||||
};
|
||||
};
|
||||
|
||||
export type PostFirmwareInfoUpdateStatusByIdAsyncResponse = (DeviceFirmwareInfoDto);
|
||||
|
||||
export type PostFirmwareInfoUpdateStatusByIdAsyncError = unknown;
|
||||
|
||||
export type PostFirmwareInfoFindByDeviceProductIdAsyncData = {
|
||||
query?: {
|
||||
input?: StringIdInput;
|
||||
|
||||
@ -0,0 +1,530 @@
|
||||
<script setup lang="ts">
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import { computed, h, nextTick, ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Button, message as Message, Modal, Tag } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
postAggregationIoTplatformGetIoTplatformProductInfoAsync,
|
||||
postFirmwareInfoCreateAsync,
|
||||
postFirmwareInfoDeleteAsync,
|
||||
postFirmwareInfoFindByIdAsync,
|
||||
postFirmwareInfoPage,
|
||||
postFirmwareInfoUpdateAsync,
|
||||
postFirmwareInfoUpdateStatusByIdAsync,
|
||||
postFilesDownload,
|
||||
postFilesUpload,
|
||||
} from '#/api-client';
|
||||
import { TableAction } from '#/components/table-action';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import {
|
||||
addFirmwareFormSchema,
|
||||
editFirmwareFormSchema,
|
||||
querySchema,
|
||||
tableSchema,
|
||||
} from './schema';
|
||||
|
||||
defineOptions({
|
||||
name: 'DeviceFirmwareInfo',
|
||||
});
|
||||
|
||||
const formOptions: VbenFormProps = {
|
||||
schema: querySchema.value,
|
||||
submitOnChange: false,
|
||||
handleValuesChange: async (values, changedFields) => {
|
||||
// 当平台类型变化时,清除产品ID
|
||||
if (changedFields.includes('ioTPlatform')) {
|
||||
if (gridApi?.formApi) {
|
||||
await gridApi.formApi.setFieldValue('ioTPlatformProductId', undefined);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const gridOptions: VxeGridProps<any> = {
|
||||
columns: tableSchema.value,
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
toolbarConfig: {
|
||||
custom: true,
|
||||
},
|
||||
customConfig: {
|
||||
storage: true,
|
||||
},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
const currentFormValues = gridApi?.formApi
|
||||
? await gridApi.formApi.getValues()
|
||||
: formValues || {};
|
||||
|
||||
const finalFormValues = { ...formValues, ...currentFormValues };
|
||||
|
||||
const { data } = await postFirmwareInfoPage({
|
||||
query: {
|
||||
input: {
|
||||
pageIndex: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...finalFormValues,
|
||||
},
|
||||
},
|
||||
});
|
||||
return data;
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({ formOptions, gridOptions });
|
||||
|
||||
const editRow: Record<string, any> = ref({});
|
||||
|
||||
const [UserModal, userModalApi] = useVbenModal({
|
||||
draggable: true,
|
||||
onConfirm: submit,
|
||||
onBeforeClose: () => {
|
||||
editRow.value = {};
|
||||
// 清除全局文件变量
|
||||
(window as any).__selectedFirmwareFile = null;
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
const [AddForm, addFormApi] = useVbenForm({
|
||||
collapsed: false,
|
||||
commonConfig: {
|
||||
labelWidth: 120,
|
||||
componentProps: {
|
||||
class: 'w-4/5',
|
||||
},
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: addFirmwareFormSchema.value,
|
||||
showCollapseButton: false,
|
||||
showDefaultActions: false,
|
||||
wrapperClass: 'grid-cols-2',
|
||||
handleValuesChange: async (values, changedFields) => {
|
||||
// 当平台类型变化时,清除产品ID
|
||||
if (changedFields.includes('ioTPlatform')) {
|
||||
await addFormApi.setFieldValue('ioTPlatformProductId', undefined);
|
||||
}
|
||||
// 当产品选择变化时,自动设置产品名称
|
||||
if (changedFields.includes('ioTPlatformProductId') && values.ioTPlatformProductId && values.ioTPlatform) {
|
||||
const productId = values.ioTPlatformProductId;
|
||||
const platform = values.ioTPlatform;
|
||||
|
||||
// 通过API获取产品信息
|
||||
try {
|
||||
const result = await postAggregationIoTplatformGetIoTplatformProductInfoAsync({
|
||||
body: {
|
||||
ioTPlatformType:
|
||||
typeof platform === 'string'
|
||||
? Number.parseInt(platform)
|
||||
: platform,
|
||||
},
|
||||
});
|
||||
|
||||
let products: any[] = [];
|
||||
if (Array.isArray(result.data)) {
|
||||
products = result.data;
|
||||
} else if (result.data && Array.isArray(result.data.items)) {
|
||||
products = result.data.items;
|
||||
} else if (result.data && Array.isArray(result.data.data)) {
|
||||
products = result.data.data;
|
||||
}
|
||||
|
||||
const selectedProduct = products.find(
|
||||
(p: any) => String(p.ioTPlatformProductId) === String(productId),
|
||||
);
|
||||
|
||||
if (selectedProduct && selectedProduct.productName) {
|
||||
await addFormApi.setFieldValue('ioTPlatformProductName', selectedProduct.productName);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取产品信息失败:', error);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const [EditForm, editFormApi] = useVbenForm({
|
||||
collapsed: false,
|
||||
commonConfig: {
|
||||
labelWidth: 120,
|
||||
componentProps: {
|
||||
class: 'w-4/5',
|
||||
},
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: editFirmwareFormSchema.value,
|
||||
showCollapseButton: false,
|
||||
showDefaultActions: false,
|
||||
wrapperClass: 'grid-cols-2',
|
||||
handleValuesChange: async (values, changedFields) => {
|
||||
// 当平台类型变化时,清除产品ID
|
||||
if (changedFields.includes('ioTPlatform')) {
|
||||
await editFormApi.setFieldValue('ioTPlatformProductId', undefined);
|
||||
}
|
||||
// 当产品选择变化时,自动设置产品名称
|
||||
if (changedFields.includes('ioTPlatformProductId') && values.ioTPlatformProductId && values.ioTPlatform) {
|
||||
const productId = values.ioTPlatformProductId;
|
||||
const platform = values.ioTPlatform;
|
||||
|
||||
// 通过API获取产品信息
|
||||
try {
|
||||
const result = await postAggregationIoTplatformGetIoTplatformProductInfoAsync({
|
||||
body: {
|
||||
ioTPlatformType:
|
||||
typeof platform === 'string'
|
||||
? Number.parseInt(platform)
|
||||
: platform,
|
||||
},
|
||||
});
|
||||
|
||||
let products: any[] = [];
|
||||
if (Array.isArray(result.data)) {
|
||||
products = result.data;
|
||||
} else if (result.data && Array.isArray(result.data.items)) {
|
||||
products = result.data.items;
|
||||
} else if (result.data && Array.isArray(result.data.data)) {
|
||||
products = result.data.data;
|
||||
}
|
||||
|
||||
const selectedProduct = products.find(
|
||||
(p: any) => String(p.ioTPlatformProductId) === String(productId),
|
||||
);
|
||||
|
||||
if (selectedProduct && selectedProduct.productName) {
|
||||
await editFormApi.setFieldValue('ioTPlatformProductName', selectedProduct.productName);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取产品信息失败:', error);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// 新增和编辑提交的逻辑
|
||||
async function submit() {
|
||||
const isEdit = !!editRow.value.id;
|
||||
const formApi = isEdit ? editFormApi : addFormApi;
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) return;
|
||||
|
||||
const formValues = await formApi.getValues();
|
||||
|
||||
// 如果产品名称为空,通过API获取产品名称
|
||||
if (!formValues.ioTPlatformProductName && formValues.ioTPlatformProductId && formValues.ioTPlatform) {
|
||||
try {
|
||||
const result = await postAggregationIoTplatformGetIoTplatformProductInfoAsync({
|
||||
body: {
|
||||
ioTPlatformType:
|
||||
typeof formValues.ioTPlatform === 'string'
|
||||
? Number.parseInt(formValues.ioTPlatform)
|
||||
: formValues.ioTPlatform,
|
||||
},
|
||||
});
|
||||
|
||||
let products: any[] = [];
|
||||
if (Array.isArray(result.data)) {
|
||||
products = result.data;
|
||||
} else if (result.data && Array.isArray(result.data.items)) {
|
||||
products = result.data.items;
|
||||
} else if (result.data && Array.isArray(result.data.data)) {
|
||||
products = result.data.data;
|
||||
}
|
||||
|
||||
const selectedProduct = products.find(
|
||||
(p: any) => String(p.ioTPlatformProductId) === String(formValues.ioTPlatformProductId),
|
||||
);
|
||||
|
||||
if (selectedProduct && selectedProduct.productName) {
|
||||
formValues.ioTPlatformProductName = selectedProduct.productName;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取产品信息失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否选择了新文件
|
||||
const selectedFile = (window as any).__selectedFirmwareFile;
|
||||
// 默认使用表单中的文件信息(编辑时保留原有文件)
|
||||
let fileId = formValues.firmwareFileId;
|
||||
let fileName = formValues.firmwareFileName;
|
||||
let fileHash = formValues.firmwareHashCode;
|
||||
let fileLength = formValues.firmwareLength;
|
||||
|
||||
// 如果选择了新文件,先上传文件
|
||||
if (selectedFile) {
|
||||
try {
|
||||
userModalApi.setState({ loading: true, confirmLoading: true });
|
||||
const uploadResult = await postFilesUpload({
|
||||
body: {
|
||||
files: [selectedFile],
|
||||
},
|
||||
});
|
||||
|
||||
if (uploadResult.data && uploadResult.data.length > 0) {
|
||||
const uploadedFile = uploadResult.data[0];
|
||||
fileId = uploadedFile.id;
|
||||
fileName = uploadedFile.fileName || selectedFile.name;
|
||||
// 注意:字段名是 md5Hash(小写m),不是 mD5Hash
|
||||
fileHash = uploadedFile.md5Hash || uploadedFile.mD5Hash || '';
|
||||
fileLength = uploadedFile.fileSize || selectedFile.size;
|
||||
} else {
|
||||
Message.error('文件上传失败');
|
||||
userModalApi.setState({ loading: false, confirmLoading: false });
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('文件上传失败:', error);
|
||||
Message.error('文件上传失败');
|
||||
userModalApi.setState({ loading: false, confirmLoading: false });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有文件ID,提示错误
|
||||
if (!fileId) {
|
||||
Message.error('请选择固件文件');
|
||||
userModalApi.setState({ loading: false, confirmLoading: false });
|
||||
return;
|
||||
}
|
||||
|
||||
const fetchParams: any = {
|
||||
ioTPlatform:
|
||||
typeof formValues.ioTPlatform === 'string'
|
||||
? Number.parseInt(formValues.ioTPlatform)
|
||||
: formValues.ioTPlatform,
|
||||
ioTPlatformProductId: String(formValues.ioTPlatformProductId),
|
||||
ioTPlatformProductName: formValues.ioTPlatformProductName || '',
|
||||
firmwareVersion: formValues.firmwareVersion,
|
||||
firmwareFileId: fileId,
|
||||
firmwareFileName: fileName,
|
||||
firmwareHashCode: fileHash,
|
||||
firmwareLength: fileLength,
|
||||
};
|
||||
|
||||
if (isEdit) {
|
||||
fetchParams.id = editRow.value.id;
|
||||
}
|
||||
|
||||
try {
|
||||
const api = isEdit
|
||||
? postFirmwareInfoUpdateAsync
|
||||
: postFirmwareInfoCreateAsync;
|
||||
const resp = await api({
|
||||
body: fetchParams,
|
||||
});
|
||||
|
||||
if (resp.data) {
|
||||
Message.success(
|
||||
isEdit ? $t('common.editSuccess') : $t('common.addSuccess'),
|
||||
);
|
||||
userModalApi.close();
|
||||
editRow.value = {};
|
||||
// 清除全局文件变量
|
||||
(window as any).__selectedFirmwareFile = null;
|
||||
gridApi.reload();
|
||||
} else {
|
||||
Message.error(isEdit ? $t('common.editFail') : $t('common.addFail'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('固件操作失败:', error);
|
||||
Message.error(isEdit ? $t('common.editFail') : $t('common.addFail'));
|
||||
} finally {
|
||||
userModalApi.setState({ loading: false, confirmLoading: false });
|
||||
}
|
||||
}
|
||||
|
||||
async function onEdit(record: any) {
|
||||
editRow.value = record;
|
||||
userModalApi.open();
|
||||
|
||||
// 设置表单值
|
||||
const formValues: any = { ...record };
|
||||
|
||||
// 确保ioTPlatform是字符串格式
|
||||
if (formValues.ioTPlatform !== undefined && formValues.ioTPlatform !== null) {
|
||||
formValues.ioTPlatform = String(formValues.ioTPlatform);
|
||||
}
|
||||
|
||||
// 清除文件选择
|
||||
(window as any).__selectedFirmwareFile = null;
|
||||
|
||||
editFormApi.setValues(formValues);
|
||||
}
|
||||
|
||||
function onDel(row: any) {
|
||||
Modal.confirm({
|
||||
title: `${$t('common.confirmDelete')}固件版本 ${row.firmwareVersion} ?`,
|
||||
onOk: async () => {
|
||||
try {
|
||||
const result = await postFirmwareInfoDeleteAsync({
|
||||
body: { id: row.id },
|
||||
});
|
||||
if (result.data) {
|
||||
gridApi.reload();
|
||||
Message.success($t('common.deleteSuccess'));
|
||||
} else {
|
||||
Message.error($t('common.deleteFail'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除固件失败:', error);
|
||||
Message.error($t('common.deleteFail'));
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 切换启用/禁用状态
|
||||
async function toggleStatus(row: any) {
|
||||
const action = row.isEnable ? '禁用' : '启用';
|
||||
Modal.confirm({
|
||||
title: `确认${action}固件版本 ${row.firmwareVersion} ?`,
|
||||
onOk: async () => {
|
||||
try {
|
||||
const result = await postFirmwareInfoUpdateStatusByIdAsync({
|
||||
body: { id: row.id },
|
||||
});
|
||||
if (result.data) {
|
||||
gridApi.reload();
|
||||
Message.success(`${action}成功`);
|
||||
} else {
|
||||
Message.error(`${action}失败`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`${action}固件失败:`, error);
|
||||
Message.error(`${action}失败`);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const openAddModal = async () => {
|
||||
editRow.value = {};
|
||||
// 清除文件选择
|
||||
(window as any).__selectedFirmwareFile = null;
|
||||
userModalApi.open();
|
||||
await nextTick();
|
||||
addFormApi.resetForm();
|
||||
};
|
||||
|
||||
// 下载文件函数
|
||||
async function onDownloadFile(row: any) {
|
||||
if (!row.firmwareFileId) {
|
||||
Message.error('文件ID不存在,无法下载');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { data } = await postFilesDownload({
|
||||
body: { id: row.firmwareFileId },
|
||||
responseType: 'blob',
|
||||
});
|
||||
const url = window.URL.createObjectURL(new Blob([data as Blob]));
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.setAttribute(
|
||||
'download',
|
||||
row.firmwareFileName || 'firmware-file',
|
||||
);
|
||||
document.body.append(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
window.URL.revokeObjectURL(url);
|
||||
Message.success('文件下载成功');
|
||||
} catch (error) {
|
||||
console.error('文件下载失败:', error);
|
||||
Message.error('文件下载失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 工具栏按钮配置
|
||||
const toolbarActions = computed(() => [
|
||||
{
|
||||
label: $t('common.add'),
|
||||
type: 'primary',
|
||||
icon: 'ant-design:plus-outlined',
|
||||
onClick: openAddModal.bind(null),
|
||||
auth: ['AbpIdentity.Users.Create'],
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<Grid>
|
||||
<template #toolbar-actions>
|
||||
<TableAction :actions="toolbarActions" />
|
||||
</template>
|
||||
|
||||
<template #isEnable="{ row }">
|
||||
<component
|
||||
:is="
|
||||
h(
|
||||
Tag,
|
||||
{ color: row.isEnable ? 'green' : 'red' },
|
||||
() => (row.isEnable ? '启用' : '禁用'),
|
||||
)
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #firmwareFileName="{ row }">
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
style="padding: 0"
|
||||
:disabled="!row.firmwareFileId"
|
||||
@click="onDownloadFile.bind(null, row)()"
|
||||
>
|
||||
{{ row.firmwareFileName || '-' }}
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<template #action="{ row }">
|
||||
<div style="display: flex; gap: 8px; align-items: center">
|
||||
<Button
|
||||
size="small"
|
||||
type="link"
|
||||
@click="onEdit.bind(null, row)()"
|
||||
>
|
||||
{{ $t('common.edit') }}
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
type="link"
|
||||
:style="{ color: row.isEnable ? '#ff4d4f' : '#52c41a' }"
|
||||
@click="toggleStatus.bind(null, row)()"
|
||||
>
|
||||
{{ row.isEnable ? '禁用' : '启用' }}
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
type="link"
|
||||
style="color: #ff4d4f"
|
||||
@click="onDel.bind(null, row)()"
|
||||
>
|
||||
{{ $t('common.delete') }}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</Grid>
|
||||
<UserModal
|
||||
:title="editRow.id ? $t('common.edit') : $t('common.add')"
|
||||
class="w-[800px]"
|
||||
>
|
||||
<component :is="editRow.id ? EditForm : AddForm" />
|
||||
</UserModal>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
@ -0,0 +1,561 @@
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import { computed, h } from 'vue';
|
||||
|
||||
import { z } from '@vben/common-ui';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import {
|
||||
getCommonGetSelectList,
|
||||
postAggregationIoTplatformGetIoTplatformProductInfoAsync,
|
||||
} from '#/api-client';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
export const querySchema = computed(() => [
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'searchKeyword',
|
||||
label: '搜索关键字',
|
||||
componentProps: {
|
||||
placeholder: '请输入产品名称',
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'ApiSelect',
|
||||
fieldName: 'ioTPlatform',
|
||||
label: $t('abp.deviceInfos.ioTPlatform'),
|
||||
componentProps: {
|
||||
api: getCommonGetSelectList,
|
||||
params: {
|
||||
query: {
|
||||
typeName: 'IoTPlatformTypeEnum',
|
||||
},
|
||||
},
|
||||
labelField: 'value',
|
||||
valueField: 'key',
|
||||
optionsPropName: 'options',
|
||||
immediate: true,
|
||||
allowClear: true,
|
||||
placeholder: `${$t('common.pleaseSelect')}${$t('abp.deviceInfos.ioTPlatform')}`,
|
||||
afterFetch: (res: any) => {
|
||||
if (Array.isArray(res)) {
|
||||
return res;
|
||||
}
|
||||
if (res && Array.isArray(res.items)) {
|
||||
return res.items;
|
||||
}
|
||||
if (res && Array.isArray(res.data)) {
|
||||
return res.data;
|
||||
}
|
||||
return [];
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'ApiSelect',
|
||||
fieldName: 'ioTPlatformProductId',
|
||||
label: $t('common.BelongingProductName'),
|
||||
dependencies: {
|
||||
show(values: any) {
|
||||
return !!values.ioTPlatform;
|
||||
},
|
||||
triggerFields: ['ioTPlatform'],
|
||||
},
|
||||
componentProps: (formValues: any) => {
|
||||
const platform = formValues?.ioTPlatform;
|
||||
|
||||
return {
|
||||
api: platform
|
||||
? postAggregationIoTplatformGetIoTplatformProductInfoAsync
|
||||
: null,
|
||||
params: platform
|
||||
? {
|
||||
body: {
|
||||
ioTPlatformType:
|
||||
typeof platform === 'string'
|
||||
? Number.parseInt(platform)
|
||||
: platform,
|
||||
},
|
||||
}
|
||||
: {},
|
||||
labelField: 'productName',
|
||||
valueField: 'ioTPlatformProductId',
|
||||
optionsPropName: 'options',
|
||||
immediate: false,
|
||||
allowClear: true,
|
||||
placeholder:
|
||||
$t('common.pleaseSelect') + $t('common.BelongingProductName'),
|
||||
afterFetch: (res: any) => {
|
||||
if (Array.isArray(res)) {
|
||||
return res;
|
||||
}
|
||||
if (res && Array.isArray(res.items)) {
|
||||
return res.items;
|
||||
}
|
||||
if (res && Array.isArray(res.data)) {
|
||||
return res.data;
|
||||
}
|
||||
if (res && res.data && Array.isArray(res.data.items)) {
|
||||
return res.data.items;
|
||||
}
|
||||
return [];
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
export const tableSchema: any = computed((): VxeGridProps['columns'] => [
|
||||
{ title: $t('common.seq'), type: 'seq', width: 50 },
|
||||
{
|
||||
field: 'ioTPlatform',
|
||||
title: $t('common.BelongingIoTPlatform'),
|
||||
minWidth: '150',
|
||||
formatter: ({ cellValue }) => {
|
||||
const platformMap: Record<string, string> = {
|
||||
'1': 'CTWing',
|
||||
'2': 'OneNET',
|
||||
};
|
||||
return platformMap[String(cellValue)] || cellValue || '-';
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'ioTPlatformProductName',
|
||||
title: $t('common.BelongingProductName'),
|
||||
minWidth: '150',
|
||||
},
|
||||
{
|
||||
field: 'firmwareVersion',
|
||||
title: '固件版本',
|
||||
minWidth: '150',
|
||||
},
|
||||
{
|
||||
field: 'firmwareFileName',
|
||||
title: '固件文件名称',
|
||||
minWidth: '200',
|
||||
slots: { default: 'firmwareFileName' },
|
||||
},
|
||||
{
|
||||
field: 'firmwareLength',
|
||||
title: '文件大小',
|
||||
minWidth: '120',
|
||||
formatter: ({ cellValue }) => {
|
||||
if (!cellValue) return '-';
|
||||
const bytes = Number(cellValue);
|
||||
if (bytes < 1024) return `${bytes} B`;
|
||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`;
|
||||
if (bytes < 1024 * 1024 * 1024)
|
||||
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
||||
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'firmwareHashCode',
|
||||
title: '哈希值',
|
||||
minWidth: '200',
|
||||
showOverflow: 'tooltip',
|
||||
},
|
||||
{
|
||||
field: 'isEnable',
|
||||
title: '是否启用',
|
||||
minWidth: '100',
|
||||
slots: { default: 'isEnable' },
|
||||
},
|
||||
{
|
||||
field: 'creationTime',
|
||||
title: '创建时间',
|
||||
minWidth: '180',
|
||||
formatter: ({ cellValue }) => {
|
||||
return cellValue ? dayjs(cellValue).format('YYYY-MM-DD HH:mm:ss') : '';
|
||||
},
|
||||
},
|
||||
{
|
||||
title: $t('common.action'),
|
||||
field: 'action',
|
||||
fixed: 'right',
|
||||
width: '200',
|
||||
slots: { default: 'action' },
|
||||
},
|
||||
]);
|
||||
|
||||
export const addFirmwareFormSchema: any = computed(() => [
|
||||
{
|
||||
component: 'ApiSelect',
|
||||
fieldName: 'ioTPlatform',
|
||||
label: $t('abp.deviceInfos.ioTPlatform'),
|
||||
componentProps: {
|
||||
api: getCommonGetSelectList,
|
||||
params: {
|
||||
query: {
|
||||
typeName: 'IoTPlatformTypeEnum',
|
||||
},
|
||||
},
|
||||
labelField: 'value',
|
||||
valueField: 'key',
|
||||
optionsPropName: 'options',
|
||||
immediate: true,
|
||||
allowClear: true,
|
||||
placeholder:
|
||||
$t('common.pleaseSelect') + $t('abp.deviceInfos.ioTPlatform'),
|
||||
afterFetch: (res: any) => {
|
||||
if (Array.isArray(res)) {
|
||||
return res;
|
||||
}
|
||||
if (res && Array.isArray(res.items)) {
|
||||
return res.items;
|
||||
}
|
||||
if (res && Array.isArray(res.data)) {
|
||||
return res.data;
|
||||
}
|
||||
return [];
|
||||
},
|
||||
},
|
||||
rules: z.string().min(1, {
|
||||
message: `${$t('common.pleaseSelect')}${$t('abp.deviceInfos.ioTPlatform')}`,
|
||||
}),
|
||||
},
|
||||
{
|
||||
component: 'ApiSelect',
|
||||
fieldName: 'ioTPlatformProductId',
|
||||
label: $t('common.BelongingProductName'),
|
||||
dependencies: {
|
||||
show(values: any) {
|
||||
return !!values.ioTPlatform;
|
||||
},
|
||||
rules(values: any) {
|
||||
if (values.ioTPlatform) {
|
||||
return 'required';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
triggerFields: ['ioTPlatform'],
|
||||
},
|
||||
componentProps: (formValues: any) => {
|
||||
const platform = formValues?.ioTPlatform;
|
||||
|
||||
return {
|
||||
api: platform
|
||||
? postAggregationIoTplatformGetIoTplatformProductInfoAsync
|
||||
: null,
|
||||
params: platform
|
||||
? {
|
||||
body: {
|
||||
ioTPlatformType:
|
||||
typeof platform === 'string'
|
||||
? Number.parseInt(platform)
|
||||
: platform,
|
||||
},
|
||||
}
|
||||
: {},
|
||||
labelField: 'productName',
|
||||
valueField: 'ioTPlatformProductId',
|
||||
optionsPropName: 'options',
|
||||
immediate: false,
|
||||
allowClear: true,
|
||||
placeholder:
|
||||
$t('common.pleaseSelect') + $t('common.BelongingProductName'),
|
||||
afterFetch: (res: any) => {
|
||||
if (Array.isArray(res)) {
|
||||
return res;
|
||||
}
|
||||
if (res && Array.isArray(res.items)) {
|
||||
return res.items;
|
||||
}
|
||||
if (res && Array.isArray(res.data)) {
|
||||
return res.data;
|
||||
}
|
||||
if (res && res.data && Array.isArray(res.data.items)) {
|
||||
return res.data.items;
|
||||
}
|
||||
return [];
|
||||
},
|
||||
};
|
||||
},
|
||||
rules: z.string().optional(),
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'firmwareVersion',
|
||||
label: '固件版本',
|
||||
componentProps: {
|
||||
placeholder: '请输入固件版本',
|
||||
},
|
||||
rules: z.string().min(1, {
|
||||
message: '请输入固件版本',
|
||||
}),
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'firmwareFileName',
|
||||
label: '固件文件名称',
|
||||
componentProps: {
|
||||
placeholder: '请选择文件',
|
||||
readonly: true,
|
||||
addonAfter: h(
|
||||
'button',
|
||||
{
|
||||
type: 'button',
|
||||
style:
|
||||
'border: none; background: #1890ff; color: white; padding: 4px 8px; border-radius: 4px; cursor: pointer;',
|
||||
onClick: () => {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = '*/*';
|
||||
input.addEventListener('change', (e: any) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
const currentInput = document.querySelector(
|
||||
'input[placeholder="请选择文件"]',
|
||||
) as HTMLInputElement;
|
||||
if (currentInput) {
|
||||
currentInput.value = file.name;
|
||||
currentInput.dispatchEvent(
|
||||
new Event('input', { bubbles: true }),
|
||||
);
|
||||
currentInput.dispatchEvent(
|
||||
new Event('change', { bubbles: true }),
|
||||
);
|
||||
// 存储文件对象到全局变量
|
||||
(window as any).__selectedFirmwareFile = file;
|
||||
}
|
||||
}
|
||||
});
|
||||
input.click();
|
||||
},
|
||||
},
|
||||
'选择文件',
|
||||
),
|
||||
},
|
||||
rules: z.string().min(1, {
|
||||
message: '请选择固件文件',
|
||||
}),
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'firmwareFileId',
|
||||
label: '',
|
||||
componentProps: {
|
||||
type: 'hidden',
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'firmwareHashCode',
|
||||
label: '',
|
||||
componentProps: {
|
||||
type: 'hidden',
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'firmwareLength',
|
||||
label: '',
|
||||
componentProps: {
|
||||
type: 'hidden',
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'ioTPlatformProductName',
|
||||
label: '',
|
||||
componentProps: {
|
||||
type: 'hidden',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
export const editFirmwareFormSchema: any = computed(() => [
|
||||
{
|
||||
component: 'ApiSelect',
|
||||
fieldName: 'ioTPlatform',
|
||||
label: $t('abp.deviceInfos.ioTPlatform'),
|
||||
componentProps: {
|
||||
api: getCommonGetSelectList,
|
||||
params: {
|
||||
query: {
|
||||
typeName: 'IoTPlatformTypeEnum',
|
||||
},
|
||||
},
|
||||
labelField: 'value',
|
||||
valueField: 'key',
|
||||
optionsPropName: 'options',
|
||||
immediate: true,
|
||||
allowClear: true,
|
||||
placeholder:
|
||||
$t('common.pleaseSelect') + $t('abp.deviceInfos.ioTPlatform'),
|
||||
afterFetch: (res: any) => {
|
||||
if (Array.isArray(res)) {
|
||||
return res;
|
||||
}
|
||||
if (res && Array.isArray(res.items)) {
|
||||
return res.items;
|
||||
}
|
||||
if (res && Array.isArray(res.data)) {
|
||||
return res.data;
|
||||
}
|
||||
return [];
|
||||
},
|
||||
},
|
||||
rules: z.string().min(1, {
|
||||
message: `${$t('common.pleaseSelect')}${$t('abp.deviceInfos.ioTPlatform')}`,
|
||||
}),
|
||||
},
|
||||
{
|
||||
component: 'ApiSelect',
|
||||
fieldName: 'ioTPlatformProductId',
|
||||
label: $t('common.BelongingProductName'),
|
||||
dependencies: {
|
||||
show(values: any) {
|
||||
return !!values.ioTPlatform;
|
||||
},
|
||||
rules(values: any) {
|
||||
if (values.ioTPlatform) {
|
||||
return 'required';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
triggerFields: ['ioTPlatform'],
|
||||
},
|
||||
componentProps: (formValues: any) => {
|
||||
const platform = formValues?.ioTPlatform;
|
||||
|
||||
return {
|
||||
api: platform
|
||||
? postAggregationIoTplatformGetIoTplatformProductInfoAsync
|
||||
: null,
|
||||
params: platform
|
||||
? {
|
||||
body: {
|
||||
ioTPlatformType:
|
||||
typeof platform === 'string'
|
||||
? Number.parseInt(platform)
|
||||
: platform,
|
||||
},
|
||||
}
|
||||
: {},
|
||||
labelField: 'productName',
|
||||
valueField: 'ioTPlatformProductId',
|
||||
optionsPropName: 'options',
|
||||
immediate: false,
|
||||
allowClear: true,
|
||||
placeholder:
|
||||
$t('common.pleaseSelect') + $t('common.BelongingProductName'),
|
||||
afterFetch: (res: any) => {
|
||||
if (Array.isArray(res)) {
|
||||
return res;
|
||||
}
|
||||
if (res && Array.isArray(res.items)) {
|
||||
return res.items;
|
||||
}
|
||||
if (res && Array.isArray(res.data)) {
|
||||
return res.data;
|
||||
}
|
||||
if (res && res.data && Array.isArray(res.data.items)) {
|
||||
return res.data.items;
|
||||
}
|
||||
return [];
|
||||
},
|
||||
};
|
||||
},
|
||||
rules: z.string().optional(),
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'firmwareVersion',
|
||||
label: '固件版本',
|
||||
componentProps: {
|
||||
placeholder: '请输入固件版本',
|
||||
},
|
||||
rules: z.string().min(1, {
|
||||
message: '请输入固件版本',
|
||||
}),
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'firmwareFileName',
|
||||
label: '固件文件名称',
|
||||
componentProps: {
|
||||
placeholder: '请选择文件',
|
||||
readonly: true,
|
||||
addonAfter: h(
|
||||
'button',
|
||||
{
|
||||
type: 'button',
|
||||
style:
|
||||
'border: none; background: #1890ff; color: white; padding: 4px 8px; border-radius: 4px; cursor: pointer;',
|
||||
onClick: () => {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = '*/*';
|
||||
input.addEventListener('change', (e: any) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
const currentInput = document.querySelector(
|
||||
'input[placeholder="请选择文件"]',
|
||||
) as HTMLInputElement;
|
||||
if (currentInput) {
|
||||
currentInput.value = file.name;
|
||||
currentInput.dispatchEvent(
|
||||
new Event('input', { bubbles: true }),
|
||||
);
|
||||
currentInput.dispatchEvent(
|
||||
new Event('change', { bubbles: true }),
|
||||
);
|
||||
// 存储文件对象到全局变量
|
||||
(window as any).__selectedFirmwareFile = file;
|
||||
}
|
||||
}
|
||||
});
|
||||
input.click();
|
||||
},
|
||||
},
|
||||
'选择文件',
|
||||
),
|
||||
},
|
||||
rules: z.string().min(1, {
|
||||
message: '请选择固件文件',
|
||||
}),
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'firmwareFileId',
|
||||
label: '',
|
||||
componentProps: {
|
||||
type: 'hidden',
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'firmwareHashCode',
|
||||
label: '',
|
||||
componentProps: {
|
||||
type: 'hidden',
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'firmwareLength',
|
||||
label: '',
|
||||
componentProps: {
|
||||
type: 'hidden',
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'ioTPlatformProductName',
|
||||
label: '',
|
||||
componentProps: {
|
||||
type: 'hidden',
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'id',
|
||||
label: '',
|
||||
componentProps: {
|
||||
type: 'hidden',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
/*
|
||||
* @Description: 文件内容描述
|
||||
* @Author: 陈益
|
||||
* @Date: 2025-12-31 14:25:04
|
||||
* @LastEditors: 陈益
|
||||
*/
|
||||
Loading…
x
Reference in New Issue
Block a user