BatchUpdateStatusAsync

This commit is contained in:
ChenYi 2026-01-05 17:30:43 +08:00
parent 3a2301efce
commit ffc2d6a346
8 changed files with 248 additions and 72 deletions

View File

@ -2358,7 +2358,7 @@ export const DeviceManagementInfoDtoSchema = {
description: '物联网平台推送是否成功'
},
deviceOnlineStatus: {
'$ref': '#/components/schemas/DeviceOnlineStatusEnum'
'$ref': '#/components/schemas/DeviceOnlineStatus'
},
deviceOnlineStatusName: {
type: 'string',
@ -2449,7 +2449,7 @@ export const DeviceManagementInfoDtoPagedResultDtoSchema = {
additionalProperties: false
} as const;
export const DeviceOnlineStatusEnumSchema = {
export const DeviceOnlineStatusSchema = {
enum: [1, 2],
type: 'integer',
description: '设备在线状态',
@ -5847,7 +5847,7 @@ export const IoTPlatformProductInfoOutputSchema = {
description: '平台产品信息'
} as const;
export const IoTPlatformProductPropertyInfoInputSchema = {
export const IoTPlatformProductThingModelInfoInputSchema = {
required: ['ioTPlatformProductId', 'ioTPlatformType'],
type: 'object',
properties: {
@ -11000,7 +11000,7 @@ export const UpdateFeaturesDtoSchema = {
additionalProperties: false
} as const;
export const UpdateIoTPlatformProductPropertyInfoInputSchema = {
export const UpdateIoTPlatformProductThingModelInfoInputSchema = {
required: ['ioTPlatformProductId', 'ioTPlatformType'],
type: 'object',
properties: {

File diff suppressed because one or more lines are too long

View File

@ -1308,7 +1308,7 @@ export type DeviceManagementInfoDto = {
*
*/
isPlatformPushSuccess?: boolean;
deviceOnlineStatus?: DeviceOnlineStatusEnum;
deviceOnlineStatus?: DeviceOnlineStatus;
/**
* 线
*/
@ -1365,7 +1365,7 @@ export type DeviceManagementInfoDtoPagedResultDto = {
/**
* 线
*/
export type DeviceOnlineStatusEnum = 1 | 2;
export type DeviceOnlineStatus = 1 | 2;
/**
*
@ -2990,7 +2990,7 @@ export type IoTPlatformProductInfoOutput = {
/**
*
*/
export type IoTPlatformProductPropertyInfoInput = {
export type IoTPlatformProductThingModelInfoInput = {
ioTPlatformType: IoTPlatformTypeEnum;
/**
* JiShe.ServicePro.Core.DataDictionaryTypeConst
@ -5976,7 +5976,7 @@ export type UpdateFeaturesDto = {
/**
*
*/
export type UpdateIoTPlatformProductPropertyInfoInput = {
export type UpdateIoTPlatformProductThingModelInfoInput = {
ioTPlatformType: IoTPlatformTypeEnum;
/**
* Id
@ -6588,7 +6588,7 @@ export type PostAggregationDeviceDeviceBatchUpgradeForApiAsyncData = {
};
};
export type PostAggregationDeviceDeviceBatchUpgradeForApiAsyncResponse = (boolean);
export type PostAggregationDeviceDeviceBatchUpgradeForApiAsyncResponse = (Array<(string)>);
export type PostAggregationDeviceDeviceBatchUpgradeForApiAsyncError = unknown;
@ -7100,25 +7100,25 @@ export type PostAggregationIoTplatformGetIoTplatformAccountInfoAsyncResponse = (
export type PostAggregationIoTplatformGetIoTplatformAccountInfoAsyncError = unknown;
export type PostAggregationIoTplatformGetIoTplatformProductPropertyInfoAsyncData = {
export type PostAggregationIoTplatformGetIoTplatformProductThingModelInfoAsyncData = {
query?: {
input?: IoTPlatformProductPropertyInfoInput;
input?: IoTPlatformProductThingModelInfoInput;
};
};
export type PostAggregationIoTplatformGetIoTplatformProductPropertyInfoAsyncResponse = (Array<SelectResult>);
export type PostAggregationIoTplatformGetIoTplatformProductThingModelInfoAsyncResponse = (Array<SelectResult>);
export type PostAggregationIoTplatformGetIoTplatformProductPropertyInfoAsyncError = unknown;
export type PostAggregationIoTplatformGetIoTplatformProductThingModelInfoAsyncError = unknown;
export type PostAggregationIoTplatformUpdateIoTplatformProductPropertyInfoAsyncData = {
export type PostAggregationIoTplatformUpdateIoTplatformProductThingModelInfoAsyncData = {
query?: {
input?: UpdateIoTPlatformProductPropertyInfoInput;
input?: UpdateIoTPlatformProductThingModelInfoInput;
};
};
export type PostAggregationIoTplatformUpdateIoTplatformProductPropertyInfoAsyncResponse = (unknown);
export type PostAggregationIoTplatformUpdateIoTplatformProductThingModelInfoAsyncResponse = (unknown);
export type PostAggregationIoTplatformUpdateIoTplatformProductPropertyInfoAsyncError = unknown;
export type PostAggregationIoTplatformUpdateIoTplatformProductThingModelInfoAsyncError = unknown;
export type PostIoTplatformThingModelInfoCreateAsyncData = {
query?: {

View File

@ -2,16 +2,17 @@
import type { VbenFormProps } from '#/adapter/form';
import type { VxeGridProps } from '#/adapter/vxe-table';
import { computed, h } from 'vue';
import { computed, h, ref } from 'vue';
import { Page } from '@vben/common-ui';
import { Button, message as Message, Modal, Tag } from 'ant-design-vue';
import { Button, Select, message as Message, Modal, Tag } from 'ant-design-vue';
import dayjs from 'dayjs';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import {
postUpgradeRecordBatchUpdateStatusAsync,
postUpgradeRecordDeleteAsync,
postUpgradeRecordPage,
} from '#/api-client';
@ -40,7 +41,19 @@ const formOptions: VbenFormProps = {
},
};
// DeviceUpgradeStatusTypeEnum
const upgradeStatusOptions = [
{ label: '未升级', value: 1 },
{ label: '升级中', value: 2 },
{ label: '固件下载中', value: 3 },
{ label: '升级成功', value: 4 },
{ label: '升级失败', value: 5 },
];
const gridOptions: VxeGridProps<any> = {
checkboxConfig: {
highlight: true,
},
columns: tableSchema.value,
height: 'auto',
keepSource: true,
@ -61,8 +74,14 @@ const gridOptions: VxeGridProps<any> = {
const finalFormValues = { ...formValues, ...currentFormValues };
// upgradeIdentifier
if (finalFormValues.upgradeIdentifier !== undefined && finalFormValues.upgradeIdentifier !== null && finalFormValues.upgradeIdentifier !== '') {
finalFormValues.upgradeIdentifier = Number(finalFormValues.upgradeIdentifier);
if (
finalFormValues.upgradeIdentifier !== undefined &&
finalFormValues.upgradeIdentifier !== null &&
finalFormValues.upgradeIdentifier !== ''
) {
finalFormValues.upgradeIdentifier = Number(
finalFormValues.upgradeIdentifier,
);
} else {
// undefined
finalFormValues.upgradeIdentifier = undefined;
@ -83,11 +102,104 @@ const gridOptions: VxeGridProps<any> = {
const [Grid, gridApi] = useVbenVxeGrid({ formOptions, gridOptions });
//
const getSelectedRecords = () => {
if (!gridApi?.grid) return [];
const gridInstance = gridApi.grid as any;
if (typeof gridInstance.getCheckboxRecords === 'function') {
return gridInstance.getCheckboxRecords();
}
if (Array.isArray(gridInstance.checkboxRecords)) {
return gridInstance.checkboxRecords;
}
return [];
};
//
const handleBatchUpdateStatus = () => {
const records = getSelectedRecords();
if (!records || records.length === 0) {
Message.warning('请先勾选要更新状态的升级记录');
return;
}
const ids = records
.map((r: any) => r.id)
.filter((id: string | undefined) => !!id);
if (!ids.length) {
Message.error('选中的升级记录缺少主键 Id无法批量更新');
return;
}
const selectedStatus = ref<number | null>(null);
Modal.confirm({
title: '批量更新升级状态',
content: h('div', { style: 'margin-top: 8px;' }, [
h(
'div',
{
style: 'margin-bottom: 8px;',
},
'请选择要更新为的升级状态:',
),
h(Select, {
style: 'width: 100%',
placeholder: '请选择升级状态',
options: upgradeStatusOptions,
onChange: (value: number) => {
selectedStatus.value = value;
},
}),
]),
okText: $t('common.confirm'),
cancelText: $t('common.cancel'),
onOk: async () => {
if (!selectedStatus.value) {
Message.warning('请选择升级状态');
//
return Promise.reject();
}
try {
// JSON Body
await postUpgradeRecordBatchUpdateStatusAsync({
body: {
ids,
upgradeStatus: selectedStatus.value,
},
});
Message.success('批量更新升级状态成功');
gridApi.reload();
} catch (error) {
console.error('批量更新升级状态失败:', error);
Message.error('批量更新升级状态失败');
}
},
});
};
//
const toolbarActions = computed(() => [
{
label: '批量状态更新',
type: 'default',
onClick: handleBatchUpdateStatus,
},
]);
//
function onDel(row: any) {
Modal.confirm({
title: `${$t('common.confirmDelete')}升级记录 ?`,
content: `设备:${row.deviceName || row.deviceAddress || '-'},升级日期:${row.upgradeDate ? dayjs(row.upgradeDate).format('YYYY-MM-DD HH:mm:ss') : '-'}`,
content: `设备:${
row.deviceName || row.deviceAddress || '-'
}升级日期${
row.upgradeDate
? dayjs(row.upgradeDate).format('YYYY-MM-DD HH:mm:ss')
: '-'
}`,
onOk: async () => {
try {
const result = await postUpgradeRecordDeleteAsync({
@ -110,10 +222,10 @@ function onDel(row: any) {
//
const getUpgradeStatusColor = (status: string) => {
const statusMap: Record<string, string> = {
'进行中': 'processing',
'成功': 'success',
'失败': 'error',
'已取消': 'default',
进行中: 'processing',
成功: 'success',
失败: 'error',
已取消: 'default',
};
return statusMap[status] || 'default';
};
@ -131,6 +243,10 @@ const getUpgradeResultColor = (result: string) => {
<template>
<Page auto-content-height>
<Grid>
<template #toolbar-actions>
<TableAction :actions="toolbarActions" />
</template>
<template #upgradeStatus="{ row }">
<component
:is="

View File

@ -111,6 +111,8 @@ export const querySchema = computed(() => [
export const tableSchema: any = computed((): VxeGridProps['columns'] => [
{ title: $t('common.seq'), type: 'seq', width: 50 },
// 勾选列,用于批量操作
{ title: '', type: 'checkbox', width: 50 },
{
field: 'deviceName',
title: '设备名称',

View File

@ -12,7 +12,7 @@ import { message as Message, Tag } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import {
postAggregationIoTplatformUpdateIoTplatformProductPropertyInfoAsync,
postAggregationIoTplatformUpdateIoTplatformProductThingModelInfoAsync,
postFilesDownload,
postFilesUpload,
postOneNetProductDeleteAsync,
@ -76,6 +76,8 @@ const [Grid, gridApi] = useVbenVxeGrid({ formOptions, gridOptions });
const editRow: Record<string, any> = ref({});
//
let selectedFile: File | null = null;
// loading 使 loading
const thingModelUpdateLoading = ref<Record<string, boolean>>({});
//
setFileSelectedCallback((file) => {
@ -336,15 +338,58 @@ function onThingModelManagement(record: any) {
});
}
//
function getRowActions(row: any) {
const productId = row?.ioTPlatformProductId;
return [
{
label: $t('common.edit'),
type: 'link',
size: 'small',
auth: ['AbpIdentity.Users.Update'],
onClick: onEdit.bind(null, row),
},
{
label: $t('abp.deviceInfos.deviceInfoManage'),
type: 'link',
size: 'small',
onClick: onDeviceManagement.bind(null, row),
},
{
label: $t('abp.deviceInfos.thingModelInfoManage'),
type: 'link',
size: 'small',
onClick: onThingModelManagement.bind(null, row),
},
{
label: 'OneNET物模型更新',
type: 'link',
size: 'small',
loading: productId ? (thingModelUpdateLoading.value[productId] || false) : false,
onClick: onThingModelUpdate.bind(null, row),
},
];
}
//
async function onThingModelUpdate(record: any) {
const productId = record?.ioTPlatformProductId;
if (!productId) {
Message.error('产品ID不存在无法更新物模型');
return;
}
// loading
thingModelUpdateLoading.value = {
...thingModelUpdateLoading.value,
[productId]: true,
};
try {
const resp =
await postAggregationIoTplatformUpdateIoTplatformProductPropertyInfoAsync(
await postAggregationIoTplatformUpdateIoTplatformProductThingModelInfoAsync(
{
body: {
ioTPlatformType: 2, // OneNET 2
ioTPlatformProductId: record.ioTPlatformProductId,
ioTPlatformProductId: productId,
},
},
);
@ -357,6 +402,12 @@ async function onThingModelUpdate(record: any) {
} catch (error) {
console.error('物模型更新失败:', error);
Message.error('物模型更新失败');
} finally {
// loading
thingModelUpdateLoading.value = {
...thingModelUpdateLoading.value,
[productId]: false,
};
}
}
</script>
@ -392,33 +443,7 @@ async function onThingModelUpdate(record: any) {
</template>
<template #action="{ row }">
<TableAction :actions="[
{
label: $t('common.edit'),
type: 'link',
size: 'small',
auth: ['AbpIdentity.Users.Update'],
onClick: onEdit.bind(null, row),
},
{
label: $t('abp.deviceInfos.deviceInfoManage'),
type: 'link',
size: 'small',
onClick: onDeviceManagement.bind(null, row),
},
{
label: $t('abp.deviceInfos.thingModelInfoManage'),
type: 'link',
size: 'small',
onClick: onThingModelManagement.bind(null, row),
},
{
label: 'OneNET物模型更新',
type: 'link',
size: 'small',
onClick: onThingModelUpdate.bind(null, row),
},
]" :drop-down-actions="[
<TableAction :actions="getRowActions(row)" :drop-down-actions="[
{
label: row.isEnabled
? $t('common.disabled')

View File

@ -193,6 +193,7 @@ const [ThingModelModal, thingModelModalApi] = useVbenModal({
//
let platformValue: 1 | 2;
let productIdValue: string;
let filedTypeValue: string | undefined;
if (isEdit) {
// editRow.value
@ -202,6 +203,7 @@ const [ThingModelModal, thingModelModalApi] = useVbenModal({
: editRow.value.ioTPlatform) as 1 | 2)
: (Number.parseInt(ioTPlatform.value) as 1 | 2);
productIdValue = editRow.value.ioTPlatformProductId || productId.value;
filedTypeValue = editRow.value.filedType;
} else {
//
const formValues = gridApi?.formApi
@ -227,6 +229,7 @@ const [ThingModelModal, thingModelModalApi] = useVbenModal({
isEdit,
platformValue,
productIdValue,
filedTypeValue,
editRowValue: isEdit ? editRow.value : null,
});
@ -237,7 +240,12 @@ const [ThingModelModal, thingModelModalApi] = useVbenModal({
});
//
setTimeout(() => {
setTimeout(async () => {
// filedType
if (!isEdit) {
const currentFormValues = await formApi.getValues();
filedTypeValue = currentFormValues.filedType || filedTypeValue;
}
const fieldRef = formApi.getFieldComponentRef(
'ioTPlatformRawFieldName',
);
@ -246,6 +254,7 @@ const [ThingModelModal, thingModelModalApi] = useVbenModal({
body: {
ioTPlatformType: platformValue,
ioTPlatformProductId: productIdValue,
...(filedTypeValue ? { filedType: String(filedTypeValue) } : {}),
},
});
}
@ -507,6 +516,7 @@ async function onEdit(record: any) {
: record.ioTPlatform) as 1 | 2)
: (Number.parseInt(ioTPlatform.value) as 1 | 2);
const productIdValue = record.ioTPlatformProductId || productId.value;
const filedTypeValue = record.filedType;
thingModelModalApi.open();
await nextTick();
@ -526,6 +536,7 @@ async function onEdit(record: any) {
body: {
ioTPlatformType: platformValue,
ioTPlatformProductId: productIdValue,
...(filedTypeValue ? { filedType: String(filedTypeValue) } : {}),
},
});
}
@ -561,12 +572,16 @@ const openAddModal = async () => {
});
//
// filedType
const currentFormValues = await addFormApi.getValues();
const filedTypeValue = currentFormValues.filedType;
const fieldRef = addFormApi.getFieldComponentRef('ioTPlatformRawFieldName');
if (fieldRef && typeof fieldRef.updateParam === 'function') {
fieldRef.updateParam({
body: {
ioTPlatformType: platformValue,
ioTPlatformProductId: productIdValue,
...(filedTypeValue ? { filedType: String(filedTypeValue) } : {}),
},
});
}

View File

@ -7,7 +7,7 @@ import dayjs from 'dayjs';
import {
getCommonGetSelectList,
postAggregationIoTplatformGetIoTplatformProductInfoAsync,
postAggregationIoTplatformGetIoTplatformProductPropertyInfoAsync,
postAggregationIoTplatformGetIoTplatformProductThingModelInfoAsync,
postOneNetProductListAsync,
} from '#/api-client';
import { $t } from '#/locales';
@ -308,6 +308,12 @@ export const getAddThingModelFormSchema = (
),
dependencies: {
triggerFields: ['_ioTPlatform', '_ioTPlatformProductId', 'filedType'],
trigger: async (_formValues: any, formApi: any) => {
// 当 filedType 变化时,清空字段值,强制组件重新加载
if (formApi && typeof formApi.setFieldValue === 'function') {
await formApi.setFieldValue('ioTPlatformRawFieldName', undefined);
}
},
componentProps: (formValues: any) => {
// 优先从表单值获取(这是最可靠的方式,因为值是在打开弹窗时设置的)
let platform = formValues?._ioTPlatform;
@ -365,7 +371,7 @@ export const getAddThingModelFormSchema = (
return {
api:
platform && productId
? postAggregationIoTplatformGetIoTplatformProductPropertyInfoAsync
? postAggregationIoTplatformGetIoTplatformProductThingModelInfoAsync
: null,
params:
platform && productId
@ -376,10 +382,13 @@ export const getAddThingModelFormSchema = (
? Number.parseInt(platform)
: platform,
ioTPlatformProductId: String(productId),
...(filedType ? { filedType: String(filedType) } : {}),
// 确保 FiledType 参数始终存在(即使为空),以便触发接口重新请求
FiledType: filedType ? String(filedType) : undefined,
},
}
: {},
// 添加 key 确保 filedType 变化时组件重新渲染
key: `platform-${platform}-product-${productId}-type-${filedType || 'none'}`,
labelField: 'value',
valueField: 'key',
optionsPropName: 'options',
@ -620,6 +629,12 @@ export const getEditThingModelFormSchema = (
),
dependencies: {
triggerFields: ['_ioTPlatform', '_ioTPlatformProductId', 'filedType'],
trigger: async (_formValues: any, formApi: any) => {
// 当 filedType 变化时,清空字段值,强制组件重新加载
if (formApi && typeof formApi.setFieldValue === 'function') {
await formApi.setFieldValue('ioTPlatformRawFieldName', undefined);
}
},
componentProps: (formValues: any) => {
// 优先从表单值获取(这是最可靠的方式,因为值是在打开弹窗时设置的)
let platform = formValues?._ioTPlatform;
@ -677,7 +692,7 @@ export const getEditThingModelFormSchema = (
return {
api:
platform && productId
? postAggregationIoTplatformGetIoTplatformProductPropertyInfoAsync
? postAggregationIoTplatformGetIoTplatformProductThingModelInfoAsync
: null,
params:
platform && productId
@ -688,10 +703,13 @@ export const getEditThingModelFormSchema = (
? Number.parseInt(platform)
: platform,
ioTPlatformProductId: String(productId),
...(filedType ? { filedType: String(filedType) } : {}),
// 确保 FiledType 参数始终存在(即使为空),以便触发接口重新请求
FiledType: filedType ? String(filedType) : undefined,
},
}
: {},
// 添加 key 确保 filedType 变化时组件重新渲染
key: `platform-${platform}-product-${productId}-type-${filedType || 'none'}`,
labelField: 'value',
valueField: 'key',
optionsPropName: 'options',