平台端物模型管理新增模型刷功能

This commit is contained in:
ChenYi 2026-04-15 08:51:22 +08:00
parent 90a0a05147
commit 8f4cd47a4f
6 changed files with 297 additions and 43 deletions

View File

@ -1720,6 +1720,30 @@ export const CreateOrganizationUnitInputSchema = {
additionalProperties: false additionalProperties: false
} as const; } as const;
export const CreatePlatformThingModelCommandInputSchema = {
required: ['issueCommand', 'operateType', 'thingModelDataId'],
type: 'object',
properties: {
thingModelDataId: {
type: 'string',
description: '平台端物模型数据Id',
format: 'uuid'
},
operateType: {
type: 'integer',
description: '待下发的操作指令类型例如阀控操作时是拉闸还是合闸根据StandardFieldName进行分组',
format: 'int32'
},
issueCommand: {
minLength: 1,
type: 'string',
description: '完整透明转发指令'
}
},
additionalProperties: false,
description: '新增平台端物模型操作指令'
} as const;
export const CreateTextTemplateInputSchema = { export const CreateTextTemplateInputSchema = {
required: ['code', 'content', 'cultureName', 'name'], required: ['code', 'content', 'cultureName', 'name'],
type: 'object', type: 'object',
@ -2995,8 +3019,7 @@ export const DeviceThingModelCommandInfoDtoSchema = {
operateCommandType: { operateCommandType: {
type: 'integer', type: 'integer',
description: '待下发的操作指令类型,,例如阀控操作时,是拉闸还是合闸', description: '待下发的操作指令类型,,例如阀控操作时,是拉闸还是合闸',
format: 'int32', format: 'int32'
nullable: true
} }
}, },
additionalProperties: false, additionalProperties: false,
@ -6272,7 +6295,10 @@ export const IoTPlatformThingModelCreateInputSchema = {
description: '是否是特殊物模型标识符' description: '是否是特殊物模型标识符'
}, },
ioTPlatformRawFieldExtension: { ioTPlatformRawFieldExtension: {
'$ref': '#/components/schemas/JToken' type: 'object',
additionalProperties: {},
description: '物联网平台中对应产品物模型标识符扩展,用于扩展结构体类型',
nullable: true
}, },
isOperableIdentifier: { isOperableIdentifier: {
type: 'boolean', type: 'boolean',
@ -6287,7 +6313,10 @@ export const IoTPlatformThingModelCreateInputSchema = {
'$ref': '#/components/schemas/ThingModelIdentifierTypeEnum' '$ref': '#/components/schemas/ThingModelIdentifierTypeEnum'
}, },
standardFieldFieldExtension: { standardFieldFieldExtension: {
'$ref': '#/components/schemas/JToken' type: 'object',
additionalProperties: {},
description: '标准物模型字段标识符扩展,数组或者结构体时候的参数或者元素名称集合',
nullable: true
} }
}, },
additionalProperties: false additionalProperties: false
@ -6709,7 +6738,10 @@ export const IoTPlatformThingModelUpdateInputSchema = {
description: '是否是特殊物模型标识符' description: '是否是特殊物模型标识符'
}, },
ioTPlatformRawFieldExtension: { ioTPlatformRawFieldExtension: {
'$ref': '#/components/schemas/JToken' type: 'object',
additionalProperties: {},
description: '物联网平台中对应产品物模型标识符扩展,用于扩展结构体类型',
nullable: true
}, },
isOperableIdentifier: { isOperableIdentifier: {
type: 'boolean', type: 'boolean',
@ -6724,7 +6756,10 @@ export const IoTPlatformThingModelUpdateInputSchema = {
'$ref': '#/components/schemas/ThingModelIdentifierTypeEnum' '$ref': '#/components/schemas/ThingModelIdentifierTypeEnum'
}, },
standardFieldFieldExtension: { standardFieldFieldExtension: {
'$ref': '#/components/schemas/JToken' type: 'object',
additionalProperties: {},
description: '标准物模型字段标识符扩展,数组或者结构体时候的参数或者元素名称集合',
nullable: true
}, },
id: { id: {
type: 'string', type: 'string',
@ -6740,13 +6775,6 @@ export const IoTPlatformTypeEnumSchema = {
description: '物联网平台类型枚举' description: '物联网平台类型枚举'
} as const; } as const;
export const JTokenSchema = {
type: 'array',
items: {
'$ref': '#/components/schemas/JToken'
}
} as const;
export const LanguageInfoSchema = { export const LanguageInfoSchema = {
type: 'object', type: 'object',
properties: { properties: {
@ -9389,6 +9417,14 @@ export const PagingDataDictionaryDetailOutputSchema = {
description: '扩展属性', description: '扩展属性',
nullable: true nullable: true
}, },
extendedAttributeValue: {
type: 'object',
additionalProperties: {
nullable: true
},
description: '扩展属性值',
nullable: true
},
isEnabled: { isEnabled: {
type: 'boolean', type: 'boolean',
description: '启/停用(默认启用)' description: '启/停用(默认启用)'

File diff suppressed because one or more lines are too long

View File

@ -552,6 +552,24 @@ export type CreateOrganizationUnitInput = {
parentId?: (string) | null; parentId?: (string) | null;
}; };
/**
*
*/
export type CreatePlatformThingModelCommandInput = {
/**
* Id
*/
thingModelDataId: string;
/**
* StandardFieldName进行分组
*/
operateType: number;
/**
*
*/
issueCommand: string;
};
/** /**
* *
*/ */
@ -1731,7 +1749,7 @@ export type DeviceThingModelCommandInfoDto = {
/** /**
* , * ,
*/ */
operateCommandType?: (number) | null; operateCommandType?: number;
}; };
export type DeviceThingModelCommandInfoDtoPagedResultDto = { export type DeviceThingModelCommandInfoDtoPagedResultDto = {
@ -3286,7 +3304,12 @@ export type IoTPlatformThingModelCreateInput = {
* *
*/ */
isSpecialIdentifier?: boolean; isSpecialIdentifier?: boolean;
ioTPlatformRawFieldExtension?: JToken; /**
*
*/
ioTPlatformRawFieldExtension?: {
[key: string]: unknown;
} | null;
/** /**
* *
*/ */
@ -3296,7 +3319,12 @@ export type IoTPlatformThingModelCreateInput = {
*/ */
accessMode?: (string) | null; accessMode?: (string) | null;
identifierType?: ThingModelIdentifierTypeEnum; identifierType?: ThingModelIdentifierTypeEnum;
standardFieldFieldExtension?: JToken; /**
*
*/
standardFieldFieldExtension?: {
[key: string]: unknown;
} | null;
}; };
/** /**
@ -3565,7 +3593,12 @@ export type IoTPlatformThingModelUpdateInput = {
* *
*/ */
isSpecialIdentifier?: boolean; isSpecialIdentifier?: boolean;
ioTPlatformRawFieldExtension?: JToken; /**
*
*/
ioTPlatformRawFieldExtension?: {
[key: string]: unknown;
} | null;
/** /**
* *
*/ */
@ -3575,7 +3608,12 @@ export type IoTPlatformThingModelUpdateInput = {
*/ */
accessMode?: (string) | null; accessMode?: (string) | null;
identifierType?: ThingModelIdentifierTypeEnum; identifierType?: ThingModelIdentifierTypeEnum;
standardFieldFieldExtension?: JToken; /**
*
*/
standardFieldFieldExtension?: {
[key: string]: unknown;
} | null;
id?: string; id?: string;
}; };
@ -3599,8 +3637,6 @@ export type IValueValidator = {
} | null; } | null;
}; };
export type JToken = Array<JToken>;
export type LanguageInfo = { export type LanguageInfo = {
cultureName?: (string) | null; cultureName?: (string) | null;
uiCultureName?: (string) | null; uiCultureName?: (string) | null;
@ -5219,6 +5255,12 @@ export type PagingDataDictionaryDetailOutput = {
* *
*/ */
extendedAttribute?: (string) | null; extendedAttribute?: (string) | null;
/**
*
*/
extendedAttributeValue?: {
[key: string]: unknown;
} | null;
/** /**
* /() * /()
*/ */
@ -7736,6 +7778,16 @@ export type PostIoTplatformThingModelInfoGetIoTplatformThingModelServiceResponse
export type PostIoTplatformThingModelInfoGetIoTplatformThingModelServiceError = unknown; export type PostIoTplatformThingModelInfoGetIoTplatformThingModelServiceError = unknown;
export type PostIoTplatformThingModelInfoCreateIoTplatformThingModelCommandData = {
query?: {
input?: CreatePlatformThingModelCommandInput;
};
};
export type PostIoTplatformThingModelInfoCreateIoTplatformThingModelCommandResponse = (IoTPlatformThingModelCommandDto);
export type PostIoTplatformThingModelInfoCreateIoTplatformThingModelCommandError = unknown;
export type PostLanguagesAllResponse = (Array<PageLanguageOutput>); export type PostLanguagesAllResponse = (Array<PageLanguageOutput>);
export type PostLanguagesAllError = (RemoteServiceErrorResponse); export type PostLanguagesAllError = (RemoteServiceErrorResponse);

View File

@ -337,9 +337,9 @@ const commandTableColumns = computed(() => [
}, },
}, },
{ {
title: '平台物模型属性标识符', title: '标准物模型属性标识符',
dataIndex: 'ioTPlatformRawFieldName', dataIndex: 'standardFieldName',
key: 'ioTPlatformRawFieldName', key: 'standardFieldName',
width: 200, width: 200,
}, },
{ {
@ -473,6 +473,7 @@ const bindRows: Array<Record<string, any>> = ref([]);
interface ThingModelProperty { interface ThingModelProperty {
id: string; // ID id: string; // ID
ioTPlatformRawFieldName: string; // ioTPlatformRawFieldName: string; //
standardFieldName: string; //
standardFieldDisplayName: string; // standardFieldDisplayName: string; //
commandValue: string; // commandValue: string; //
result: string; // result: string; //
@ -487,6 +488,10 @@ const thingModelLoading = ref(false);
// //
const filterKeyword = ref(''); const filterKeyword = ref('');
// 使
const getPropertyIdentifier = (property: ThingModelProperty) =>
(property.standardFieldName || '').trim();
// //
const isNumericType = (dataType: string | undefined): boolean => { const isNumericType = (dataType: string | undefined): boolean => {
if (!dataType) return false; if (!dataType) return false;
@ -559,7 +564,7 @@ const filteredThingModelProperties = computed(() => {
return thingModelProperties.value.filter((prop) => { return thingModelProperties.value.filter((prop) => {
return ( return (
prop.standardFieldDisplayName?.toLowerCase().includes(keyword) || prop.standardFieldDisplayName?.toLowerCase().includes(keyword) ||
prop.ioTPlatformRawFieldName?.toLowerCase().includes(keyword) prop.standardFieldName?.toLowerCase().includes(keyword)
); );
}); });
}); });
@ -593,6 +598,7 @@ const fetchThingModelProperties = async (row: Record<string, any>) => {
thingModelProperties.value = items.map((item: any) => ({ thingModelProperties.value = items.map((item: any) => ({
id: item.id || '', id: item.id || '',
ioTPlatformRawFieldName: item.ioTPlatformRawFieldName || '', ioTPlatformRawFieldName: item.ioTPlatformRawFieldName || '',
standardFieldName: item.standardFieldName || '',
standardFieldDisplayName: item.standardFieldDisplayName || '', standardFieldDisplayName: item.standardFieldDisplayName || '',
commandValue: '', commandValue: '',
result: '', result: '',
@ -626,7 +632,7 @@ const submitBatchCommand = async () => {
const selectedProperties = thingModelProperties.value.filter( const selectedProperties = thingModelProperties.value.filter(
(prop) => (prop) =>
prop.selected && prop.selected &&
prop.ioTPlatformRawFieldName && getPropertyIdentifier(prop) &&
(prop.accessMode === 'rw' || prop.accessMode === 'w') && // (prop.accessMode === 'rw' || prop.accessMode === 'w') && //
prop.commandValue && prop.commandValue &&
prop.commandValue.trim() !== '', prop.commandValue.trim() !== '',
@ -647,7 +653,10 @@ const submitBatchCommand = async () => {
prop.commandValue.trim(), prop.commandValue.trim(),
prop.ioTPlatformRawFieldDataType, prop.ioTPlatformRawFieldDataType,
); );
commandContent[prop.ioTPlatformRawFieldName] = convertedValue; const identifier = getPropertyIdentifier(prop);
if (identifier) {
commandContent[identifier] = convertedValue;
}
}); });
try { try {
@ -2023,8 +2032,9 @@ const sendCommand = async (property: ThingModelProperty) => {
return; return;
} }
if (!property.ioTPlatformRawFieldName) { const identifier = getPropertyIdentifier(property);
Message.warning('该属性缺少平台字段名,无法发送指令'); if (!identifier) {
Message.warning('该属性缺少标准物模型标识符,无法发送指令');
return; return;
} }
@ -2036,7 +2046,7 @@ const sendCommand = async (property: ThingModelProperty) => {
property.commandValue.trim(), property.commandValue.trim(),
property.ioTPlatformRawFieldDataType, property.ioTPlatformRawFieldDataType,
); );
commandContent[property.ioTPlatformRawFieldName] = convertedValue; commandContent[identifier] = convertedValue;
const result = await postAggregationDeviceDeviceCommandForApiAsync({ const result = await postAggregationDeviceDeviceCommandForApiAsync({
body: { body: {
@ -2075,8 +2085,9 @@ const readData = async (property: ThingModelProperty) => {
return; return;
} }
if (!property.ioTPlatformRawFieldName) { const identifier = getPropertyIdentifier(property);
Message.warning('该属性缺少平台字段名,无法抄读数据'); if (!identifier) {
Message.warning('该属性缺少标准物模型标识符,无法抄读数据');
return; return;
} }
@ -2091,16 +2102,14 @@ const readData = async (property: ThingModelProperty) => {
{ {
body: { body: {
id: String(commandRow.value.id), id: String(commandRow.value.id),
propertyList: [property.ioTPlatformRawFieldName], propertyList: { [identifier]: '' },
}, },
}, },
); );
if (result.data !== undefined && result.data !== null) { if (result.data !== undefined && result.data !== null) {
// //
const propertyValue = (result.data as any)[ const propertyValue = (result.data as any)[identifier];
property.ioTPlatformRawFieldName
];
if (propertyValue !== undefined && propertyValue !== null) { if (propertyValue !== undefined && propertyValue !== null) {
property.result = property.result =
typeof propertyValue === 'string' typeof propertyValue === 'string'
@ -2141,7 +2150,7 @@ const submitBatchRead = async () => {
// w // w
const selectedProperties = thingModelProperties.value.filter( const selectedProperties = thingModelProperties.value.filter(
(prop) => (prop) =>
prop.selected && prop.ioTPlatformRawFieldName && prop.accessMode !== 'w', // prop.selected && getPropertyIdentifier(prop) && prop.accessMode !== 'w', //
); );
if (selectedProperties.length === 0) { if (selectedProperties.length === 0) {
@ -2155,8 +2164,15 @@ const submitBatchRead = async () => {
} }
// //
const propertyList = selectedProperties.map( const propertyList = selectedProperties.reduce<Record<string, string>>(
(prop) => prop.ioTPlatformRawFieldName, (acc, prop) => {
const identifier = getPropertyIdentifier(prop);
if (identifier) {
acc[identifier] = '';
}
return acc;
},
{},
); );
try { try {
@ -2180,9 +2196,10 @@ const submitBatchRead = async () => {
if (result.data !== undefined && result.data !== null) { if (result.data !== undefined && result.data !== null) {
// //
selectedProperties.forEach((prop) => { selectedProperties.forEach((prop) => {
const propertyValue = (result.data as any)[ const identifier = getPropertyIdentifier(prop);
prop.ioTPlatformRawFieldName const propertyValue = identifier
]; ? (result.data as any)[identifier]
: undefined;
if (propertyValue !== undefined && propertyValue !== null) { if (propertyValue !== undefined && propertyValue !== null) {
prop.result = prop.result =
typeof propertyValue === 'string' typeof propertyValue === 'string'

View File

@ -13,6 +13,7 @@ import { useVbenForm } from '#/adapter/form';
import { useVbenVxeGrid } from '#/adapter/vxe-table'; import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { import {
getCommonGetSelectList, getCommonGetSelectList,
postAggregationIoTplatformUpdateIoTplatformProductThingModelInfoAsync,
postIoTplatformThingModelInfoCopyAnotherThingModelAsync, postIoTplatformThingModelInfoCopyAnotherThingModelAsync,
postIoTplatformThingModelInfoCopyStandardThingModel, postIoTplatformThingModelInfoCopyStandardThingModel,
postIoTplatformThingModelInfoCreateAsync, postIoTplatformThingModelInfoCreateAsync,
@ -224,6 +225,8 @@ const editRow: Record<string, any> = ref({});
// //
const hasData = ref(true); const hasData = ref(true);
// loading
const thingModelRefreshLoading = ref(false);
const [ThingModelModal, thingModelModalApi] = useVbenModal({ const [ThingModelModal, thingModelModalApi] = useVbenModal({
draggable: true, draggable: true,
@ -522,6 +525,49 @@ onMounted(async () => {
}, 300); }, 300);
}); });
function normalizeFieldExtensionValue(fieldValue: unknown) {
if (typeof fieldValue !== 'string') {
return fieldValue;
}
const trimmed = fieldValue.trim();
if (!trimmed) {
return '';
}
try {
return JSON.parse(trimmed);
} catch {
return fieldValue;
}
}
function parseJsonFieldValue(
fieldValue: unknown,
fieldLabel: string,
): { ok: boolean; value: unknown } {
if (fieldValue == null || fieldValue === '') {
return { ok: true, value: fieldValue };
}
if (typeof fieldValue !== 'string') {
return { ok: true, value: fieldValue };
}
const trimmed = fieldValue.trim();
if (!trimmed) {
return { ok: true, value: '' };
}
try {
return {
ok: true,
value: JSON.parse(trimmed),
};
} catch {
Message.error(`${fieldLabel}必须是合法JSON格式`);
return { ok: false, value: fieldValue };
}
}
// //
async function submit() { async function submit() {
const isEdit = !!editRow.value.id; const isEdit = !!editRow.value.id;
@ -574,6 +620,14 @@ async function submit() {
} }
const formValues = await formApi.getValues(); const formValues = await formApi.getValues();
const ioTPlatformRawFieldExtensionParsed = parseJsonFieldValue(
formValues.ioTPlatformRawFieldExtension,
'平台物模型值类型扩展',
);
if (!ioTPlatformRawFieldExtensionParsed.ok) {
return;
}
const fetchParams: any = { const fetchParams: any = {
...formValues, ...formValues,
// //
@ -588,6 +642,12 @@ async function submit() {
}), }),
// ID // ID
...(isEdit && { id: editRow.value.id }), ...(isEdit && { id: editRow.value.id }),
// JSON
standardFieldFieldExtension: normalizeFieldExtensionValue(
formValues.standardFieldFieldExtension,
),
// JSON
ioTPlatformRawFieldExtension: ioTPlatformRawFieldExtensionParsed.value,
}; };
try { try {
@ -695,6 +755,48 @@ const openAddModal = async () => {
}, 100); }, 100);
}; };
//
async function onThingModelRefresh() {
const formValues = gridApi?.formApi ? await gridApi.formApi.getValues() : {};
const platformValue = formValues.ioTPlatform || ioTPlatform.value;
const productIdValue = formValues.ioTPlatformProductId || productId.value;
if (!productIdValue) {
Message.error('请选择产品后再刷新模型');
return;
}
const ioTPlatformType = Number.parseInt(String(platformValue || ''), 10);
if (Number.isNaN(ioTPlatformType)) {
Message.error('平台类型无效,无法刷新模型');
return;
}
thingModelRefreshLoading.value = true;
try {
const resp =
await postAggregationIoTplatformUpdateIoTplatformProductThingModelInfoAsync(
{
body: {
ioTPlatformType,
ioTPlatformProductId: String(productIdValue),
},
},
);
if (resp.data) {
Message.success('模型刷新成功');
await gridApi.reload();
} else {
Message.error('模型刷新失败');
}
} catch (error) {
console.error('模型刷新失败:', error);
Message.error('模型刷新失败');
} finally {
thingModelRefreshLoading.value = false;
}
}
// //
const openCopyAnotherThingModelModal = async () => { const openCopyAnotherThingModelModal = async () => {
console.log('打开复制模态框,当前参数:', { console.log('打开复制模态框,当前参数:', {
@ -851,6 +953,15 @@ async function onDel(record: any) {
ifShow: !hasData, ifShow: !hasData,
disabled: !productId, disabled: !productId,
}, },
{
label: '模型刷新',
type: 'default',
icon: 'ant-design:reload-outlined',
onClick: onThingModelRefresh,
auth: ['AbpIdentity.Users.Create'],
disabled: !productId,
loading: thingModelRefreshLoading,
},
]" /> ]" />
</template> </template>

View File

@ -30,6 +30,21 @@ function formatFieldExtensionCell(cellValue: unknown): string {
return String(cellValue); return String(cellValue);
} }
/** 表单中的扩展字段统一转换为可编辑文本 */
function formatFieldExtensionFormValue(fieldValue: unknown): string {
if (fieldValue == null || fieldValue === '') {
return '';
}
if (typeof fieldValue === 'string') {
return fieldValue;
}
try {
return JSON.stringify(fieldValue, null, 2);
} catch {
return String(fieldValue);
}
}
export const querySchema = computed(() => [ export const querySchema = computed(() => [
{ {
component: 'ApiSelect', component: 'ApiSelect',
@ -405,6 +420,10 @@ export const getAddThingModelFormSchema = (
formValues.standardFieldValueType = (item?.extendedAttribute ?? '') formValues.standardFieldValueType = (item?.extendedAttribute ?? '')
.toString() .toString()
.toUpperCase(); .toUpperCase();
// 选择后自动回填标准物模型扩展参数extendedAttributeValue
formValues.standardFieldFieldExtension = formatFieldExtensionFormValue(
item?.extendedAttributeValue,
);
}, },
placeholder: placeholder:
$t('common.pleaseSelect') + $t('common.pleaseSelect') +
@ -425,6 +444,15 @@ export const getAddThingModelFormSchema = (
$t('abp.thingModelInfos.StandardFieldDisplayName'), $t('abp.thingModelInfos.StandardFieldDisplayName'),
}, },
}, },
{
component: 'Textarea',
fieldName: 'standardFieldFieldExtension',
label: $t('abp.thingModelInfos.StandardFieldFieldExtension'),
componentProps: {
rows: 4,
placeholder: '请选择标准物模型后自动回填扩展参数JSON格式',
},
},
{ {
component: 'ApiSelect', component: 'ApiSelect',
fieldName: 'ioTPlatformRawFieldName', fieldName: 'ioTPlatformRawFieldName',