From 2ff8a447cf8b56f1ced7061ba27c3d7f07b12c2b Mon Sep 17 00:00:00 2001 From: ChenYi <296215406@outlook.com> Date: Thu, 25 Dec 2025 11:26:14 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E8=AE=BE=E5=A4=87=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web-antd/src/api-client/schemas.gen.ts | 144 +++- apps/web-antd/src/api-client/types.gen.ts | 144 +++- .../devicemanagement/deviceinfo/index.vue | 714 +++++++++++------- .../devicemanagement/deviceinfo/schema.ts | 92 +-- 4 files changed, 735 insertions(+), 359 deletions(-) diff --git a/apps/web-antd/src/api-client/schemas.gen.ts b/apps/web-antd/src/api-client/schemas.gen.ts index f2c12dd..d9856d3 100644 --- a/apps/web-antd/src/api-client/schemas.gen.ts +++ b/apps/web-antd/src/api-client/schemas.gen.ts @@ -750,6 +750,10 @@ export const CTWingAccountListInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, phoneNumber: { type: 'string', description: '手机号码', @@ -1091,6 +1095,10 @@ export const CTWingPrivateProductInfoListInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, ctWingAccountId: { type: 'string', description: 'CTWing账户Id', @@ -2238,6 +2246,10 @@ export const DeviceTableModelDataInfoPageInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, ioTDataType: { type: 'string', description: `数据类型,用于构建存储路径 @@ -2528,6 +2540,10 @@ export const DeviceThingModelCommandInfoPageInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, deviceThingModelId: { type: 'string', description: '设备端物模型Id', @@ -2550,10 +2566,6 @@ export const DeviceThingModelCommandInfoPageInputSchema = { type: 'string', description: '物联网平台中对应产品物模型属性或者事件类型 JiShe.ServicePro.Core.DataDictionaryTypeConst', nullable: true - }, - isPage: { - type: 'boolean', - description: '是否分页' } }, additionalProperties: false, @@ -2793,6 +2805,10 @@ export const DeviceThingModelPageInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, ioTPlatform: { '$ref': '#/components/schemas/IoTPlatformTypeEnum' }, @@ -2810,10 +2826,6 @@ export const DeviceThingModelPageInputSchema = { type: 'string', description: '物联网平台中对应产品物模型属性或者事件类型 JiShe.ServicePro.Core.DataDictionaryTypeConst', nullable: true - }, - isPage: { - type: 'boolean', - description: '是否分页' } }, additionalProperties: false @@ -3070,6 +3082,10 @@ export const DeviceThingModelPropertyPageInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, deviceThingModelId: { type: 'string', description: '设备端物模型Id', @@ -3092,10 +3108,6 @@ export const DeviceThingModelPropertyPageInputSchema = { type: 'string', description: '物联网平台中对应产品物模型属性或者事件类型 JiShe.ServicePro.Core.DataDictionaryTypeConst', nullable: true - }, - isPage: { - type: 'boolean', - description: '是否分页' } }, additionalProperties: false, @@ -3236,6 +3248,10 @@ export const DeviceTreeModelDataInfoInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, ioTDataType: { type: 'string', description: `数据类型,用于构建存储路径 @@ -4431,6 +4447,10 @@ export const GetOrganizationUnitRoleInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, organizationUnitId: { type: 'string', format: 'uuid' @@ -4499,6 +4519,10 @@ export const GetOrganizationUnitUserInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, organizationUnitId: { type: 'string', format: 'uuid' @@ -4608,6 +4632,10 @@ export const GetUnAddRoleInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, organizationUnitId: { type: 'string', format: 'uuid' @@ -4680,6 +4708,10 @@ export const GetUnAddUserInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, organizationUnitId: { type: 'string', format: 'uuid' @@ -5576,6 +5608,10 @@ export const IoTPlatformThingModelPageInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, ioTPlatform: { '$ref': '#/components/schemas/IoTPlatformTypeEnum' }, @@ -5899,6 +5935,10 @@ export const MeterReadingPacketInfoPageInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, ioTDataType: { type: 'string', description: `数据类型,用于构建存储路径 @@ -6704,6 +6744,10 @@ export const OneNetAccountListInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, phoneNumber: { type: 'string', description: '手机号码', @@ -6817,6 +6861,10 @@ export const OneNetProductInfoListInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, oneNETAccountId: { type: 'string', description: 'OneNET账户Id', @@ -6948,6 +6996,10 @@ export const PageDeviceInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, deviceAddress: { type: 'string', description: '设备地址', @@ -7002,6 +7054,10 @@ export const PageFileObjectInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, startCreationTime: { type: 'string', description: '开始创建时间', @@ -7237,6 +7293,10 @@ export const PageLanguageInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, filter: { type: 'string', nullable: true @@ -7337,6 +7397,10 @@ export const PageLanguageTextInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, cultureName: { type: 'string', description: '语言', @@ -7425,6 +7489,10 @@ export const PageMenuInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, startCreationTime: { type: 'string', description: '开始创建时间', @@ -7650,6 +7718,10 @@ export const PageTextTemplateInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, name: { type: 'string', description: '名称', @@ -7810,6 +7882,10 @@ export const PagingAuditLogInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, startTime: { type: 'string', description: '开始时间', @@ -8029,6 +8105,10 @@ export const PagingDataDictionaryDetailInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, dataDictionaryId: { type: 'string', format: 'uuid' @@ -8131,6 +8211,10 @@ export const PagingDataDictionaryInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, filter: { type: 'string', nullable: true @@ -8294,6 +8378,10 @@ export const PagingIdentitySecurityLogInputSchema = { format: 'int32', readOnly: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, sorting: { type: 'string', description: '排序', @@ -8456,6 +8544,10 @@ export const PagingNotificationInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, title: { type: 'string', description: '标题', @@ -8640,6 +8732,10 @@ export const PagingNotificationSubscriptionInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, notificationId: { type: 'string', format: 'uuid' @@ -8798,6 +8894,10 @@ export const PagingRoleListInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, filter: { type: 'string', nullable: true @@ -8833,6 +8933,10 @@ export const PagingTenantInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, filter: { type: 'string', nullable: true @@ -8868,6 +8972,10 @@ export const PagingUserListInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, filter: { type: 'string', description: '关键字', @@ -9082,6 +9190,10 @@ export const QueryCTWingAepReceiveMessageInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, ioTDataType: { type: 'string', description: `数据类型,用于构建存储路径 @@ -9295,6 +9407,10 @@ export const QueryOneNETReceiveMessageInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, ioTDataType: { type: 'string', description: `数据类型,用于构建存储路径 @@ -9696,6 +9812,10 @@ export const SelectDataDictionaryDetailInputSchema = { `, nullable: true }, + isPage: { + type: 'boolean', + description: '是否分页' + }, dataDictionaryId: { type: 'string', format: 'uuid' diff --git a/apps/web-antd/src/api-client/types.gen.ts b/apps/web-antd/src/api-client/types.gen.ts index 31932fd..fec75c2 100644 --- a/apps/web-antd/src/api-client/types.gen.ts +++ b/apps/web-antd/src/api-client/types.gen.ts @@ -622,6 +622,10 @@ export type CTWingAccountListInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; /** * 手机号码 */ @@ -848,6 +852,10 @@ export type CTWingPrivateProductInfoListInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; /** * CTWing账户Id */ @@ -1238,6 +1246,10 @@ export type DeviceTableModelDataInfoPageInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; /** * 数据类型,用于构建存储路径 * JiShe.ServicePro.Consts.IoTDBDataTypeConst @@ -1417,6 +1429,10 @@ export type DeviceThingModelCommandInfoPageInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; /** * 设备端物模型Id */ @@ -1434,10 +1450,6 @@ export type DeviceThingModelCommandInfoPageInput = { * 物联网平台中对应产品物模型属性或者事件类型 JiShe.ServicePro.Core.DataDictionaryTypeConst */ filedType?: (string) | null; - /** - * 是否分页 - */ - isPage?: boolean; }; /** @@ -1577,6 +1589,10 @@ export type DeviceThingModelPageInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; ioTPlatform?: IoTPlatformTypeEnum; /** * 搜索关键字 @@ -1590,10 +1606,6 @@ export type DeviceThingModelPageInput = { * 物联网平台中对应产品物模型属性或者事件类型 JiShe.ServicePro.Core.DataDictionaryTypeConst */ filedType?: (string) | null; - /** - * 是否分页 - */ - isPage?: boolean; }; /** @@ -1757,6 +1769,10 @@ export type DeviceThingModelPropertyPageInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; /** * 设备端物模型Id */ @@ -1774,10 +1790,6 @@ export type DeviceThingModelPropertyPageInput = { * 物联网平台中对应产品物模型属性或者事件类型 JiShe.ServicePro.Core.DataDictionaryTypeConst */ filedType?: (string) | null; - /** - * 是否分页 - */ - isPage?: boolean; }; /** @@ -1876,6 +1888,10 @@ export type DeviceTreeModelDataInfoInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; /** * 数据类型,用于构建存储路径 * JiShe.ServicePro.Consts.IoTDBDataTypeConst @@ -2321,6 +2337,10 @@ export type GetOrganizationUnitRoleInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; organizationUnitId?: string; }; @@ -2354,6 +2374,10 @@ export type GetOrganizationUnitUserInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; organizationUnitId?: string; filter?: (string) | null; }; @@ -2405,6 +2429,10 @@ export type GetUnAddRoleInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; organizationUnitId?: string; filter?: (string) | null; }; @@ -2439,6 +2467,10 @@ export type GetUnAddUserInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; organizationUnitId?: string; filter?: (string) | null; }; @@ -2828,6 +2860,10 @@ export type IoTPlatformThingModelPageInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; ioTPlatform?: IoTPlatformTypeEnum; /** * 搜索关键字 @@ -3040,6 +3076,10 @@ export type MeterReadingPacketInfoPageInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; /** * 数据类型,用于构建存储路径 * JiShe.ServicePro.Consts.IoTDBDataTypeConst @@ -3369,6 +3409,10 @@ export type OneNetAccountListInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; /** * 手机号码 */ @@ -3589,6 +3633,10 @@ export type OneNetProductInfoListInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; /** * OneNET账户Id */ @@ -3688,6 +3736,10 @@ export type PageDeviceInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; /** * 设备地址 */ @@ -3730,6 +3782,10 @@ export type PageFileObjectInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; /** * 开始创建时间 */ @@ -3837,6 +3893,10 @@ export type PageLanguageInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; filter?: (string) | null; }; @@ -3906,6 +3966,10 @@ export type PageLanguageTextInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; /** * 语言 */ @@ -3966,6 +4030,10 @@ export type PageMenuInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; /** * 开始创建时间 */ @@ -4110,6 +4178,10 @@ export type PageTextTemplateInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; /** * 名称 */ @@ -4201,6 +4273,10 @@ export type PagingAuditLogInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; /** * 开始时间 */ @@ -4301,6 +4377,10 @@ export type PagingDataDictionaryDetailInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; dataDictionaryId?: string; filter?: (string) | null; }; @@ -4362,6 +4442,10 @@ export type PagingDataDictionaryInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; filter?: (string) | null; }; @@ -4425,6 +4509,10 @@ export type PagingIdentitySecurityLogInput = { * 跳过多少条 */ readonly skipCount?: number; + /** + * 是否分页 + */ + isPage?: boolean; /** * 排序 */ @@ -4505,6 +4593,10 @@ export type PagingNotificationInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; /** * 标题 */ @@ -4617,6 +4709,10 @@ export type PagingNotificationSubscriptionInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; notificationId?: string; /** * 接受者Id @@ -4713,6 +4809,10 @@ export type PagingRoleListInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; filter?: (string) | null; }; @@ -4736,6 +4836,10 @@ export type PagingTenantInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; filter?: (string) | null; }; @@ -4759,6 +4863,10 @@ export type PagingUserListInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; /** * 关键字 */ @@ -4846,6 +4954,10 @@ export type QueryCTWingAepReceiveMessageInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; /** * 数据类型,用于构建存储路径 * JiShe.ServicePro.Consts.IoTDBDataTypeConst @@ -5002,6 +5114,10 @@ export type QueryOneNETReceiveMessageInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; /** * 数据类型,用于构建存储路径 * JiShe.ServicePro.Consts.IoTDBDataTypeConst @@ -5213,6 +5329,10 @@ export type SelectDataDictionaryDetailInput = { * */ sorting?: (string) | null; + /** + * 是否分页 + */ + isPage?: boolean; dataDictionaryId?: string; filter?: (string) | null; typeCode?: (string) | null; diff --git a/apps/web-antd/src/views/devicemanagement/deviceinfo/index.vue b/apps/web-antd/src/views/devicemanagement/deviceinfo/index.vue index a4d0a77..00b6696 100644 --- a/apps/web-antd/src/views/devicemanagement/deviceinfo/index.vue +++ b/apps/web-antd/src/views/devicemanagement/deviceinfo/index.vue @@ -3,16 +3,18 @@ import type { VbenFormProps } from '#/adapter/form'; import type { VxeGridProps } from '#/adapter/vxe-table'; import { computed, h, nextTick, ref, watch } from 'vue'; -import { useRouter, useRoute } from 'vue-router'; +import { useRoute, useRouter } from 'vue-router'; import { Page, useVbenModal } from '@vben/common-ui'; import { Button, + Checkbox, Input, message as Message, Modal, Popover, + Table, Tag, } from 'ant-design-vue'; @@ -23,23 +25,24 @@ import { postAggregationDeviceCreateAsync, postAggregationDeviceDeleteAsync, postAggregationDeviceDeviceCommandForApiAsync, + postAggregationDeviceGetDevicePropertyValueForApiAsync, postAggregationDeviceRepushDeviceInfoToIoTplatform, - postDeviceInfoCacheDeviceDataToRedis, postDeviceInfoBindingDeviceThingModel, + postDeviceInfoCacheDeviceDataToRedis, postDeviceInfoPage, + postIoTplatformThingModelInfoPageAsync, } from '#/api-client'; import { Icon } from '#/components/icon'; import { Loading } from '#/components/Loading'; import { TableAction } from '#/components/table-action'; import { $t } from '#/locales'; -import IoTPlatformThingModelDataSelect from '#/views/thingmodelinfo/IoTPlatformThingModelDataSelect.vue'; import { addDeviceFormSchema, batchAddDeviceFormSchema, + bindDeviceThingModelFormSchema, commandFormSchema, editDeviceFormSchemaEdit, - bindDeviceThingModelFormSchema, querySchema, tableSchema, } from './schema'; @@ -94,7 +97,7 @@ const gridOptions: VxeGridProps = { : formValues || {}; // 优先使用从表单API获取的值(最新的),如果没有则使用传入的formValues - const finalFormValues = { ...(formValues || {}), ...currentFormValues }; + const finalFormValues = { ...formValues, ...currentFormValues }; const { data } = await postDeviceInfoPage({ body: { @@ -113,7 +116,7 @@ const gridOptions: VxeGridProps = { // 取消选择时不需要验证 return; } - + // 获取当前所有已选中的设备 let currentSelected: any[] = []; if (gridApi?.grid) { @@ -124,17 +127,23 @@ const gridOptions: VxeGridProps = { currentSelected = gridInstance.checkboxRecords; } } - + // 如果已经有选中的设备,检查产品ID是否一致 if (currentSelected.length > 0) { const firstProductId = currentSelected[0]?.ioTPlatformProductId; const currentProductId = row?.ioTPlatformProductId; - + // 检查产品ID是否一致 - if (firstProductId && currentProductId && firstProductId !== currentProductId) { + if ( + firstProductId && + currentProductId && + firstProductId !== currentProductId + ) { // 产品ID不一致,阻止选择并提示 - Message.warning('只能选择同一种产品的设备进行批量绑定,当前设备的产品与已选设备的产品不一致'); - + Message.warning( + '只能选择同一种产品的设备进行批量绑定,当前设备的产品与已选设备的产品不一致', + ); + // 延迟取消选择,确保提示显示 setTimeout(() => { if (gridApi?.grid) { @@ -146,7 +155,6 @@ const gridOptions: VxeGridProps = { } } }, 100); - return; } } }, @@ -156,18 +164,20 @@ const gridOptions: VxeGridProps = { // 取消全选或只有一条记录时不需要验证 return; } - + // 检查所有记录的产品ID是否一致 const productIds = records .map((r: any) => r.ioTPlatformProductId) - .filter((id: any) => id); - + .filter(Boolean); + if (productIds.length > 0) { const uniqueProductIds = [...new Set(productIds)]; if (uniqueProductIds.length > 1) { // 产品ID不一致,阻止全选并提示 - Message.warning('只能选择同一种产品的设备进行批量绑定,当前页面包含多种产品'); - + Message.warning( + '只能选择同一种产品的设备进行批量绑定,当前页面包含多种产品', + ); + // 延迟取消全选 setTimeout(() => { if (gridApi?.grid) { @@ -179,7 +189,6 @@ const gridOptions: VxeGridProps = { } } }, 100); - return; } } }, @@ -199,18 +208,20 @@ watch( try { // 设置表单筛选条件 const filterValues: any = {}; - + if (newQuery.ioTPlatform) { filterValues.ioTPlatform = newQuery.ioTPlatform; } - + if (newQuery.ioTPlatformDeviceOpenInfo) { // 设置产品ID字段 - filterValues.ioTPlatformProductId = newQuery.ioTPlatformDeviceOpenInfo; + filterValues.ioTPlatformProductId = + newQuery.ioTPlatformDeviceOpenInfo; // 同时设置通用字段用于查询 - filterValues.ioTPlatformDeviceOpenInfo = newQuery.ioTPlatformDeviceOpenInfo; + filterValues.ioTPlatformDeviceOpenInfo = + newQuery.ioTPlatformDeviceOpenInfo; } - + // 设置表单值并重新加载数据 if (gridApi?.formApi) { await gridApi.formApi.setValues(filterValues); @@ -224,7 +235,7 @@ watch( }, 200); // 增加延迟时间,确保ApiSelect组件完全初始化 } }, - { immediate: true } + { immediate: true }, ); // 监听产品选择器变化,自动设置ioTPlatformDeviceOpenInfo @@ -233,11 +244,14 @@ watch( (formValues) => { if (formValues) { const { ioTPlatformProductId } = formValues; - + // 如果选择了产品,设置ioTPlatformDeviceOpenInfo if (ioTPlatformProductId) { console.log('检测到产品选择变化:', ioTPlatformProductId); - gridApi?.formApi?.setFieldValue('ioTPlatformDeviceOpenInfo', ioTPlatformProductId); + gridApi?.formApi?.setFieldValue( + 'ioTPlatformDeviceOpenInfo', + ioTPlatformProductId, + ); console.log('已设置ioTPlatformDeviceOpenInfo为:', ioTPlatformProductId); } else { console.log('清空产品选择'); @@ -245,7 +259,7 @@ watch( } } }, - { deep: true } + { deep: true }, ); const cacheRefreshLoading = ref(false); @@ -253,71 +267,174 @@ const pageLoading = ref(false); const loadingTip = ref('缓存刷新中...'); const commandRow: Record = ref({}); -// 绑定设备端物模型相关 -const bindRows: Array> = ref([]); - -// 指令属性-值对列表 -interface CommandProperty { - id: string; // 唯一标识 - propertyId: string; // 物模型属性ID - propertyName: string; // 物模型属性名称(用于显示) - platformFieldName: string; // 平台字段名称(用于提交的键) - standardFieldName: string; // 标准字段名称 - value: string; // 指令值 -} - -const commandProperties = ref([ +// 表格列定义 +const commandTableColumns = computed(() => [ { - id: `prop-${Date.now()}`, - propertyId: '', - propertyName: '', - platformFieldName: '', - standardFieldName: '', - value: '', + title: '', + key: 'selected', + width: 50, + fixed: 'left' as const, + customRender: ({ record }: any) => { + return h(Checkbox, { + checked: record.selected, + 'onUpdate:checked': (checked: boolean) => { + record.selected = checked; + }, + }); + }, + }, + { + title: '平台物模型属性标识符', + dataIndex: 'ioTPlatformRawFieldName', + key: 'ioTPlatformRawFieldName', + width: 200, + }, + { + title: '标准物模型属性名称', + dataIndex: 'standardFieldDisplayName', + key: 'standardFieldDisplayName', + width: 200, + }, + { + title: '指令内容', + dataIndex: 'commandValue', + key: 'commandValue', + width: 200, + customRender: ({ record }: any) => { + return h(Input, { + value: record.commandValue, + placeholder: '请输入指令内容', + 'onUpdate:value': (val: string) => { + record.commandValue = val; + }, + }); + }, + }, + { + title: '返回结果', + dataIndex: 'result', + key: 'result', + width: 250, + customRender: ({ record }: any) => { + // 确保 result 是字符串类型 + const resultText = record.result + ? (typeof record.result === 'string' + ? record.result + : String(record.result)) + : '-'; + const resultStr = String(resultText); + const isError = resultStr.includes('失败') || resultStr.includes('失败:'); + + return h( + 'div', + { + style: { + color: + resultText === '-' ? '#666' : isError ? '#ff4d4f' : '#52c41a', + wordBreak: 'break-all', + }, + }, + resultStr, + ); + }, + }, + { + title: '操作', + key: 'action', + width: 200, + fixed: 'right' as const, + customRender: ({ record }: any) => { + return h( + 'div', + { + style: { + display: 'flex', + gap: '8px', + }, + }, + [ + h( + Button, + { + type: 'primary', + size: 'small', + loading: record.loading, + onClick: () => sendCommand(record), + }, + '发送指令', + ), + h( + Button, + { + size: 'small', + loading: record.loading, + onClick: () => readData(record), + }, + '抄读数据', + ), + ], + ); + }, }, ]); -// 添加新的属性-值对 -const addCommandProperty = () => { - commandProperties.value.push({ - id: `prop-${Date.now()}-${Math.random()}`, - propertyId: '', - propertyName: '', - platformFieldName: '', - standardFieldName: '', - value: '', - }); -}; +// 绑定设备端物模型相关 +const bindRows: Array> = ref([]); -// 删除属性-值对 -const removeCommandProperty = (id: string) => { - const index = commandProperties.value.findIndex((p) => p.id === id); - if (index > -1) { - commandProperties.value.splice(index, 1); - } - // 如果删除后没有项目,至少保留一个 - if (commandProperties.value.length === 0) { - addCommandProperty(); - } -}; +// 平台物模型属性列表(用于表格展示) +interface ThingModelProperty { + id: string; // 物模型属性ID + ioTPlatformRawFieldName: string; // 平台原始字段名 + standardFieldDisplayName: string; // 标准字段显示名 + commandValue: string; // 指令内容 + result: string; // 返回结果 + loading?: boolean; // 是否正在加载 + selected?: boolean; // 是否选中 +} -// 处理物模型属性选择变化 -const handlePropertyChange = (item: any, property: CommandProperty) => { - if (item) { - property.propertyId = item.id || item.value || ''; - property.propertyName = - item.standardFieldDisplayName || - item.ioTPlatformRawFieldName || - item.label || - ''; - // 优先使用平台原始字段名,如果没有则使用标准字段名 - property.platformFieldName = item.ioTPlatformRawFieldName || item.standardFieldName || ''; - property.standardFieldName = item.standardFieldName || ''; - } else { - property.propertyId = ''; - property.propertyName = ''; - property.platformFieldName = ''; - property.standardFieldName = ''; +const thingModelProperties = ref([]); +const thingModelLoading = ref(false); + +// 获取平台物模型属性列表 +const fetchThingModelProperties = async (row: Record) => { + if (!row.ioTPlatform || !row.ioTPlatformProductId) { + Message.warning('设备缺少平台或产品信息,无法获取物模型属性'); + return; + } + + thingModelLoading.value = true; + try { + const requestBody: any = { + pageIndex: 1, + pageSize: 1000, // 获取所有数据 + IsPage: false, // 不分页 + ioTPlatform: + typeof row.ioTPlatform === 'string' + ? Number.parseInt(row.ioTPlatform) + : row.ioTPlatform, + ioTPlatformProductId: String(row.ioTPlatformProductId), + }; + + const { data } = await postIoTplatformThingModelInfoPageAsync({ + body: requestBody, + }); + + const items = Array.isArray(data?.items) ? data!.items : []; + thingModelProperties.value = items.map((item: any) => ({ + id: item.id || '', + ioTPlatformRawFieldName: item.ioTPlatformRawFieldName || '', + standardFieldDisplayName: item.standardFieldDisplayName || '', + commandValue: '', + result: '', + loading: false, + selected: false, + })); + } catch (error) { + console.error('获取平台物模型属性失败:', error); + Message.error('获取平台物模型属性失败'); + thingModelProperties.value = []; + } finally { + thingModelLoading.value = false; } }; @@ -330,30 +447,79 @@ const [UserModal, userModalApi] = useVbenModal({ }, }); +// 批量发送指令 +const submitBatchCommand = async () => { + // 获取所有选中的行,并过滤掉值为空的 + const selectedProperties = thingModelProperties.value.filter( + (prop) => + prop.selected && + prop.ioTPlatformRawFieldName && + prop.commandValue && + prop.commandValue.trim() !== '', + ); + + if (selectedProperties.length === 0) { + Message.warning('请至少选择一个已填写指令内容的属性'); + return; + } + + // 将选中的属性组成键值对(键为标识符,值为指令内容) + const commandContent: Record = {}; + selectedProperties.forEach((prop) => { + commandContent[prop.ioTPlatformRawFieldName] = prop.commandValue.trim(); + }); + + try { + commandModalApi.setState({ loading: true, confirmLoading: true }); + + // 调用指令接口 + const result = await postAggregationDeviceDeviceCommandForApiAsync({ + body: { + id: commandRow.value.id, + commandContent, + }, + }); + + if (result.data) { + // 更新所有选中行的返回结果 + selectedProperties.forEach((prop) => { + prop.result = + typeof result.data === 'string' + ? result.data + : (result.data + ? JSON.stringify(result.data) + : '指令下发成功'); + }); + Message.success(`成功发送 ${selectedProperties.length} 个指令`); + } else { + selectedProperties.forEach((prop) => { + prop.result = '指令下发失败'; + }); + Message.error('指令下发失败'); + } + } catch (error) { + console.error('批量指令发送失败:', error); + selectedProperties.forEach((prop) => { + prop.result = `指令下发失败: ${(error as Error).message}`; + }); + Message.error('指令下发失败'); + } finally { + commandModalApi.setState({ loading: false, confirmLoading: false }); + } +}; + const [CommandModal, commandModalApi] = useVbenModal({ draggable: true, - onConfirm: submitCommand, + onConfirm: submitBatchCommand, onBeforeClose: () => { commandRow.value = {}; - // 重置属性列表,保留一个空项 - commandProperties.value = [ - { - id: `prop-${Date.now()}`, - propertyId: '', - propertyName: '', - platformFieldName: '', - standardFieldName: '', - value: '', - }, - ]; + thingModelProperties.value = []; return true; }, - onOpenChange: (isOpen: boolean) => { + onOpenChange: async (isOpen: boolean) => { if (isOpen && commandRow.value) { - // 打开模态框时,确保至少有一个属性-值对 - if (commandProperties.value.length === 0) { - addCommandProperty(); - } + // 打开模态框时,获取平台物模型属性列表 + await fetchThingModelProperties(commandRow.value); } }, }); @@ -455,15 +621,21 @@ const [BindForm, bindFormApi] = useVbenForm({ wrapperClass: 'grid-cols-1', handleValuesChange: async (values, changedFields) => { // 当开关打开时,手动触发设备物模型下拉框的重新加载 - if (changedFields.includes('isNeedConfigDevicMdoel') && values.isNeedConfigDevicMdoel) { + if ( + changedFields.includes('isNeedConfigDevicMdoel') && + values.isNeedConfigDevicMdoel + ) { console.log('开关打开,准备触发设备物模型下拉框重新加载'); await nextTick(); setTimeout(async () => { try { - const productId = values._ioTPlatformProductId || values.ioTPlatformProductId; + const productId = + values._ioTPlatformProductId || values.ioTPlatformProductId; console.log('手动触发下拉框重新加载,产品ID:', productId); if (productId) { - const fieldRef = bindFormApi.getFieldComponentRef('deviceThingModelDataId'); + const fieldRef = bindFormApi.getFieldComponentRef( + 'deviceThingModelDataId', + ); if (fieldRef && typeof fieldRef.updateParam === 'function') { fieldRef.updateParam({ query: { @@ -682,27 +854,19 @@ const openAddModal = async () => { userModalApi.open(); }; -const openCommandModal = (row: Record) => { +const openCommandModal = async (row: Record) => { commandRow.value = row; - // 重置属性列表,保留一个空项 - commandProperties.value = [ - { - id: `prop-${Date.now()}`, - propertyId: '', - propertyName: '', - platformFieldName: '', - standardFieldName: '', - value: '', - }, - ]; + thingModelProperties.value = []; commandModalApi.open(); }; // 打开绑定设备端物模型弹窗(支持单个设备或多个设备) -const openBindModal = async (rowOrRows?: Record | Array>) => { +const openBindModal = async ( + rowOrRows?: Array> | Record, +) => { // 如果没有传入参数,尝试从表格获取选中的设备 let selectedRows: Array> = []; - + if (rowOrRows) { // 如果传入的是数组,直接使用 if (Array.isArray(rowOrRows)) { @@ -715,7 +879,7 @@ const openBindModal = async (rowOrRows?: Record | Array | Array | Array 1) { const productIds = selectedRows .map((row) => row.ioTPlatformProductId) - .filter((id) => id); // 过滤掉空值 - + .filter(Boolean); // 过滤掉空值 + if (productIds.length === 0) { Message.error('选中的设备中没有有效的产品ID,无法进行绑定'); return; } - + // 检查产品ID是否一致 const uniqueProductIds = [...new Set(productIds)]; if (uniqueProductIds.length > 1) { - Message.error(`选中的设备包含 ${uniqueProductIds.length} 种不同的产品,请只选择同一种产品的设备进行批量绑定`); + Message.error( + `选中的设备包含 ${uniqueProductIds.length} 种不同的产品,请只选择同一种产品的设备进行批量绑定`, + ); return; } } - + console.log('打开绑定弹窗,选中的设备:', selectedRows); bindRows.value = selectedRows; - + // 获取第一个设备的产品ID(用于下拉框) const firstDevice = selectedRows[0]; const productId = firstDevice.ioTPlatformProductId; console.log('设置表单值,产品ID:', productId, '类型:', typeof productId); - + if (!productId) { - console.warn('警告:设备行数据中没有 ioTPlatformProductId 字段,请检查表格数据是否包含该字段'); + console.warn( + '警告:设备行数据中没有 ioTPlatformProductId 字段,请检查表格数据是否包含该字段', + ); } - + bindModalApi.open(); await nextTick(); - + await bindFormApi.setValues({ isNeedConfigDevicMdoel: false, deviceThingModelDataId: undefined, _ioTPlatformProductId: productId ? String(productId) : undefined, }); - + const formValues = await bindFormApi.getValues(); console.log('表单值设置完成,当前表单值:', formValues); - console.log('表单值中的 _ioTPlatformProductId:', formValues._ioTPlatformProductId); + console.log( + '表单值中的 _ioTPlatformProductId:', + formValues._ioTPlatformProductId, + ); }; -// 指令提交逻辑 -async function submitCommand() { - // 验证所有属性-值对都已填写 - const invalidProperties = commandProperties.value.filter( - (p) => !p.propertyId || !p.value || p.value.trim() === '', - ); - - if (invalidProperties.length > 0) { - Message.error('请完整填写所有属性名称和指令值'); +// 发送指令 +const sendCommand = async (property: ThingModelProperty) => { + if (!property.commandValue || property.commandValue.trim() === '') { + Message.warning('请输入指令内容'); return; } - // 验证至少有一个有效的属性-值对(有键和值) - const validProperties = commandProperties.value.filter( - (p) => { - const key = p.platformFieldName || p.standardFieldName || p.propertyName; - return key && p.value && p.value.trim() !== ''; - }, - ); - - if (validProperties.length === 0) { - Message.error('请至少添加一个有效的属性-值对'); + if (!property.ioTPlatformRawFieldName) { + Message.warning('该属性缺少平台字段名,无法发送指令'); return; } + property.loading = true; try { - commandModalApi.setState({ loading: true, confirmLoading: true }); - - // 将属性-值对组成字典 const commandContent: Record = {}; - commandProperties.value.forEach((prop) => { - if (prop.value && prop.value.trim() !== '') { - // 优先使用平台字段名作为键,如果没有则使用标准字段名,最后使用显示名称 - const key = prop.platformFieldName || prop.standardFieldName || prop.propertyName; - if (key) { - commandContent[key] = prop.value; - } - } - }); + commandContent[property.ioTPlatformRawFieldName] = property.commandValue; - // 调用指令接口 const result = await postAggregationDeviceDeviceCommandForApiAsync({ body: { id: commandRow.value.id, - commandContent: commandContent, + commandContent, }, }); if (result.data) { + // 确保 result 始终是字符串类型 + property.result = + typeof result.data === 'string' + ? result.data + : (result.data + ? JSON.stringify(result.data) + : '指令下发成功'); Message.success('指令下发成功'); - commandModalApi.close(); - commandRow.value = {}; - // 重置属性列表 - commandProperties.value = [ - { - id: `prop-${Date.now()}`, - propertyId: '', - propertyName: '', - platformFieldName: '', - standardFieldName: '', - value: '', - }, - ]; } else { + property.result = '指令下发失败'; Message.error('指令下发失败'); } } catch (error) { console.error('指令发送失败:', error); + property.result = `指令下发失败: ${(error as Error).message}`; Message.error('指令下发失败'); } finally { - commandModalApi.setState({ loading: false, confirmLoading: false }); + property.loading = false; } -} +}; + +// 抄读数据 +const readData = async (property: ThingModelProperty) => { + if (!property.ioTPlatformRawFieldName) { + Message.warning('该属性缺少平台字段名,无法抄读数据'); + return; + } + + if (!commandRow.value.id) { + Message.warning('设备ID不存在,无法抄读数据'); + return; + } + + property.loading = true; + try { + const result = await postAggregationDeviceGetDevicePropertyValueForApiAsync( + { + body: { + id: String(commandRow.value.id), + propertyList: [property.ioTPlatformRawFieldName], + }, + }, + ); + + if (result.data !== undefined && result.data !== null) { + // 返回结果是一个对象,需要根据属性名获取对应的值 + const propertyValue = (result.data as any)[ + property.ioTPlatformRawFieldName + ]; + if (propertyValue !== undefined && propertyValue !== null) { + property.result = + typeof propertyValue === 'string' + ? propertyValue + : JSON.stringify(propertyValue); + Message.success('抄读数据成功'); + } else { + property.result = '未获取到数据'; + Message.warning('未获取到该属性的数据'); + } + } else { + property.result = '抄读数据失败'; + } + } catch (error) { + console.error('抄读数据失败:', error); + property.result = `抄读数据失败: ${(error as Error).message}`; + } finally { + property.loading = false; + } +}; // 绑定设备端物模型提交逻辑 async function submitBindDeviceThingModel() { @@ -885,9 +1077,7 @@ async function submitBindDeviceThingModel() { } // 获取所有选中设备的ID - const deviceIds = bindRows.value - .map((row) => row.id) - .filter((id) => id); // 过滤掉空值 + const deviceIds = bindRows.value.map((row) => row.id).filter(Boolean); // 过滤掉空值 if (deviceIds.length === 0) { Message.error('选中的设备中没有有效的设备ID'); @@ -914,7 +1104,6 @@ async function submitBindDeviceThingModel() { } } catch (error) { console.error('绑定设备端物模型失败:', error); - Message.error('绑定设备端物模型失败'); } finally { bindModalApi.setState({ loading: false, confirmLoading: false }); } @@ -1153,80 +1342,94 @@ const openBatchBindModal = async () => { try { console.log('gridApi:', gridApi); console.log('gridApi.grid:', gridApi?.grid); - + // 尝试多种方式获取选中的行 let checkboxRecords: any[] = []; - + // 方式1:通过 grid 获取 if (gridApi?.grid) { console.log('grid 可用,尝试获取选中行'); const gridInstance = gridApi.grid as any; - + // 尝试不同的方法名 if (typeof gridInstance.getCheckboxRecords === 'function') { checkboxRecords = gridInstance.getCheckboxRecords(); console.log('通过 grid.getCheckboxRecords 获取:', checkboxRecords); } else if (typeof gridInstance.getCheckboxReserveRecords === 'function') { checkboxRecords = gridInstance.getCheckboxReserveRecords(); - console.log('通过 grid.getCheckboxReserveRecords 获取:', checkboxRecords); + console.log( + '通过 grid.getCheckboxReserveRecords 获取:', + checkboxRecords, + ); } - + // 尝试直接访问内部属性 if (checkboxRecords.length === 0 && gridInstance.checkboxRecords) { checkboxRecords = gridInstance.checkboxRecords; console.log('通过 grid.checkboxRecords 属性获取:', checkboxRecords); } - + // 尝试通过 store 获取 if (checkboxRecords.length === 0 && gridInstance.$store) { const store = gridInstance.$store; if (store.checkboxRecords) { checkboxRecords = store.checkboxRecords; - console.log('通过 grid.$store.checkboxRecords 获取:', checkboxRecords); + console.log( + '通过 grid.$store.checkboxRecords 获取:', + checkboxRecords, + ); } } } - + // 方式2:通过 gridApi.store 获取 if (checkboxRecords.length === 0 && gridApi?.store) { const store = gridApi.store as any; if (store.checkboxRecords) { checkboxRecords = store.checkboxRecords; - console.log('通过 gridApi.store.checkboxRecords 获取:', checkboxRecords); + console.log( + '通过 gridApi.store.checkboxRecords 获取:', + checkboxRecords, + ); } } - + // 方式3:直接调用 gridApi 的方法 - if (checkboxRecords.length === 0 && typeof (gridApi as any).getCheckboxRecords === 'function') { + if ( + checkboxRecords.length === 0 && + typeof (gridApi as any).getCheckboxRecords === 'function' + ) { checkboxRecords = (gridApi as any).getCheckboxRecords(); console.log('通过 gridApi.getCheckboxRecords 获取:', checkboxRecords); } - + console.log('最终获取到的选中设备:', checkboxRecords); console.log('选中设备数量:', checkboxRecords.length); - + if (!checkboxRecords || checkboxRecords.length === 0) { Message.warning('请先在表格中勾选要绑定的设备(点击每行前面的复选框)'); return; } - + // 验证所有选中的设备必须是同一种产品 const productIds = checkboxRecords .map((row) => row.ioTPlatformProductId) - .filter((id) => id); // 过滤掉空值 - + .filter(Boolean); // 过滤掉空值 + if (productIds.length === 0) { Message.error('选中的设备中没有有效的产品ID,无法进行绑定'); return; } - + // 检查产品ID是否一致 const uniqueProductIds = [...new Set(productIds)]; if (uniqueProductIds.length > 1) { - Message.error(`选中的设备包含 ${uniqueProductIds.length} 种不同的产品,请只选择同一种产品的设备进行批量绑定`); + Message.error( + `选中的设备包含 ${uniqueProductIds.length} 种不同的产品,请只选择同一种产品的设备进行批量绑定`, + ); return; } - + await openBindModal(checkboxRecords); } catch (error) { console.error('获取表格选中的设备失败:', error); @@ -1338,10 +1541,10 @@ const toolbarActions = computed(() => [ {{ $t('abp.deviceInfos.viewData') }}