完善设备操作和设备端物模型管理

This commit is contained in:
ChenYi 2026-01-28 15:05:30 +08:00
parent 9197411c7b
commit 45055cdfef
9 changed files with 170 additions and 49 deletions

View File

@ -507,7 +507,7 @@ export const BindingDeviceThingModelInputSchema = {
description: '设备数据Id集合', description: '设备数据Id集合',
nullable: true nullable: true
}, },
isNeedConfigDevicMdoel: { isNeedConfigDeviceModel: {
type: 'boolean', type: 'boolean',
description: '是否需要配置设备模型默认false' description: '是否需要配置设备模型默认false'
}, },
@ -2413,7 +2413,7 @@ export const DeviceManagementInfoDtoSchema = {
nullable: true, nullable: true,
readOnly: true readOnly: true
}, },
isNeedConfigDevicMdoel: { isNeedConfigDeviceModel: {
type: 'boolean', type: 'boolean',
description: '是否需要配置设备模型默认false' description: '是否需要配置设备模型默认false'
}, },
@ -2443,6 +2443,15 @@ export const DeviceManagementInfoDtoSchema = {
type: 'integer', type: 'integer',
description: '子设备容量', description: '子设备容量',
format: 'int32' format: 'int32'
},
readingMode: {
'$ref': '#/components/schemas/DeviceReadingModeEnum'
},
readingModeName: {
type: 'string',
description: '抄读模式,JiShe.ServicePro.Core.DeviceReadingModeEnum',
nullable: true,
readOnly: true
} }
}, },
additionalProperties: false additionalProperties: false
@ -2494,6 +2503,14 @@ export const DevicePropertyValueForApiInputSchema = {
description: '设备属性抄读' description: '设备属性抄读'
} as const; } as const;
export const DeviceReadingModeEnumSchema = {
enum: [1, 2],
type: 'integer',
description: '设备抄读模式',
format: 'int32',
'说明:': '标准模式=1,特殊模式=2'
} as const;
export const DeviceSourceTypeEnumSchema = { export const DeviceSourceTypeEnumSchema = {
enum: [1, 2, 3, 4, 5, 6], enum: [1, 2, 3, 4, 5, 6],
type: 'integer', type: 'integer',
@ -2771,6 +2788,10 @@ export const DeviceThingModelCommandInfoDtoSchema = {
}, },
description: '指令设备端物模型的属性名称集合,JSON格式字符串数组一个指令的返回报文包含多个属性标识的数据', description: '指令设备端物模型的属性名称集合,JSON格式字符串数组一个指令的返回报文包含多个属性标识的数据',
nullable: true nullable: true
},
isEnable: {
type: 'boolean',
description: '是否启用'
} }
}, },
additionalProperties: false, additionalProperties: false,
@ -3797,7 +3818,7 @@ export const DeviceUpgradeResultTypeEnumSchema = {
type: 'integer', type: 'integer',
description: '设备升级结果枚举', description: '设备升级结果枚举',
format: 'int32', format: 'int32',
'说明:': 'Default=0,Success=1,InvalidUrlOrModuleUrlFetchFailed=1001,DownloadTimeout=2001,ParseHttpDlFileFailedOrDownloadIncomplete=2002,DownloadSizeMismatch=2003,ModuleReadTimeout=2004,FilePointerException=2005,ExternalFlashWriteFailed=3001,ExternalFlashCrc16Mismatch=3002,SignatureVerificationFailed=4001,Crc32Mismatch=5001,DiffDecompressOrPatchFailed=6001,RestoredFirmwareSizeExceededOrTargetAddressInBootArea=6002,IapPowerAbnormal=6003' '说明:': '默认值=0,升级成功=1,URL 为空/长度非法、模块侧下载前取 URL 失败直接退出下载线程=1001,HTTP 下载未在超时时间内完成会放弃升级并退出下载状态=2001,解析 +MHTTPDLFILE 失败或下载未完成会失败并重试,最终失败会退出=2002,下载返回的文件总长度与期望不一致会退出=2003,本地从模组读取文件超时=2004,文件读写指针异常=2005,HTTP 分块写入外部存储失败时返回=3001,读/写外部 Flash 做 CRC16 校验,不一致返回=3002,对生成的签名和云端下发的签名进行验证,如果不同则直接退出=4001,下载完成后计算 CRC32 与升级包中的 CRC32 不一致退出=5001,差分解压/补丁失败、字典超限、压缩类型不支持时=6001,还原后固件大小超限或目标地址落入 BOOT 区会终止 IAP=6002,IAP 读写外部 Flash 或串口收发时检测到供电异常会复位退出升级=6003'
} as const; } as const;
export const DeviceUpgradeSourceTypeEnumSchema = { export const DeviceUpgradeSourceTypeEnumSchema = {
@ -10883,11 +10904,11 @@ export const TenantDtoPagedResultDtoSchema = {
} as const; } as const;
export const ThingModelIdentifierTypeEnumSchema = { export const ThingModelIdentifierTypeEnumSchema = {
enum: [1, 2], enum: [1, 2, 3],
type: 'integer', type: 'integer',
description: '设备物模型标识符类型枚举', description: '设备物模型标识符类型枚举',
format: 'int32', format: 'int32',
'说明:': '数据标识符=1,属性标识符=2' '说明:': '唯一标识=1,数据标识符=2,属性标识符=3'
} as const; } as const;
export const TimeZoneSchema = { export const TimeZoneSchema = {

File diff suppressed because one or more lines are too long

View File

@ -199,7 +199,7 @@ export type BindingDeviceThingModelInput = {
/** /**
* false * false
*/ */
isNeedConfigDevicMdoel?: boolean; isNeedConfigDeviceModel?: boolean;
/** /**
* Id * Id
*/ */
@ -1341,7 +1341,7 @@ export type DeviceManagementInfoDto = {
/** /**
* false * false
*/ */
isNeedConfigDevicMdoel?: boolean; isNeedConfigDeviceModel?: boolean;
/** /**
* Id * Id
*/ */
@ -1362,6 +1362,11 @@ export type DeviceManagementInfoDto = {
* *
*/ */
subDeviceCapacity?: number; subDeviceCapacity?: number;
readingMode?: DeviceReadingModeEnum;
/**
* ,JiShe.ServicePro.Core.DeviceReadingModeEnum
*/
readonly readingModeName?: (string) | null;
}; };
export type DeviceManagementInfoDtoPagedResultDto = { export type DeviceManagementInfoDtoPagedResultDto = {
@ -1385,6 +1390,11 @@ export type DevicePropertyValueForApiInput = {
propertyList: Array<(string)>; propertyList: Array<(string)>;
}; };
/**
*
*/
export type DeviceReadingModeEnum = 1 | 2;
/** /**
* *
*/ */
@ -1566,6 +1576,10 @@ export type DeviceThingModelCommandInfoDto = {
* ,JSON格式字符串数组 * ,JSON格式字符串数组
*/ */
propertyArray?: Array<(string)> | null; propertyArray?: Array<(string)> | null;
/**
*
*/
isEnable?: boolean;
}; };
export type DeviceThingModelCommandInfoDtoPagedResultDto = { export type DeviceThingModelCommandInfoDtoPagedResultDto = {
@ -5964,7 +5978,7 @@ export type TenantDtoPagedResultDto = {
/** /**
* *
*/ */
export type ThingModelIdentifierTypeEnum = 1 | 2; export type ThingModelIdentifierTypeEnum = 1 | 2 | 3;
export type TimeZone = { export type TimeZone = {
iana?: IanaTimeZone; iana?: IanaTimeZone;
@ -6898,7 +6912,9 @@ export type PostDeviceThingModelManagementMessageAnalysisTestAsyncData = {
}; };
export type PostDeviceThingModelManagementMessageAnalysisTestAsyncResponse = ({ export type PostDeviceThingModelManagementMessageAnalysisTestAsyncResponse = ({
[key: string]: {
[key: string]: unknown; [key: string]: unknown;
};
}); });
export type PostDeviceThingModelManagementMessageAnalysisTestAsyncError = unknown; export type PostDeviceThingModelManagementMessageAnalysisTestAsyncError = unknown;
@ -7033,6 +7049,16 @@ export type PostDeviceThingModelManagementCommandFindByIdAsyncResponse = (Device
export type PostDeviceThingModelManagementCommandFindByIdAsyncError = unknown; export type PostDeviceThingModelManagementCommandFindByIdAsyncError = unknown;
export type PostDeviceThingModelManagementUpdateCommandStatusByIdAsyncData = {
query?: {
input?: IdInput;
};
};
export type PostDeviceThingModelManagementUpdateCommandStatusByIdAsyncResponse = (boolean);
export type PostDeviceThingModelManagementUpdateCommandStatusByIdAsyncError = unknown;
export type PostDeviceThingModelManagementCommandPageAsyncData = { export type PostDeviceThingModelManagementCommandPageAsyncData = {
query?: { query?: {
input?: DeviceThingModelCommandInfoPageInput; input?: DeviceThingModelCommandInfoPageInput;

View File

@ -218,7 +218,8 @@
"LastOfflineTime": "LastOfflineTime", "LastOfflineTime": "LastOfflineTime",
"deviceInfoManage": "DeviceInfoManage", "deviceInfoManage": "DeviceInfoManage",
"thingModelInfoManage": "ThingModelInfoManage", "thingModelInfoManage": "ThingModelInfoManage",
"isNeedConfigDevicMdoel": "IsNeedConfigDevicMdoel", "isNeedConfigDeviceModel": "IsNeedConfigDeviceModel",
"readingMode": "ReadingMode",
"deviceThingModelName": "ThingModelInfoManage" "deviceThingModelName": "ThingModelInfoManage"
}, },
"thingModelInfos": { "thingModelInfos": {

View File

@ -211,7 +211,8 @@
"LastOfflineTime": "最后离线时间", "LastOfflineTime": "最后离线时间",
"deviceInfoManage": "设备管理", "deviceInfoManage": "设备管理",
"thingModelInfoManage": "物模型管理", "thingModelInfoManage": "物模型管理",
"isNeedConfigDevicMdoel": "是否绑定设备模型", "isNeedConfigDeviceModel": "是否绑定设备模型",
"readingMode": "抄读模式",
"deviceThingModelName": "设备物模型名称" "deviceThingModelName": "设备物模型名称"
}, },
"thingModelInfos": { "thingModelInfos": {

View File

@ -794,8 +794,8 @@ const [BindForm, bindFormApi] = useVbenForm({
handleValuesChange: async (values, changedFields) => { handleValuesChange: async (values, changedFields) => {
// //
if ( if (
changedFields.includes('isNeedConfigDevicMdoel') && changedFields.includes('isNeedConfigDeviceModel') &&
values.isNeedConfigDevicMdoel values.isNeedConfigDeviceModel
) { ) {
console.log('开关打开,准备触发设备物模型下拉框重新加载'); console.log('开关打开,准备触发设备物模型下拉框重新加载');
await nextTick(); await nextTick();
@ -1491,19 +1491,19 @@ const openBindModal = async (
// //
// 使 // 使
let isNeedConfigDevicMdoel = false; let isNeedConfigDeviceModel = false;
let deviceThingModelDataId: string | undefined; let deviceThingModelDataId: string | undefined;
if (selectedRows.length === 1) { if (selectedRows.length === 1) {
const row = selectedRows[0] || {}; const row = selectedRows[0] || {};
isNeedConfigDevicMdoel = !!row.isNeedConfigDevicMdoel; isNeedConfigDeviceModel = !!row.isNeedConfigDeviceModel;
if (row.deviceThingModelDataId) { if (row.deviceThingModelDataId) {
deviceThingModelDataId = String(row.deviceThingModelDataId); deviceThingModelDataId = String(row.deviceThingModelDataId);
} }
} else { } else {
// //
const allNeedConfig = selectedRows.every( const allNeedConfig = selectedRows.every(
(row) => !!row.isNeedConfigDevicMdoel, (row) => !!row.isNeedConfigDeviceModel,
); );
const firstModelId = selectedRows[0]?.deviceThingModelDataId; const firstModelId = selectedRows[0]?.deviceThingModelDataId;
const allSameModel = const allSameModel =
@ -1511,12 +1511,12 @@ const openBindModal = async (
!!firstModelId && !!firstModelId &&
selectedRows.every((row) => row.deviceThingModelDataId === firstModelId); selectedRows.every((row) => row.deviceThingModelDataId === firstModelId);
isNeedConfigDevicMdoel = allNeedConfig; isNeedConfigDeviceModel = allNeedConfig;
deviceThingModelDataId = allSameModel ? String(firstModelId) : undefined; deviceThingModelDataId = allSameModel ? String(firstModelId) : undefined;
} }
await bindFormApi.setValues({ await bindFormApi.setValues({
isNeedConfigDevicMdoel, isNeedConfigDeviceModel,
deviceThingModelDataId, deviceThingModelDataId,
_ioTPlatformProductId: productId ? String(productId) : undefined, _ioTPlatformProductId: productId ? String(productId) : undefined,
}); });
@ -1635,9 +1635,23 @@ const readData = async (property: ThingModelProperty) => {
} else { } else {
property.result = '抄读数据失败'; property.result = '抄读数据失败';
} }
} catch (error) { } catch (error: any) {
console.error('抄读数据失败:', error); console.error('抄读数据失败:', error);
property.result = `抄读数据失败: ${(error as Error).message}`;
//
const backendMessage =
error?.response?.data?.error?.message ??
error?.data?.error?.message ??
error?.error?.message;
const finalMessage =
backendMessage && typeof backendMessage === 'string'
? backendMessage
: (error as Error).message || '抄读数据失败';
//
property.result = `抄读数据失败: ${finalMessage}`;
// Message.error
} finally { } finally {
property.loading = false; property.loading = false;
} }
@ -1727,7 +1741,7 @@ async function submitBindDeviceThingModel() {
const formValues = await bindFormApi.getValues(); const formValues = await bindFormApi.getValues();
// //
const isNeedConfig = !!formValues.isNeedConfigDevicMdoel; const isNeedConfig = !!formValues.isNeedConfigDeviceModel;
const deviceThingModelDataId = formValues.deviceThingModelDataId as const deviceThingModelDataId = formValues.deviceThingModelDataId as
| string | string
| undefined; | undefined;
@ -1755,7 +1769,7 @@ async function submitBindDeviceThingModel() {
const result = await postAggregationDeviceBindingDeviceThingModel({ const result = await postAggregationDeviceBindingDeviceThingModel({
body: { body: {
devieDataIdList: deviceIds, devieDataIdList: deviceIds,
isNeedConfigDevicMdoel: isNeedConfig, isNeedConfigDeviceModel: isNeedConfig,
deviceThingModelDataId: isNeedConfig ? deviceThingModelDataId : null, deviceThingModelDataId: isNeedConfig ? deviceThingModelDataId : null,
}, },
}); });
@ -2302,7 +2316,7 @@ const toolbarActions = computed(() => [
</div> </div>
<div class="ml-4 text-[11px] text-blue-700"> <div class="ml-4 text-[11px] text-blue-700">
当前是否需要配置设备模型 当前是否需要配置设备模型
{{ row.isNeedConfigDevicMdoel ? '是' : '否' }} {{ row.isNeedConfigDeviceModel ? '是' : '否' }}
<span class="mx-1">|</span> <span class="mx-1">|</span>
当前已绑定设备端物模型 当前已绑定设备端物模型
{{ row.deviceThingModelName || '未绑定' }} {{ row.deviceThingModelName || '未绑定' }}

View File

@ -168,8 +168,8 @@ export const tableSchema: any = computed((): VxeGridProps['columns'] => [
}, },
}, },
{ {
field: 'isNeedConfigDevicMdoel', field: 'isNeedConfigDeviceModel',
title: $t('abp.deviceInfos.isNeedConfigDevicMdoel'), title: $t('abp.deviceInfos.isNeedConfigDeviceModel'),
minWidth: '150', minWidth: '150',
formatter: ({ cellValue }) => { formatter: ({ cellValue }) => {
return cellValue ? '是' : '否'; return cellValue ? '是' : '否';
@ -180,6 +180,11 @@ export const tableSchema: any = computed((): VxeGridProps['columns'] => [
title: $t('abp.deviceInfos.deviceThingModelName'), title: $t('abp.deviceInfos.deviceThingModelName'),
minWidth: '150', minWidth: '150',
}, },
{
field: 'readingModeName',
title: $t('abp.deviceInfos.readingMode'),
minWidth: '150',
},
{ {
field: 'platformPassword', field: 'platformPassword',
title: $t('abp.deviceInfos.platformPassword'), title: $t('abp.deviceInfos.platformPassword'),
@ -393,7 +398,7 @@ export const bindDeviceThingModelFormSchema: any = computed(() => [
}, },
{ {
component: 'Switch', component: 'Switch',
fieldName: 'isNeedConfigDevicMdoel', fieldName: 'isNeedConfigDeviceModel',
label: '是否需要配置设备模型', label: '是否需要配置设备模型',
defaultValue: false, defaultValue: false,
componentProps: { componentProps: {
@ -407,15 +412,15 @@ export const bindDeviceThingModelFormSchema: any = computed(() => [
// 仅在需要配置设备模型时显示 // 仅在需要配置设备模型时显示
dependencies: { dependencies: {
show(values: any) { show(values: any) {
const shouldShow = !!values.isNeedConfigDevicMdoel; const shouldShow = !!values.isNeedConfigDeviceModel;
console.log('设备物模型下拉框显示检查:', { console.log('设备物模型下拉框显示检查:', {
isNeedConfigDevicMdoel: values.isNeedConfigDevicMdoel, isNeedConfigDeviceModel: values.isNeedConfigDeviceModel,
shouldShow, shouldShow,
_ioTPlatformProductId: values._ioTPlatformProductId, _ioTPlatformProductId: values._ioTPlatformProductId,
}); });
return shouldShow; return shouldShow;
}, },
triggerFields: ['isNeedConfigDevicMdoel', '_ioTPlatformProductId'], triggerFields: ['isNeedConfigDeviceModel', '_ioTPlatformProductId'],
}, },
// 选择设备端物模型 // 选择设备端物模型
componentProps: (formValues: any) => { componentProps: (formValues: any) => {
@ -425,7 +430,7 @@ export const bindDeviceThingModelFormSchema: any = computed(() => [
console.log('设备物模型下拉框 componentProps 被调用:', { console.log('设备物模型下拉框 componentProps 被调用:', {
formValues, formValues,
productId, productId,
isNeedConfigDevicMdoel: formValues?.isNeedConfigDevicMdoel, isNeedConfigDeviceModel: formValues?.isNeedConfigDeviceModel,
}); });
const config = { const config = {

View File

@ -230,13 +230,12 @@ const getUpgradeStatusColor = (status: string) => {
return statusMap[status] || 'default'; return statusMap[status] || 'default';
}; };
// // upgradeResult 1 绿
const getUpgradeResultColor = (result: string) => { const getUpgradeResultColor = (
if (!result) return 'default'; upgradeResult: number | string | undefined | null,
if (result.includes('成功') || result.includes('完成')) return 'success'; ) => {
if (result.includes('失败') || result.includes('错误')) return 'error'; if (upgradeResult == null || upgradeResult === '') return 'default';
if (result.includes('进行中') || result.includes('处理中')) return 'processing'; return upgradeResult === 1 ? 'success' : 'error';
return 'default';
}; };
</script> </script>
@ -264,7 +263,7 @@ const getUpgradeResultColor = (result: string) => {
:is=" :is="
h( h(
Tag, Tag,
{ color: getUpgradeResultColor(row.upgradeResultName || '') }, { color: getUpgradeResultColor(row.upgradeResult) },
() => row.upgradeResultName || '-', () => row.upgradeResultName || '-',
) )
" "

View File

@ -3,7 +3,7 @@ import { nextTick, ref } from 'vue';
import { useVbenModal, z } from '@vben/common-ui'; import { useVbenModal, z } from '@vben/common-ui';
import { message as Message } from 'ant-design-vue'; import { message as Message, Tag } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form'; import { useVbenForm } from '#/adapter/form';
import { useVbenVxeGrid } from '#/adapter/vxe-table'; import { useVbenVxeGrid } from '#/adapter/vxe-table';
@ -14,6 +14,7 @@ import {
postDeviceThingModelManagementCommandPageAsync, postDeviceThingModelManagementCommandPageAsync,
postDeviceThingModelManagementCommandUpdateAsync, postDeviceThingModelManagementCommandUpdateAsync,
postDeviceThingModelManagementPropertyPageAsync, postDeviceThingModelManagementPropertyPageAsync,
postDeviceThingModelManagementUpdateCommandStatusByIdAsync,
} from '#/api-client'; } from '#/api-client';
import { TableAction } from '#/components/table-action'; import { TableAction } from '#/components/table-action';
import { $t } from '#/locales'; import { $t } from '#/locales';
@ -70,10 +71,17 @@ const [Grid, gridApi] = useVbenVxeGrid({
return cellValue || '-'; return cellValue || '-';
}, },
}, },
{
field: 'isEnable',
title: '状态',
width: 90,
align: 'center',
slots: { default: 'isEnable' },
},
{ {
field: 'action', field: 'action',
title: $t('common.action'), title: $t('common.action'),
width: 200, width: 190,
fixed: 'right', fixed: 'right',
slots: { default: 'action' }, slots: { default: 'action' },
}, },
@ -324,6 +332,30 @@ async function onDeleteCommand(record: any) {
} }
} }
// JSON { id, isEnable }
async function onUpdateCommandStatus(record: any) {
const nextEnable = !record.isEnable;
const action = nextEnable ? '启用' : '禁用';
try {
const resp =
await postDeviceThingModelManagementUpdateCommandStatusByIdAsync({
body: { id: record.id, isEnable: nextEnable } as any,
});
if (resp.data) {
Message.success(`${action}成功`);
await nextTick();
if (gridApi && gridApi.reload) {
await gridApi.reload();
}
} else {
Message.error(`${action}失败`);
}
} catch (error) {
console.error(`${action}指令状态失败:`, error);
Message.error(`${action}失败`);
}
}
const [Modal, modalApi] = useVbenModal({ const [Modal, modalApi] = useVbenModal({
onOpenChange(isOpen: boolean) { onOpenChange(isOpen: boolean) {
if (isOpen) { if (isOpen) {
@ -356,8 +388,19 @@ const [Modal, modalApi] = useVbenModal({
]" /> ]" />
</template> </template>
<template #isEnable="{ row }">
<Tag v-if="row.isEnable" color="green">启用</Tag>
<Tag v-else color="red">禁用</Tag>
</template>
<template #action="{ row }"> <template #action="{ row }">
<TableAction :actions="[ <TableAction :actions="[
{
label: row.isEnable ? '禁用' : '启用',
type: 'link',
size: 'small',
style: row.isEnable ? { color: '#ff4d4f' } : { color: '#52c41a' },
onClick: onUpdateCommandStatus.bind(null, row),
},
{ {
label: $t('common.edit'), label: $t('common.edit'),
type: 'link', type: 'link',
@ -370,6 +413,7 @@ const [Modal, modalApi] = useVbenModal({
icon: 'ant-design:delete-outlined', icon: 'ant-design:delete-outlined',
type: 'link', type: 'link',
size: 'small', size: 'small',
style: { color: '#ff4d4f' },
auth: ['AbpIdentity.Users.Delete'], auth: ['AbpIdentity.Users.Delete'],
popConfirm: { popConfirm: {
title: $t('common.askConfirmDelete'), title: $t('common.askConfirmDelete'),