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
parent
f02a5975b8
commit
0d175cbe9c
|
|
@ -46,7 +46,7 @@ export namespace RuleSceneApi {
|
||||||
identifier?: string;
|
identifier?: string;
|
||||||
value?: any;
|
value?: any;
|
||||||
alertConfigId?: number;
|
alertConfigId?: number;
|
||||||
params?: Record<string, any>;
|
params?: string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ export namespace RuleSceneApi {
|
||||||
status?: number;
|
status?: number;
|
||||||
triggers?: Trigger[];
|
triggers?: Trigger[];
|
||||||
actions?: Action[];
|
actions?: Action[];
|
||||||
|
lastTriggeredTime?: Date;
|
||||||
createTime?: Date;
|
createTime?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,7 +47,7 @@ export namespace RuleSceneApi {
|
||||||
identifier?: string;
|
identifier?: string;
|
||||||
value?: any;
|
value?: any;
|
||||||
alertConfigId?: number;
|
alertConfigId?: number;
|
||||||
params?: Record<string, any>;
|
params?: string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,15 @@ export namespace ThingModelApi {
|
||||||
dataSpecsList?: any[];
|
dataSpecsList?: any[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** IoT 物模型 TSL 响应 */
|
||||||
|
export interface ThingModelTSL {
|
||||||
|
productId?: number;
|
||||||
|
productKey?: string;
|
||||||
|
properties?: Property[];
|
||||||
|
events?: Event[];
|
||||||
|
services?: Service[];
|
||||||
|
}
|
||||||
|
|
||||||
/** IoT 数据定义(数值型) */
|
/** IoT 数据定义(数值型) */
|
||||||
export interface DataSpecsNumberData {
|
export interface DataSpecsNumberData {
|
||||||
min?: number | string;
|
min?: number | string;
|
||||||
|
|
@ -236,7 +245,10 @@ export function deleteThingModel(id: number) {
|
||||||
|
|
||||||
/** 获取物模型 TSL */
|
/** 获取物模型 TSL */
|
||||||
export function getThingModelTSLByProductId(productId: number) {
|
export function getThingModelTSLByProductId(productId: number) {
|
||||||
return requestClient.get<any>('/iot/thing-model/get-tsl', {
|
return requestClient.get<ThingModelApi.ThingModelTSL>(
|
||||||
params: { productId },
|
'/iot/thing-model/get-tsl',
|
||||||
});
|
{
|
||||||
|
params: { productId },
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import { getDictOptions } from '@vben/hooks';
|
||||||
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
|
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
|
||||||
|
|
||||||
import dayjs from 'dayjs';
|
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 { getDeviceMessageSummaryByDate } from '#/api/iot/statistics';
|
||||||
import ShortcutDateRangePicker from '#/components/shortcut-date-range-picker/shortcut-date-range-picker.vue';
|
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>
|
<span class="text-sm text-gray-500">时间间隔</span>
|
||||||
<ElSelect
|
<ElSelect
|
||||||
v-model="queryParams.interval"
|
v-model="queryParams.interval"
|
||||||
:options="intervalOptions"
|
|
||||||
placeholder="间隔类型"
|
placeholder="间隔类型"
|
||||||
:style="{ width: '80px' }"
|
:style="{ width: '80px' }"
|
||||||
@change="handleIntervalChange"
|
@change="handleIntervalChange"
|
||||||
/>
|
>
|
||||||
|
<ElOption
|
||||||
|
v-for="opt in intervalOptions"
|
||||||
|
:key="opt.value"
|
||||||
|
:label="opt.label"
|
||||||
|
:value="opt.value"
|
||||||
|
/>
|
||||||
|
</ElSelect>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -89,49 +89,45 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
||||||
export function useGridColumns(): VxeTableGridOptions['columns'] {
|
export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||||
return [
|
return [
|
||||||
{ type: 'checkbox', width: 40 },
|
{ type: 'checkbox', width: 40 },
|
||||||
{
|
|
||||||
field: 'id',
|
|
||||||
title: '规则编号',
|
|
||||||
minWidth: 80,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
field: 'name',
|
field: 'name',
|
||||||
title: '规则名称',
|
title: '规则名称',
|
||||||
minWidth: 150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'description',
|
|
||||||
title: '规则描述',
|
|
||||||
minWidth: 200,
|
minWidth: 200,
|
||||||
|
align: 'left',
|
||||||
|
showOverflow: false,
|
||||||
|
slots: { default: 'name' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'status',
|
field: 'triggers',
|
||||||
title: '规则状态',
|
title: '触发条件',
|
||||||
minWidth: 100,
|
minWidth: 280,
|
||||||
cellRender: {
|
align: 'left',
|
||||||
name: 'CellDict',
|
showOverflow: false,
|
||||||
props: { type: DICT_TYPE.COMMON_STATUS },
|
slots: { default: 'triggers' },
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'actionCount',
|
field: 'actions',
|
||||||
title: '执行动作数',
|
title: '执行动作',
|
||||||
minWidth: 100,
|
minWidth: 250,
|
||||||
|
align: 'left',
|
||||||
|
showOverflow: false,
|
||||||
|
slots: { default: 'actionsCol' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'executeCount',
|
field: 'lastTriggeredTime',
|
||||||
title: '执行次数',
|
title: '最近触发',
|
||||||
minWidth: 100,
|
width: 180,
|
||||||
|
slots: { default: 'lastTriggeredTime' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'createTime',
|
field: 'createTime',
|
||||||
title: '创建时间',
|
title: '创建时间',
|
||||||
minWidth: 180,
|
width: 180,
|
||||||
formatter: 'formatDateTime',
|
formatter: 'formatDateTime',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 240,
|
width: 210,
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
slots: { default: 'actions' },
|
slots: { default: 'actions' },
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
<!-- 设备控制配置组件 -->
|
<!-- 设备控制配置组件 -->
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Action } from '#/api/iot/rule/scene';
|
import type { Action } from '#/api/iot/rule/scene';
|
||||||
import type {
|
import type { ThingModelApi } from '#/api/iot/thingmodel';
|
||||||
ThingModelProperty,
|
|
||||||
ThingModelService,
|
|
||||||
} from '#/api/iot/thingmodel';
|
|
||||||
|
|
||||||
import { computed, onMounted, ref, watch } from 'vue';
|
import { computed, onMounted, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
|
@ -44,10 +41,10 @@ const emit = defineEmits<{
|
||||||
|
|
||||||
const action = useVModel(props, 'modelValue', emit);
|
const action = useVModel(props, 'modelValue', emit);
|
||||||
|
|
||||||
const thingModelProperties = ref<ThingModelProperty[]>([]); // 物模型属性列表
|
const thingModelProperties = ref<ThingModelApi.Property[]>([]); // 物模型属性列表
|
||||||
const loadingThingModel = ref(false); // 物模型加载状态
|
const loadingThingModel = ref(false); // 物模型加载状态
|
||||||
const selectedService = ref<null | ThingModelService>(null); // 选中的服务对象
|
const selectedService = ref<null | ThingModelApi.Service>(null); // 选中的服务对象
|
||||||
const serviceList = ref<ThingModelService[]>([]); // 服务列表
|
const serviceList = ref<ThingModelApi.Service[]>([]); // 服务列表
|
||||||
const loadingServices = ref(false); // 服务加载状态
|
const loadingServices = ref(false); // 服务加载状态
|
||||||
|
|
||||||
// 参数值的计算属性,用于双向绑定
|
// 参数值的计算属性,用于双向绑定
|
||||||
|
|
@ -178,7 +175,7 @@ async function loadThingModelProperties(productId: number) {
|
||||||
|
|
||||||
// 过滤出可写的属性(accessMode 包含 'w')
|
// 过滤出可写的属性(accessMode 包含 'w')
|
||||||
thingModelProperties.value = tslData.properties.filter(
|
thingModelProperties.value = tslData.properties.filter(
|
||||||
(property: ThingModelProperty) =>
|
(property: ThingModelApi.Property) =>
|
||||||
property.accessMode &&
|
property.accessMode &&
|
||||||
(property.accessMode === IoTThingModelAccessModeEnum.READ_WRITE.value ||
|
(property.accessMode === IoTThingModelAccessModeEnum.READ_WRITE.value ||
|
||||||
property.accessMode === IoTThingModelAccessModeEnum.WRITE_ONLY.value),
|
property.accessMode === IoTThingModelAccessModeEnum.WRITE_ONLY.value),
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,6 @@
|
||||||
<!-- 属性选择器组件 -->
|
<!-- 属性选择器组件 -->
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type {
|
import type { ThingModelApi } from '#/api/iot/thingmodel';
|
||||||
ThingModelApi,
|
|
||||||
ThingModelEvent,
|
|
||||||
ThingModelProperty,
|
|
||||||
ThingModelService,
|
|
||||||
} from '#/api/iot/thingmodel';
|
|
||||||
|
|
||||||
import { computed, ref, watch } from 'vue';
|
import { computed, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
|
@ -61,18 +56,18 @@ interface PropertySelectorItem {
|
||||||
range?: string;
|
range?: string;
|
||||||
eventType?: string;
|
eventType?: string;
|
||||||
callType?: string;
|
callType?: string;
|
||||||
inputParams?: ThingModelParam[];
|
inputParams?: ThingModelApi.Param[];
|
||||||
outputParams?: ThingModelParam[];
|
outputParams?: ThingModelApi.Param[];
|
||||||
property?: ThingModelProperty;
|
property?: ThingModelApi.Property;
|
||||||
event?: ThingModelEvent;
|
event?: ThingModelApi.Event;
|
||||||
service?: ThingModelService;
|
service?: ThingModelApi.Service;
|
||||||
}
|
}
|
||||||
|
|
||||||
const localValue = useVModel(props, 'modelValue', emit);
|
const localValue = useVModel(props, 'modelValue', emit);
|
||||||
|
|
||||||
const loading = ref(false); // 加载状态
|
const loading = ref(false); // 加载状态
|
||||||
const propertyList = ref<ThingModelApi.Property[]>([]); // 属性列表
|
const propertyList = ref<PropertySelectorItem[]>([]); // 属性列表
|
||||||
const thingModelTSL = ref<null | ThingModelApi.ThingModel>(null); // 物模型TSL数据
|
const thingModelTSL = ref<null | ThingModelApi.ThingModelTSL>(null); // 物模型 TSL 数据
|
||||||
|
|
||||||
// 计算属性:属性分组
|
// 计算属性:属性分组
|
||||||
const propertyGroups = computed(() => {
|
const propertyGroups = computed(() => {
|
||||||
|
|
@ -169,10 +164,10 @@ function parseThingModelData() {
|
||||||
if (tsl.properties && Array.isArray(tsl.properties)) {
|
if (tsl.properties && Array.isArray(tsl.properties)) {
|
||||||
tsl.properties.forEach((prop) => {
|
tsl.properties.forEach((prop) => {
|
||||||
properties.push({
|
properties.push({
|
||||||
identifier: prop.identifier,
|
identifier: prop.identifier!,
|
||||||
name: prop.name,
|
name: prop.name!,
|
||||||
description: prop.description,
|
description: prop.description,
|
||||||
dataType: prop.dataType,
|
dataType: prop.dataType!,
|
||||||
type: IoTThingModelTypeEnum.PROPERTY,
|
type: IoTThingModelTypeEnum.PROPERTY,
|
||||||
accessMode: prop.accessMode,
|
accessMode: prop.accessMode,
|
||||||
required: prop.required,
|
required: prop.required,
|
||||||
|
|
@ -187,8 +182,8 @@ function parseThingModelData() {
|
||||||
if (tsl.events && Array.isArray(tsl.events)) {
|
if (tsl.events && Array.isArray(tsl.events)) {
|
||||||
tsl.events.forEach((event) => {
|
tsl.events.forEach((event) => {
|
||||||
properties.push({
|
properties.push({
|
||||||
identifier: event.identifier,
|
identifier: event.identifier!,
|
||||||
name: event.name,
|
name: event.name!,
|
||||||
description: event.description,
|
description: event.description,
|
||||||
dataType: 'struct',
|
dataType: 'struct',
|
||||||
type: IoTThingModelTypeEnum.EVENT,
|
type: IoTThingModelTypeEnum.EVENT,
|
||||||
|
|
@ -204,8 +199,8 @@ function parseThingModelData() {
|
||||||
if (tsl.services && Array.isArray(tsl.services)) {
|
if (tsl.services && Array.isArray(tsl.services)) {
|
||||||
tsl.services.forEach((service) => {
|
tsl.services.forEach((service) => {
|
||||||
properties.push({
|
properties.push({
|
||||||
identifier: service.identifier,
|
identifier: service.identifier!,
|
||||||
name: service.name,
|
name: service.name!,
|
||||||
description: service.description,
|
description: service.description,
|
||||||
dataType: 'struct',
|
dataType: 'struct',
|
||||||
type: IoTThingModelTypeEnum.SERVICE,
|
type: IoTThingModelTypeEnum.SERVICE,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue