fix: 修复 iot 模块 ele 端迁移阻塞的类型 / 字段问题(P0)

- ele property-selector / device-control-config,改用 ThingModelApi 命名空间引用
- ele api/iot/thingmodel,补 ThingModelTSL 类型,收敛 getThingModelTSLByProductId 返回值
- ele api/iot/rule/scene,SceneRule 补 lastTriggeredTime 字段
- ele views/iot/rule/scene/data.ts,useGridColumns 对齐 antd 的 5 列结构
- ele views/iot/home/modules/message-trend-card,ElSelect 改用 ElOption 子节点
- ele / antd api/iot/rule/scene,Action.params 类型 Record<string, any> 改为 string

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
pull/345/head
YunaiV 2026-05-21 10:20:30 +08:00
parent f02a5975b8
commit 0d175cbe9c
7 changed files with 68 additions and 61 deletions

View File

@ -46,7 +46,7 @@ export namespace RuleSceneApi {
identifier?: string;
value?: any;
alertConfigId?: number;
params?: Record<string, any>;
params?: string;
}
}

View File

@ -11,6 +11,7 @@ export namespace RuleSceneApi {
status?: number;
triggers?: Trigger[];
actions?: Action[];
lastTriggeredTime?: Date;
createTime?: Date;
}
@ -46,7 +47,7 @@ export namespace RuleSceneApi {
identifier?: string;
value?: any;
alertConfigId?: number;
params?: Record<string, any>;
params?: string;
}
}

View File

@ -68,6 +68,15 @@ export namespace ThingModelApi {
dataSpecsList?: any[];
}
/** IoT 物模型 TSL 响应 */
export interface ThingModelTSL {
productId?: number;
productKey?: string;
properties?: Property[];
events?: Event[];
services?: Service[];
}
/** IoT 数据定义(数值型) */
export interface DataSpecsNumberData {
min?: number | string;
@ -236,7 +245,10 @@ export function deleteThingModel(id: number) {
/** 获取物模型 TSL */
export function getThingModelTSLByProductId(productId: number) {
return requestClient.get<any>('/iot/thing-model/get-tsl', {
params: { productId },
});
return requestClient.get<ThingModelApi.ThingModelTSL>(
'/iot/thing-model/get-tsl',
{
params: { productId },
},
);
}

View File

@ -10,7 +10,7 @@ import { getDictOptions } from '@vben/hooks';
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
import dayjs from 'dayjs';
import { ElCard, ElEmpty, ElSelect } from 'element-plus';
import { ElCard, ElEmpty, ElOption, ElSelect } from 'element-plus';
import { getDeviceMessageSummaryByDate } from '#/api/iot/statistics';
import ShortcutDateRangePicker from '#/components/shortcut-date-range-picker/shortcut-date-range-picker.vue';
@ -145,11 +145,17 @@ onMounted(() => {
<span class="text-sm text-gray-500">时间间隔</span>
<ElSelect
v-model="queryParams.interval"
:options="intervalOptions"
placeholder="间隔类型"
:style="{ width: '80px' }"
@change="handleIntervalChange"
/>
>
<ElOption
v-for="opt in intervalOptions"
:key="opt.value"
:label="opt.label"
:value="opt.value"
/>
</ElSelect>
</div>
</div>
</div>

View File

@ -89,49 +89,45 @@ export function useGridFormSchema(): VbenFormSchema[] {
export function useGridColumns(): VxeTableGridOptions['columns'] {
return [
{ type: 'checkbox', width: 40 },
{
field: 'id',
title: '规则编号',
minWidth: 80,
},
{
field: 'name',
title: '规则名称',
minWidth: 150,
},
{
field: 'description',
title: '规则描述',
minWidth: 200,
align: 'left',
showOverflow: false,
slots: { default: 'name' },
},
{
field: 'status',
title: '规则状态',
minWidth: 100,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.COMMON_STATUS },
},
field: 'triggers',
title: '触发条件',
minWidth: 280,
align: 'left',
showOverflow: false,
slots: { default: 'triggers' },
},
{
field: 'actionCount',
title: '执行动作数',
minWidth: 100,
field: 'actions',
title: '执行动作',
minWidth: 250,
align: 'left',
showOverflow: false,
slots: { default: 'actionsCol' },
},
{
field: 'executeCount',
title: '执行次数',
minWidth: 100,
field: 'lastTriggeredTime',
title: '最近触发',
width: 180,
slots: { default: 'lastTriggeredTime' },
},
{
field: 'createTime',
title: '创建时间',
minWidth: 180,
width: 180,
formatter: 'formatDateTime',
},
{
title: '操作',
width: 240,
width: 210,
fixed: 'right',
slots: { default: 'actions' },
},

View File

@ -1,10 +1,7 @@
<!-- 设备控制配置组件 -->
<script setup lang="ts">
import type { Action } from '#/api/iot/rule/scene';
import type {
ThingModelProperty,
ThingModelService,
} from '#/api/iot/thingmodel';
import type { ThingModelApi } from '#/api/iot/thingmodel';
import { computed, onMounted, ref, watch } from 'vue';
@ -44,10 +41,10 @@ const emit = defineEmits<{
const action = useVModel(props, 'modelValue', emit);
const thingModelProperties = ref<ThingModelProperty[]>([]); //
const thingModelProperties = ref<ThingModelApi.Property[]>([]); //
const loadingThingModel = ref(false); //
const selectedService = ref<null | ThingModelService>(null); //
const serviceList = ref<ThingModelService[]>([]); //
const selectedService = ref<null | ThingModelApi.Service>(null); //
const serviceList = ref<ThingModelApi.Service[]>([]); //
const loadingServices = ref(false); //
//
@ -178,7 +175,7 @@ async function loadThingModelProperties(productId: number) {
// accessMode 'w'
thingModelProperties.value = tslData.properties.filter(
(property: ThingModelProperty) =>
(property: ThingModelApi.Property) =>
property.accessMode &&
(property.accessMode === IoTThingModelAccessModeEnum.READ_WRITE.value ||
property.accessMode === IoTThingModelAccessModeEnum.WRITE_ONLY.value),

View File

@ -1,11 +1,6 @@
<!-- 属性选择器组件 -->
<script setup lang="ts">
import type {
ThingModelApi,
ThingModelEvent,
ThingModelProperty,
ThingModelService,
} from '#/api/iot/thingmodel';
import type { ThingModelApi } from '#/api/iot/thingmodel';
import { computed, ref, watch } from 'vue';
@ -61,18 +56,18 @@ interface PropertySelectorItem {
range?: string;
eventType?: string;
callType?: string;
inputParams?: ThingModelParam[];
outputParams?: ThingModelParam[];
property?: ThingModelProperty;
event?: ThingModelEvent;
service?: ThingModelService;
inputParams?: ThingModelApi.Param[];
outputParams?: ThingModelApi.Param[];
property?: ThingModelApi.Property;
event?: ThingModelApi.Event;
service?: ThingModelApi.Service;
}
const localValue = useVModel(props, 'modelValue', emit);
const loading = ref(false); //
const propertyList = ref<ThingModelApi.Property[]>([]); //
const thingModelTSL = ref<null | ThingModelApi.ThingModel>(null); // TSL
const propertyList = ref<PropertySelectorItem[]>([]); //
const thingModelTSL = ref<null | ThingModelApi.ThingModelTSL>(null); // TSL
//
const propertyGroups = computed(() => {
@ -169,10 +164,10 @@ function parseThingModelData() {
if (tsl.properties && Array.isArray(tsl.properties)) {
tsl.properties.forEach((prop) => {
properties.push({
identifier: prop.identifier,
name: prop.name,
identifier: prop.identifier!,
name: prop.name!,
description: prop.description,
dataType: prop.dataType,
dataType: prop.dataType!,
type: IoTThingModelTypeEnum.PROPERTY,
accessMode: prop.accessMode,
required: prop.required,
@ -187,8 +182,8 @@ function parseThingModelData() {
if (tsl.events && Array.isArray(tsl.events)) {
tsl.events.forEach((event) => {
properties.push({
identifier: event.identifier,
name: event.name,
identifier: event.identifier!,
name: event.name!,
description: event.description,
dataType: 'struct',
type: IoTThingModelTypeEnum.EVENT,
@ -204,8 +199,8 @@ function parseThingModelData() {
if (tsl.services && Array.isArray(tsl.services)) {
tsl.services.forEach((service) => {
properties.push({
identifier: service.identifier,
name: service.name,
identifier: service.identifier!,
name: service.name!,
description: service.description,
dataType: 'struct',
type: IoTThingModelTypeEnum.SERVICE,