标准物模型下拉接口适配

This commit is contained in:
ChenYi 2025-10-22 17:06:15 +08:00
parent 5e547f09b2
commit 2f10b68443
9 changed files with 424 additions and 91 deletions

View File

@ -112,6 +112,7 @@ export type ComponentType =
| 'DatePicker' | 'DatePicker'
| 'DefaultButton' | 'DefaultButton'
| 'DeviceSelect' | 'DeviceSelect'
| 'StandardThingModelCodeSelect'
| 'Divider' | 'Divider'
| 'IconPicker' | 'IconPicker'
| 'Input' | 'Input'
@ -171,8 +172,11 @@ async function initComponentAdapter() {
DatePicker, DatePicker,
// 自定义设备选择组件 // 自定义设备选择组件
DeviceSelect: defineAsyncComponent( DeviceSelect: defineAsyncComponent(
() => () => import('#/views/iotdbdatamanagement/deviceData/DeviceSelect.vue') as any,
import('../../views/iotdbdatamanagement/deviceData/DeviceSelect.vue'), ),
// 自定义标准物模型编码选择组件(分页搜索)
StandardThingModelCodeSelect: defineAsyncComponent(
() => import('#/views/devicemanagement/thingmodelinfo/StandardThingModelCodeSelect.vue') as any,
), ),
// 自定义默认按钮 // 自定义默认按钮
DefaultButton: (props, { attrs, slots }) => { DefaultButton: (props, { attrs, slots }) => {

View File

@ -8399,6 +8399,49 @@ export const ReturnValueApiDescriptionModelSchema = {
additionalProperties: false additionalProperties: false
} as const; } as const;
export const SelectDataDictionaryDetailInputSchema = {
type: 'object',
properties: {
pageIndex: {
type: 'integer',
description: '当前页面.默认从1开始',
format: 'int32'
},
pageSize: {
type: 'integer',
description: '每页多少条.每页显示多少记录',
format: 'int32'
},
skipCount: {
type: 'integer',
description: '跳过多少条',
format: 'int32',
readOnly: true
},
sorting: {
type: 'string',
description: `排序
<example>
name desc
</example>`,
nullable: true
},
dataDictionaryId: {
type: 'string',
format: 'uuid'
},
filter: {
type: 'string',
nullable: true
},
typeCode: {
type: 'string',
nullable: true
}
},
additionalProperties: false
} as const;
export const SelectResultSchema = { export const SelectResultSchema = {
type: 'object', type: 'object',
properties: { properties: {

File diff suppressed because one or more lines are too long

View File

@ -4351,6 +4351,31 @@ export type ReturnValueApiDescriptionModel = {
typeSimple?: (string) | null; typeSimple?: (string) | null;
}; };
export type SelectDataDictionaryDetailInput = {
/**
* .1
*/
pageIndex?: number;
/**
* .
*/
pageSize?: number;
/**
*
*/
readonly skipCount?: number;
/**
*
* <example>
* name desc
* </example>
*/
sorting?: (string) | null;
dataDictionaryId?: string;
filter?: (string) | null;
typeCode?: (string) | null;
};
/** /**
* *
*/ */
@ -5036,6 +5061,14 @@ export type PostDataDictionaryPageDetailResponse = (PagingDataDictionaryDetailOu
export type PostDataDictionaryPageDetailError = (RemoteServiceErrorResponse); export type PostDataDictionaryPageDetailError = (RemoteServiceErrorResponse);
export type PostDataDictionarySelectDetailData = {
body?: SelectDataDictionaryDetailInput;
};
export type PostDataDictionarySelectDetailResponse = (PagingDataDictionaryDetailOutputPagedResultDto);
export type PostDataDictionarySelectDetailError = (RemoteServiceErrorResponse);
export type PostDataDictionaryCreateData = { export type PostDataDictionaryCreateData = {
body?: CreateDataDictinaryInput; body?: CreateDataDictinaryInput;
}; };

View File

@ -67,5 +67,6 @@
"operationSuccess": "OperationSuccess", "operationSuccess": "OperationSuccess",
"operationFailed": "OperationFailed", "operationFailed": "OperationFailed",
"required": "Required", "required": "Required",
"SearchKeyWords": "搜索关键字",
"loading": "Loading" "loading": "Loading"
} }

View File

@ -68,5 +68,6 @@
"operationSuccess": "操作成功", "operationSuccess": "操作成功",
"operationFailed": "操作失败", "operationFailed": "操作失败",
"required": "必填", "required": "必填",
"SearchKeyWords": "搜索关键字",
"loading": "正在处理" "loading": "正在处理"
} }

7
apps/web-antd/src/types/vue-shim.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
declare module '*.vue' {
import type { DefineComponent } from 'vue';
const component: DefineComponent<{}, {}, any>;
export default component;
}

View File

@ -0,0 +1,155 @@
<script setup lang="ts">
import { ref, watch, computed } from 'vue';
import { Select, Divider, Row } from 'ant-design-vue';
import { ChevronLeft, ChevronRight } from '@vben/icons';
import { postDataDictionarySelectDetail } from '#/api-client';
import { $t } from '#/locales';
import { useDebounceFn } from '@vueuse/core';
interface Props {
value?: string;
typeCode?: string | number | null;
placeholder?: string;
disabled?: boolean;
allowClear?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
placeholder: $t('common.pleaseSelect') + $t('abp.thingModelInfos.StandardFieldName'),
disabled: false,
allowClear: true,
});
const emit = defineEmits<{
'update:value': [string | undefined];
change: [string | undefined];
'item-change': [any | null];
}>();
const VNodes = (_: any, { attrs }: any) => attrs.vnodes;
const options = ref<any[]>([]);
const total = ref(0);
const loading = ref<boolean>(false);
const query = ref({
pageIndex: 1,
pageSize: 10,
filter: '',
});
const maxPage = computed(() => Math.ceil((total.value || 0) / query.value.pageSize) || 1);
const fetchData = async () => {
if (!props.typeCode) {
options.value = [];
total.value = 0;
return;
}
loading.value = true;
try {
const { data } = await postDataDictionarySelectDetail({
body: {
filter: query.value.filter,
typeCode: String(props.typeCode),
pageIndex: query.value.pageIndex,
pageSize: query.value.pageSize,
} as any,
});
const items = Array.isArray(data?.items) ? data!.items : [];
options.value = items.map((item: any) => ({
label: item.displayText,
value: item.code,
...item,
}));
total.value = (data as any)?.totalCount || 0;
} catch (err) {
console.error('获取标准物模型编码失败:', err);
} finally {
loading.value = false;
}
};
const changePage = (next: number) => {
if (next === 1) {
if (query.value.pageIndex >= maxPage.value) return;
query.value.pageIndex += 1;
} else {
if (query.value.pageIndex <= 1) return;
query.value.pageIndex -= 1;
}
fetchData();
};
const onSearch = useDebounceFn((kw: string) => {
query.value.pageIndex = 1;
query.value.filter = kw;
fetchData();
}, 400);
const onChange = (val?: string) => {
emit('update:value', val);
emit('change', val);
if (val) {
const found = options.value.find((o) => o.value === val) || null;
emit('item-change', found);
} else {
emit('item-change', null);
}
};
watch(
() => props.typeCode,
() => {
// reset when type changes, do not auto-fetch until user searches
query.value.pageIndex = 1;
query.value.filter = '';
options.value = [];
total.value = 0;
},
{ immediate: true },
);
</script>
<template>
<Select
:value="value"
:showSearch="true"
:filter-option="false"
:options="options"
:placeholder="placeholder"
:disabled="disabled"
:allowClear="allowClear"
:loading="loading"
@search="onSearch"
@change="onChange"
>
<template #dropdownRender="{ menuNode: menu }">
<v-nodes :vnodes="menu" />
<Divider style="margin: 4px 0" />
<div @mousedown="(e) => e.preventDefault()">
<Row type="flex" justify="space-around" align="middle">
<ChevronLeft
@click="changePage(0)"
:class="{ 'text-gray-400': query.pageIndex <= 1 }"
style="cursor: pointer; width: 16px; height: 16px;"
/>
<div>{{ `${query.pageIndex}/${maxPage}` }}</div>
<ChevronRight
@click="changePage(1)"
:class="{ 'text-gray-400': query.pageIndex >= maxPage }"
style="cursor: pointer; width: 16px; height: 16px;"
/>
</Row>
</div>
</template>
</Select>
</template>
<style lang="less" scoped>
.text-gray-400 {
color: #9ca3af;
cursor: not-allowed !important;
}
</style>

View File

@ -4,72 +4,73 @@ import { z } from '@vben/common-ui';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { getCommonGetSelectList } from '#/api-client';
import { $t } from '#/locales'; import { $t } from '#/locales';
export const querySchema = computed(() => [ export const querySchema = computed(() => [
{ {
component: 'Input', component: 'Input',
fieldName: 'SearchKeyWords', fieldName: 'SearchKeyWords',
label: $t('common.keyword'), label: $t('common.SearchKeyWords'),
}, },
]); ]);
export const tableSchema = computed(() => [ export const tableSchema = computed(() => [
{ {
field: 'filedType', field: 'filedType',
title: '物模型类型', title: $t('abp.thingModelInfos.FiledType'),
minWidth: 120, minWidth: 120,
showOverflow: 'tooltip', showOverflow: 'tooltip',
formatter: ({ cellValue }: { cellValue: any }) => { formatter: ({ cellValue }: { cellValue: any }) => {
const typeMap: Record<string, string> = { const typeMap: Record<string, string> = {
'Property': '属性', Property: '属性',
'Service': '服务', Service: '服务',
'Event': '事件', Event: '事件',
}; };
return typeMap[cellValue] || cellValue; return typeMap[cellValue] || cellValue;
}, },
}, },
{ {
field: 'ioTPlatformRawFieldName', field: 'ioTPlatformRawFieldName',
title: '平台原始字段名', title: $t('abp.thingModelInfos.IoTPlatformRawFieldName'),
minWidth: 150, minWidth: 150,
showOverflow: 'tooltip', showOverflow: 'tooltip',
}, },
{ {
field: 'standardFieldName', field: 'standardFieldName',
title: '标准字段名', title: $t('abp.thingModelInfos.StandardFieldName'),
minWidth: 150, minWidth: 150,
showOverflow: 'tooltip', showOverflow: 'tooltip',
}, },
{ {
field: 'standardFieldDisplayName', field: 'standardFieldDisplayName',
title: '标准字段显示名称', title: $t('abp.thingModelInfos.StandardFieldDisplayName'),
minWidth: 150, minWidth: 150,
showOverflow: 'tooltip', showOverflow: 'tooltip',
}, },
{ {
field: 'standardFieldValueType', field: 'standardFieldValueType',
title: '标准字段值类型', title: $t('abp.thingModelInfos.StandardFieldValueType'),
minWidth: 120, minWidth: 120,
showOverflow: 'tooltip', showOverflow: 'tooltip',
formatter: ({ cellValue }: { cellValue: any }) => { formatter: ({ cellValue }: { cellValue: any }) => {
const typeMap: Record<string, string> = { const typeMap: Record<string, string> = {
'String': '字符串', String: '字符串',
'Int32': '整数', Int32: '整数',
'Int64': '长整数', Int64: '长整数',
'Float': '浮点数', Float: '浮点数',
'Double': '双精度', Double: '双精度',
'Boolean': '布尔值', Boolean: '布尔值',
'DateTime': '日期时间', DateTime: '日期时间',
'Object': 'JSON对象', Object: 'JSON对象',
'Array': '数组', Array: '数组',
}; };
return typeMap[cellValue] || cellValue; return typeMap[cellValue] || cellValue;
}, },
}, },
{ {
field: 'isValueNeedConvert', field: 'isValueNeedConvert',
title: '需要值类型转换', title: $t('abp.thingModelInfos.IsValueNeedConvert'),
minWidth: 120, minWidth: 120,
slots: { default: 'isValueNeedConvert' }, slots: { default: 'isValueNeedConvert' },
}, },
@ -93,140 +94,218 @@ export const tableSchema = computed(() => [
// 添加物模型表单schema // 添加物模型表单schema
export const addThingModelFormSchema = computed(() => [ export const addThingModelFormSchema = computed(() => [
{ {
component: 'Select', component: 'ApiSelect',
fieldName: 'filedType', fieldName: 'filedType',
label: '物模型类型', label: $t('abp.thingModelInfos.FiledType'),
rules: z.string().min(1, $t('common.required')), rules: z.preprocess((v) => (v == null ? '' : v), z.string().min(1, $t('common.required'))),
componentProps: { componentProps: {
options: [ api: getCommonGetSelectList,
{ label: '属性', value: 'Property' }, params: {
{ label: '服务', value: 'Service' }, query: {
{ label: '事件', value: 'Event' }, typeName: 'DataDictionaryTypeConst',
], },
placeholder: '请选择物模型类型', },
labelField: 'value',
valueField: 'key',
optionsPropName: 'options',
immediate: true,
allowClear: true,
placeholder:
$t('common.pleaseSelect') + $t('abp.thingModelInfos.FiledType'),
afterFetch: (res: any) => {
// 确保返回的是数组格式
if (Array.isArray(res)) {
return res;
}
if (res && Array.isArray(res.items)) {
return res.items;
}
if (res && Array.isArray(res.data)) {
return res.data;
}
return [];
},
}, },
}, },
{ {
component: 'Input', component: 'Input',
fieldName: 'ioTPlatformRawFieldName', fieldName: 'ioTPlatformRawFieldName',
label: '平台原始字段名', label: $t('abp.thingModelInfos.IoTPlatformRawFieldName'),
rules: z.string().min(1, $t('common.required')), rules: z.preprocess((v) => (v == null ? '' : v), z.string().min(1, $t('common.required'))),
componentProps: { componentProps: {
placeholder: '请输入物联网平台中的原始字段名', placeholder:
$t('common.pleaseInput') +
$t('abp.thingModelInfos.IoTPlatformRawFieldName'),
}, },
}, },
{ {
component: 'Input', component: 'StandardThingModelCodeSelect',
fieldName: 'standardFieldName', fieldName: 'standardFieldName',
label: '标准字段名', label: $t('abp.thingModelInfos.StandardFieldName'),
rules: z.string().min(1, $t('common.required')), rules: z.preprocess((v) => (v == null ? '' : v), z.string().min(1, $t('common.required'))),
dependencies: {
show(values: any) {
return !!values?.filedType;
},
triggerFields: ['filedType'],
},
componentProps: { componentProps: {
placeholder: '请输入管理后台产品标准的字段名', // 传入联动的类型编码
typeCode: (formValues: any) => formValues?.filedType,
placeholder:
$t('common.pleaseSelect') + $t('abp.thingModelInfos.StandardFieldName'),
}, },
}, },
{ {
component: 'Select', component: 'ApiSelect',
fieldName: 'standardFieldValueType', fieldName: 'standardFieldValueType',
label: '标准字段值类型', label: $t('abp.thingModelInfos.StandardFieldValueType'),
rules: z.string().min(1, $t('common.required')), rules: z.preprocess((v) => (v == null ? '' : v), z.string().min(1, $t('common.required'))),
componentProps: { componentProps: {
options: [ api: getCommonGetSelectList,
{ label: '字符串', value: 'String' }, params: {
{ label: '整数', value: 'Int32' }, query: {
{ label: '长整数', value: 'Int64' }, typeName: 'StandardThingModelDataTypeEnum',
{ label: '浮点数', value: 'Float' }, },
{ label: '双精度', value: 'Double' }, },
{ label: '布尔值', value: 'Boolean' }, labelField: 'value',
{ label: '日期时间', value: 'DateTime' }, valueField: 'secondValue',
{ label: 'JSON对象', value: 'Object' }, optionsPropName: 'options',
{ label: '数组', value: 'Array' }, immediate: true,
], placeholder:
placeholder: '请选择标准字段值类型', $t('common.pleaseSelect') +
$t('abp.thingModelInfos.StandardFieldValueType'),
afterFetch: (res: any) => {
if (Array.isArray(res)) return res;
if (res && Array.isArray(res.items)) return res.items;
if (res && Array.isArray(res.data)) return res.data;
return [];
},
}, },
}, },
{ {
component: 'Input', component: 'Input',
fieldName: 'standardFieldDisplayName', fieldName: 'standardFieldDisplayName',
label: '标准字段显示名称', label: $t('abp.thingModelInfos.StandardFieldDisplayName'),
rules: z.string().min(1, $t('common.required')), rules: z.preprocess((v) => (v == null ? '' : v), z.string().min(1, $t('common.required'))),
componentProps: { componentProps: {
placeholder: '请输入标准字段的显示名称', placeholder:
$t('common.pleaseInput') +
$t('abp.thingModelInfos.StandardFieldDisplayName'),
}, },
}, },
{ {
component: 'Switch', component: 'Switch',
fieldName: 'isValueNeedConvert', fieldName: 'isValueNeedConvert',
label: '是否需要值类型转换', label: $t('abp.thingModelInfos.IsValueNeedConvert'),
defaultValue: false, defaultValue: false,
componentProps: {
style: { width: 'auto' },
},
}, },
]); ]);
// 编辑物模型表单schema // 编辑物模型表单schema
export const editThingModelFormSchema = computed(() => [ export const editThingModelFormSchema = computed(() => [
{ {
component: 'Select', component: 'ApiSelect',
fieldName: 'filedType', fieldName: 'filedType',
label: '物模型类型', label: $t('abp.thingModelInfos.FiledType'),
rules: z.string().min(1, $t('common.required')), rules: z.preprocess((v) => (v == null ? '' : v), z.string().min(1, $t('common.required'))),
componentProps: { componentProps: {
options: [ api: getCommonGetSelectList,
{ label: '属性', value: 'Property' }, params: {
{ label: '服务', value: 'Service' }, query: {
{ label: '事件', value: 'Event' }, typeName: 'DataDictionaryTypeConst',
], },
placeholder: '请选择物模型类型', },
labelField: 'value',
valueField: 'value',
optionsPropName: 'options',
immediate: true,
placeholder: $t('abp.thingModelInfos.PleaseSelectFiledType'),
afterFetch: (res: any) => {
// 确保返回的是数组格式
if (Array.isArray(res)) {
return res;
}
if (res && Array.isArray(res.items)) {
return res.items;
}
if (res && Array.isArray(res.data)) {
return res.data;
}
return [];
},
}, },
}, },
{ {
component: 'Input', component: 'Input',
fieldName: 'ioTPlatformRawFieldName', fieldName: 'ioTPlatformRawFieldName',
label: '平台原始字段名', label: $t('abp.thingModelInfos.IoTPlatformRawFieldName'),
rules: z.string().min(1, $t('common.required')), rules: z.preprocess((v) => (v == null ? '' : v), z.string().min(1, $t('common.required'))),
componentProps: { componentProps: {
placeholder: '请输入物联网平台中的原始字段名', placeholder: $t('abp.thingModelInfos.PleaseInputIoTPlatformRawFieldName'),
}, },
}, },
{ {
component: 'Input', component: 'StandardThingModelCodeSelect',
fieldName: 'standardFieldName', fieldName: 'standardFieldName',
label: '标准字段名', label: $t('abp.thingModelInfos.StandardFieldName'),
rules: z.string().min(1, $t('common.required')), rules: z.preprocess((v) => (v == null ? '' : v), z.string().min(1, $t('common.required'))),
dependencies: {
show(values: any) {
return !!values?.filedType;
},
triggerFields: ['filedType'],
},
componentProps: { componentProps: {
placeholder: '请输入管理后台产品标准的字段名', typeCode: (formValues: any) => formValues?.filedType,
placeholder: $t('abp.thingModelInfos.PleaseSelectStandardFieldName'),
}, },
}, },
{ {
component: 'Select', component: 'ApiSelect',
fieldName: 'standardFieldValueType', fieldName: 'standardFieldValueType',
label: '标准字段值类型', label: $t('abp.thingModelInfos.StandardFieldValueType'),
rules: z.string().min(1, $t('common.required')), rules: z.string().min(1, $t('common.required')),
componentProps: { componentProps: {
options: [ api: getCommonGetSelectList,
{ label: '字符串', value: 'String' }, params: {
{ label: '整数', value: 'Int32' }, query: {
{ label: '长整数', value: 'Int64' }, typeName: 'StandardThingModelDataTypeEnum',
{ label: '浮点数', value: 'Float' }, },
{ label: '双精度', value: 'Double' }, },
{ label: '布尔值', value: 'Boolean' }, labelField: 'value',
{ label: '日期时间', value: 'DateTime' }, valueField: 'value',
{ label: 'JSON对象', value: 'Object' }, optionsPropName: 'options',
{ label: '数组', value: 'Array' }, immediate: true,
], placeholder: $t('abp.thingModelInfos.PleaseSelectStandardFieldValueType'),
placeholder: '请选择标准字段值类型', afterFetch: (res: any) => {
if (Array.isArray(res)) return res;
if (res && Array.isArray(res.items)) return res.items;
if (res && Array.isArray(res.data)) return res.data;
return [];
},
}, },
}, },
{ {
component: 'Input', component: 'Input',
fieldName: 'standardFieldDisplayName', fieldName: 'standardFieldDisplayName',
label: '标准字段显示名称', label: $t('abp.thingModelInfos.StandardFieldDisplayName'),
rules: z.string().min(1, $t('common.required')), rules: z.string().min(1, $t('common.required')),
componentProps: { componentProps: {
placeholder: '请输入标准字段的显示名称', placeholder: $t(
'abp.thingModelInfos.PleaseInputStandardFieldDisplayName',
),
}, },
}, },
{ {
component: 'Switch', component: 'Switch',
fieldName: 'isValueNeedConvert', fieldName: 'isValueNeedConvert',
label: '是否需要值类型转换', label: $t('abp.thingModelInfos.IsValueNeedConvert'),
componentProps: {
style: { width: 'auto' },
},
}, },
]); ]);