设备固件管理

This commit is contained in:
ChenYi 2025-12-31 15:08:21 +08:00
parent d672ea04aa
commit c09ca5eab0
7 changed files with 1120 additions and 3 deletions

View File

@ -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

View File

@ -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;

View File

@ -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;
// md5Hashm 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>

View File

@ -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',
},
},
]);

View File

@ -0,0 +1,6 @@
/*
* @Description:
* @Author:
* @Date: 2025-12-31 14:25:04
* @LastEditors:
*/