Quellcode durchsuchen

perf:【IoT 物联网】场景联动触发条件属性选择器组件优化

puhui999 vor 10 Monaten
Ursprung
Commit
f31d034c79

+ 468 - 0
src/views/iot/rule/scene/IotThingModelTSLRespVO数据结构文档.md

@@ -0,0 +1,468 @@
+# IotThingModelTSLRespVO 数据结构文档
+
+## 概述
+
+`IotThingModelTSLRespVO` 是IoT产品物模型TSL(Thing Specification Language)的响应数据结构,用于返回完整的产品物模型定义,包括属性、事件和服务的详细信息。TSL是阿里云IoT平台定义的一套物模型描述规范。
+
+## 主体数据结构
+
+### IotThingModelTSLRespVO
+
+```typescript
+interface IotThingModelTSLRespVO {
+  productId: number;                    // 产品编号(必填)
+  productKey: string;                   // 产品标识(必填)
+  properties: ThingModelProperty[];     // 属性列表(必填)
+  events: ThingModelEvent[];           // 事件列表(必填)
+  services: ThingModelService[];       // 服务列表(必填)
+}
+```
+
+**字段说明:**
+- `productId`: 产品编号,唯一标识一个IoT产品
+- `productKey`: 产品标识符,用于设备连接和识别
+- `properties`: 设备属性列表,描述设备的状态信息
+- `events`: 设备事件列表,描述设备主动上报的事件
+- `services`: 设备服务列表,描述可以调用的设备功能
+
+## 属性数据结构 (ThingModelProperty)
+
+### 基本结构
+
+```typescript
+interface ThingModelProperty {
+  identifier: string;                   // 属性标识符(必填)
+  name: string;                        // 属性名称(必填)
+  accessMode: string;                  // 访问模式(必填)
+  required?: boolean;                  // 是否必选
+  dataType: string;                    // 数据类型(必填)
+  dataSpecs?: ThingModelDataSpecs;     // 数据规范(非列表型)
+  dataSpecsList?: ThingModelDataSpecs[]; // 数据规范(列表型)
+}
+```
+
+### 字段详细说明
+
+#### identifier(属性标识符)
+- **类型**: `string`
+- **必填**: 是
+- **格式**: 正则表达式 `^[a-zA-Z][a-zA-Z0-9_]{0,31}$`
+- **说明**: 只能由字母、数字和下划线组成,必须以字母开头,长度不超过32个字符
+- **示例**: `"temperature"`, `"humidity"`, `"power_status"`
+
+#### name(属性名称)
+- **类型**: `string`
+- **必填**: 是
+- **说明**: 属性的显示名称,用于界面展示
+- **示例**: `"温度"`, `"湿度"`, `"电源状态"`
+
+#### accessMode(访问模式)
+- **类型**: `string`
+- **必填**: 是
+- **枚举值**:
+  - `"r"`: 只读,设备只能上报,平台不能下发
+  - `"rw"`: 读写,设备可以上报,平台也可以下发
+- **示例**: `"r"`, `"rw"`
+
+#### dataType(数据类型)
+- **类型**: `string`
+- **必填**: 是
+- **枚举值**:
+  - `"int"`: 整数型
+  - `"float"`: 单精度浮点型
+  - `"double"`: 双精度浮点型
+  - `"enum"`: 枚举型
+  - `"bool"`: 布尔型
+  - `"text"`: 文本型
+  - `"date"`: 时间型
+  - `"struct"`: 结构体型
+  - `"array"`: 数组型
+
+## 事件数据结构 (ThingModelEvent)
+
+### 基本结构
+
+```typescript
+interface ThingModelEvent {
+  identifier: string;                   // 事件标识符(必填)
+  name: string;                        // 事件名称(必填)
+  required?: boolean;                  // 是否必选
+  type: string;                        // 事件类型(必填)
+  outputParams?: ThingModelParam[];    // 输出参数
+  method?: string;                     // 执行方法
+}
+```
+
+### 字段详细说明
+
+#### type(事件类型)
+- **类型**: `string`
+- **必填**: 是
+- **枚举值**:
+  - `"info"`: 信息事件
+  - `"alert"`: 告警事件
+  - `"error"`: 故障事件
+
+#### outputParams(输出参数)
+- **类型**: `ThingModelParam[]`
+- **必填**: 否
+- **说明**: 事件触发时返回的参数信息
+
+## 服务数据结构 (ThingModelService)
+
+### 基本结构
+
+```typescript
+interface ThingModelService {
+  identifier: string;                   // 服务标识符(必填)
+  name: string;                        // 服务名称(必填)
+  required?: boolean;                  // 是否必选
+  callType: string;                    // 调用类型(必填)
+  inputParams?: ThingModelParam[];     // 输入参数
+  outputParams?: ThingModelParam[];    // 输出参数
+  method?: string;                     // 执行方法
+}
+```
+
+### 字段详细说明
+
+#### callType(调用类型)
+- **类型**: `string`
+- **必填**: 是
+- **枚举值**:
+  - `"async"`: 异步调用
+  - `"sync"`: 同步调用
+
+## 参数数据结构 (ThingModelParam)
+
+### 基本结构
+
+```typescript
+interface ThingModelParam {
+  identifier: string;                   // 参数标识符(必填)
+  name: string;                        // 参数名称(必填)
+  direction: string;                   // 参数方向(必填)
+  paraOrder?: number;                  // 参数序号
+  dataType: string;                    // 数据类型(必填)
+  dataSpecs?: ThingModelDataSpecs;     // 数据规范(非列表型)
+  dataSpecsList?: ThingModelDataSpecs[]; // 数据规范(列表型)
+}
+```
+
+### 字段详细说明
+
+#### direction(参数方向)
+- **类型**: `string`
+- **必填**: 是
+- **枚举值**:
+  - `"input"`: 输入参数
+  - `"output"`: 输出参数
+
+## 数据规范结构 (ThingModelDataSpecs)
+
+数据规范是一个抽象基类,根据不同的数据类型有不同的具体实现:
+
+### 1. 数值型数据规范 (ThingModelNumericDataSpec)
+
+适用于 `int`、`float`、`double` 类型:
+
+```typescript
+interface ThingModelNumericDataSpec {
+  dataType: "int" | "float" | "double";
+  max: string;                         // 最大值(必填)
+  min: string;                         // 最小值(必填)
+  step: string;                        // 步长(必填)
+  precise?: string;                    // 精度(float/double可选)
+  defaultValue?: string;               // 默认值
+  unit?: string;                       // 单位符号
+  unitName?: string;                   // 单位名称
+}
+```
+
+### 2. 布尔/枚举型数据规范 (ThingModelBoolOrEnumDataSpecs)
+
+适用于 `bool`、`enum` 类型:
+
+```typescript
+interface ThingModelBoolOrEnumDataSpecs {
+  dataType: "bool" | "enum";
+  name: string;                        // 枚举项名称(必填)
+  value: number;                       // 枚举值(必填)
+}
+```
+
+### 3. 文本/时间型数据规范 (ThingModelDateOrTextDataSpecs)
+
+适用于 `text`、`date` 类型:
+
+```typescript
+interface ThingModelDateOrTextDataSpecs {
+  dataType: "text" | "date";
+  length?: number;                     // 数据长度(text类型需要,最大2048)
+  defaultValue?: string;               // 默认值
+}
+```
+
+### 4. 数组型数据规范 (ThingModelArrayDataSpecs)
+
+适用于 `array` 类型:
+
+```typescript
+interface ThingModelArrayDataSpecs {
+  dataType: "array";
+  size: number;                        // 数组元素个数(必填)
+  childDataType: string;               // 数组元素数据类型(必填)
+  dataSpecsList?: ThingModelDataSpecs[]; // 子元素数据规范(struct类型时)
+}
+```
+
+**childDataType 枚举值**:
+- `"struct"`: 结构体
+- `"int"`: 整数
+- `"float"`: 单精度浮点
+- `"double"`: 双精度浮点
+- `"text"`: 文本
+
+### 5. 结构体型数据规范 (ThingModelStructDataSpecs)
+
+适用于 `struct` 类型:
+
+```typescript
+interface ThingModelStructDataSpecs {
+  dataType: "struct";
+  identifier: string;                  // 属性标识符(必填)
+  name: string;                        // 属性名称(必填)
+  accessMode: string;                  // 操作类型(必填)
+  required?: boolean;                  // 是否必选
+  childDataType: string;               // 子数据类型(必填)
+  dataSpecs?: ThingModelDataSpecs;     // 数据规范(非列表型)
+  dataSpecsList?: ThingModelDataSpecs[]; // 数据规范(列表型)
+}
+```
+
+**childDataType 枚举值**:
+- `"int"`: 整数
+- `"float"`: 单精度浮点
+- `"double"`: 双精度浮点
+- `"text"`: 文本
+- `"date"`: 时间
+- `"enum"`: 枚举
+- `"bool"`: 布尔
+
+## 数据类型映射关系
+
+### dataSpecs vs dataSpecsList
+
+- **dataSpecs**: 用于非列表型数据类型(`int`、`float`、`double`、`text`、`date`、`array`)
+- **dataSpecsList**: 用于列表型数据类型(`enum`、`bool`、`struct`)
+
+### JSON多态序列化
+
+数据规范使用Jackson的`@JsonTypeInfo`和`@JsonSubTypes`注解实现多态序列化:
+
+```json
+{
+  "dataType": "int",
+  "max": "100",
+  "min": "0",
+  "step": "1",
+  "unit": "°C",
+  "unitName": "摄氏度"
+}
+```
+
+## 完整示例
+
+### 温度传感器物模型示例
+
+```json
+{
+  "productId": 1024,
+  "productKey": "temperature_sensor",
+  "properties": [
+    {
+      "identifier": "temperature",
+      "name": "温度",
+      "accessMode": "r",
+      "required": true,
+      "dataType": "float",
+      "dataSpecs": {
+        "dataType": "float",
+        "max": "100.0",
+        "min": "-40.0",
+        "step": "0.1",
+        "precise": "1",
+        "unit": "°C",
+        "unitName": "摄氏度"
+      }
+    },
+    {
+      "identifier": "power_switch",
+      "name": "电源开关",
+      "accessMode": "rw",
+      "required": false,
+      "dataType": "bool",
+      "dataSpecsList": [
+        {
+          "dataType": "bool",
+          "name": "关闭",
+          "value": 0
+        },
+        {
+          "dataType": "bool",
+          "name": "开启",
+          "value": 1
+        }
+      ]
+    }
+  ],
+  "events": [
+    {
+      "identifier": "high_temperature_alert",
+      "name": "高温告警",
+      "required": false,
+      "type": "alert",
+      "outputParams": [
+        {
+          "identifier": "current_temp",
+          "name": "当前温度",
+          "direction": "output",
+          "dataType": "float",
+          "dataSpecs": {
+            "dataType": "float",
+            "max": "100.0",
+            "min": "-40.0",
+            "step": "0.1"
+          }
+        }
+      ]
+    }
+  ],
+  "services": [
+    {
+      "identifier": "reset_device",
+      "name": "重置设备",
+      "required": false,
+      "callType": "async",
+      "inputParams": [
+        {
+          "identifier": "reset_type",
+          "name": "重置类型",
+          "direction": "input",
+          "dataType": "enum",
+          "dataSpecsList": [
+            {
+              "dataType": "enum",
+              "name": "软重置",
+              "value": 1
+            },
+            {
+              "dataType": "enum",
+              "name": "硬重置",
+              "value": 2
+            }
+          ]
+        }
+      ],
+      "outputParams": [
+        {
+          "identifier": "result",
+          "name": "执行结果",
+          "direction": "output",
+          "dataType": "bool",
+          "dataSpecsList": [
+            {
+              "dataType": "bool",
+              "name": "失败",
+              "value": 0
+            },
+            {
+              "dataType": "bool",
+              "name": "成功",
+              "value": 1
+            }
+          ]
+        }
+      ]
+    }
+  ]
+}
+```
+
+## 前端使用建议
+
+### 1. TypeScript类型定义
+
+建议在前端项目中定义完整的TypeScript接口,确保类型安全:
+
+```typescript
+// 定义完整的类型接口
+export interface IotThingModelTSLRespVO {
+  productId: number;
+  productKey: string;
+  properties: ThingModelProperty[];
+  events: ThingModelEvent[];
+  services: ThingModelService[];
+}
+
+// 使用联合类型处理数据规范的多态性
+export type ThingModelDataSpecs = 
+  | ThingModelNumericDataSpec
+  | ThingModelBoolOrEnumDataSpecs
+  | ThingModelDateOrTextDataSpecs
+  | ThingModelArrayDataSpecs
+  | ThingModelStructDataSpecs;
+```
+
+### 2. 数据验证
+
+```typescript
+// 验证数据类型和数据规范的一致性
+function validateDataSpecs(dataType: string, dataSpecs: any): boolean {
+  switch (dataType) {
+    case 'int':
+    case 'float':
+    case 'double':
+      return dataSpecs.dataType === dataType && 
+             dataSpecs.max !== undefined && 
+             dataSpecs.min !== undefined;
+    case 'bool':
+    case 'enum':
+      return Array.isArray(dataSpecs) && 
+             dataSpecs.every(spec => spec.name && spec.value !== undefined);
+    // ... 其他类型验证
+    default:
+      return false;
+  }
+}
+```
+
+### 3. 数据转换工具
+
+```typescript
+// 将后端数据转换为前端展示格式
+function formatPropertyValue(property: ThingModelProperty, value: any): string {
+  if (property.dataType === 'enum' || property.dataType === 'bool') {
+    const spec = property.dataSpecsList?.find(s => s.value === value);
+    return spec?.name || String(value);
+  }
+  
+  if (property.dataType === 'float' || property.dataType === 'double') {
+    const unit = property.dataSpecs?.unit || '';
+    return `${value}${unit}`;
+  }
+  
+  return String(value);
+}
+```
+
+## 注意事项
+
+1. **数据规范选择**: 根据`dataType`选择使用`dataSpecs`还是`dataSpecsList`
+2. **标识符唯一性**: 在同一产品下,所有功能的`identifier`必须唯一
+3. **数据类型一致性**: 参数的`dataType`必须与其`dataSpecs`的`dataType`保持一致
+4. **枚举值处理**: 布尔型和枚举型数据使用`dataSpecsList`数组存储可选值
+5. **嵌套结构**: 结构体和数组类型可能包含嵌套的数据规范定义
+6. **版本兼容**: 物模型结构可能随版本演进,前端需要做好兼容性处理
+
+这个数据结构为IoT设备的完整功能描述提供了标准化的格式,支持复杂的数据类型和嵌套结构,能够满足各种IoT设备的建模需求。

+ 55 - 10
src/views/iot/rule/scene/components/selectors/ProductDeviceSelector.vue

@@ -34,8 +34,20 @@
         </el-form-item>
       </el-col>
 
-      <!-- 设备选择 -->
+      <!-- 设备选择模式 -->
       <el-col :span="12">
+        <el-form-item label="设备选择模式" required>
+          <el-radio-group v-model="deviceSelectionMode" @change="handleDeviceSelectionModeChange">
+            <el-radio value="specific">选择设备</el-radio>
+            <el-radio value="all">全部设备</el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </el-col>
+    </el-row>
+
+    <!-- 具体设备选择 -->
+    <el-row v-if="deviceSelectionMode === 'specific'" :gutter="16">
+      <el-col :span="24">
         <el-form-item label="选择设备" required>
           <el-select
             v-model="localDeviceId"
@@ -58,8 +70,8 @@
                   <div class="option-name">{{ device.deviceName }}</div>
                   <div class="option-nickname">{{ device.nickname || '无备注' }}</div>
                 </div>
-                <el-tag 
-                  size="small" 
+                <el-tag
+                  size="small"
                   :type="getDeviceStatusTag(device.state)"
                 >
                   {{ getDeviceStatusText(device.state) }}
@@ -72,7 +84,7 @@
     </el-row>
 
     <!-- 选择结果展示 -->
-    <div v-if="localProductId && localDeviceId" class="selection-result">
+    <div v-if="localProductId && (localDeviceId !== undefined)" class="selection-result">
       <div class="result-header">
         <Icon icon="ep:check" class="result-icon" />
         <span class="result-title">已选择设备</span>
@@ -85,9 +97,18 @@
         </div>
         <div class="result-item">
           <span class="result-label">设备:</span>
-          <span class="result-value">{{ selectedDevice?.deviceName }}</span>
-          <el-tag 
-            size="small" 
+          <span v-if="deviceSelectionMode === 'all'" class="result-value">全部设备</span>
+          <span v-else class="result-value">{{ selectedDevice?.deviceName }}</span>
+          <el-tag
+            v-if="deviceSelectionMode === 'all'"
+            size="small"
+            type="warning"
+          >
+            全部
+          </el-tag>
+          <el-tag
+            v-else
+            size="small"
             :type="getDeviceStatusTag(selectedDevice?.state)"
           >
             {{ getDeviceStatusText(selectedDevice?.state) }}
@@ -123,6 +144,9 @@ const emit = defineEmits<Emits>()
 const localProductId = useVModel(props, 'productId', emit)
 const localDeviceId = useVModel(props, 'deviceId', emit)
 
+// 设备选择模式
+const deviceSelectionMode = ref<'specific' | 'all'>('specific')
+
 // 数据状态
 const productLoading = ref(false)
 const deviceLoading = ref(false)
@@ -162,11 +186,11 @@ const handleProductChange = async (productId?: number) => {
   localProductId.value = productId
   localDeviceId.value = undefined
   deviceList.value = []
-  
+
   if (productId) {
     await getDeviceList(productId)
   }
-  
+
   emitChange()
 }
 
@@ -175,6 +199,20 @@ const handleDeviceChange = (deviceId?: number) => {
   emitChange()
 }
 
+const handleDeviceSelectionModeChange = (mode: 'specific' | 'all') => {
+  deviceSelectionMode.value = mode
+
+  if (mode === 'all') {
+    // 全部设备时,设备ID设为0
+    localDeviceId.value = 0
+  } else {
+    // 选择设备时,清空设备ID
+    localDeviceId.value = undefined
+  }
+
+  emitChange()
+}
+
 const emitChange = () => {
   emit('change', {
     productId: localProductId.value,
@@ -222,7 +260,14 @@ const getDeviceList = async (productId: number) => {
 // 初始化
 onMounted(async () => {
   await getProductList()
-  
+
+  // 根据初始设备ID设置选择模式
+  if (localDeviceId.value === 0) {
+    deviceSelectionMode.value = 'all'
+  } else if (localDeviceId.value) {
+    deviceSelectionMode.value = 'specific'
+  }
+
   if (localProductId.value) {
     await getDeviceList(localProductId.value)
   }

+ 159 - 77
src/views/iot/rule/scene/components/selectors/PropertySelector.vue

@@ -27,8 +27,8 @@
               <div class="option-identifier">{{ property.identifier }}</div>
             </div>
             <div class="option-meta">
-              <el-tag :type="getPropertyTypeTag(property.type)" size="small">
-                {{ getPropertyTypeName(property.type) }}
+              <el-tag :type="getPropertyTypeTag(property.dataType)" size="small">
+                {{ getPropertyTypeName(property.dataType) }}
               </el-tag>
             </div>
           </div>
@@ -41,8 +41,8 @@
       <div class="details-header">
         <Icon icon="ep:info-filled" class="details-icon" />
         <span class="details-title">{{ selectedProperty.name }}</span>
-        <el-tag :type="getPropertyTypeTag(selectedProperty.type)" size="small">
-          {{ getPropertyTypeName(selectedProperty.type) }}
+        <el-tag :type="getPropertyTypeTag(selectedProperty.dataType)" size="small">
+          {{ getPropertyTypeName(selectedProperty.dataType) }}
         </el-tag>
       </div>
       <div class="details-content">
@@ -70,6 +70,9 @@
 <script setup lang="ts">
 import { useVModel } from '@vueuse/core'
 import { IotRuleSceneTriggerTypeEnum } from '@/api/iot/rule/scene/scene.types'
+import { ThingModelApi, ThingModelData } from '@/api/iot/thingmodel'
+import { IoTThingModelTypeEnum } from '@/views/iot/utils/constants'
+import type { IotThingModelTSLRespVO, PropertySelectorItem } from './types'
 
 /** 属性选择器组件 */
 defineOptions({ name: 'PropertySelector' })
@@ -93,33 +96,34 @@ const localValue = useVModel(props, 'modelValue', emit)
 
 // 状态
 const loading = ref(false)
-const propertyList = ref<any[]>([])
+const propertyList = ref<PropertySelectorItem[]>([])
+const thingModelTSL = ref<IotThingModelTSLRespVO | null>(null)
 
 // 计算属性
 const propertyGroups = computed(() => {
   const groups: { label: string; options: any[] }[] = []
-  
+
   if (props.triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_PROPERTY_POST) {
     groups.push({
       label: '设备属性',
-      options: propertyList.value.filter(p => p.category === 'property')
+      options: propertyList.value.filter(p => p.type === IoTThingModelTypeEnum.PROPERTY)
     })
   }
-  
+
   if (props.triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_EVENT_POST) {
     groups.push({
       label: '设备事件',
-      options: propertyList.value.filter(p => p.category === 'event')
+      options: propertyList.value.filter(p => p.type === IoTThingModelTypeEnum.EVENT)
     })
   }
-  
+
   if (props.triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_SERVICE_INVOKE) {
     groups.push({
       label: '设备服务',
-      options: propertyList.value.filter(p => p.category === 'service')
+      options: propertyList.value.filter(p => p.type === IoTThingModelTypeEnum.SERVICE)
     })
   }
-  
+
   return groups.filter(group => group.options.length > 0)
 })
 
@@ -128,34 +132,34 @@ const selectedProperty = computed(() => {
 })
 
 // 工具函数
-const getPropertyTypeName = (type: string) => {
+const getPropertyTypeName = (dataType: string) => {
   const typeMap = {
     'int': '整数',
     'float': '浮点数',
     'double': '双精度',
-    'string': '字符串',
+    'text': '字符串',
     'bool': '布尔值',
     'enum': '枚举',
     'date': '日期',
     'struct': '结构体',
     'array': '数组'
   }
-  return typeMap[type] || type
+  return typeMap[dataType] || dataType
 }
 
-const getPropertyTypeTag = (type: string) => {
+const getPropertyTypeTag = (dataType: string) => {
   const tagMap = {
     'int': 'primary',
     'float': 'success',
     'double': 'success',
-    'string': 'info',
+    'text': 'info',
     'bool': 'warning',
     'enum': 'danger',
     'date': 'primary',
     'struct': 'info',
     'array': 'warning'
   }
-  return tagMap[type] || 'info'
+  return tagMap[dataType] || 'info'
 }
 
 // 事件处理
@@ -163,84 +167,162 @@ const handleChange = (value: string) => {
   const property = propertyList.value.find(p => p.identifier === value)
   if (property) {
     emit('change', {
-      type: property.type,
+      type: property.dataType,
       config: property
     })
   }
 }
 
-// API 调用
-const getPropertyList = async () => {
+// 获取物模型TSL数据
+const getThingModelTSL = async () => {
   if (!props.productId) {
+    thingModelTSL.value = null
     propertyList.value = []
     return
   }
-  
+
   loading.value = true
   try {
-    // 这里应该调用真实的API获取物模型数据
-    // 暂时使用模拟数据
-    propertyList.value = [
-      // 属性
-      {
-        identifier: 'temperature',
-        name: '温度',
-        type: 'float',
-        category: 'property',
-        description: '环境温度',
-        unit: '°C',
-        range: '-40~80'
-      },
-      {
-        identifier: 'humidity',
-        name: '湿度',
-        type: 'float',
-        category: 'property',
-        description: '环境湿度',
-        unit: '%',
-        range: '0~100'
-      },
-      {
-        identifier: 'power',
-        name: '电源状态',
-        type: 'bool',
-        category: 'property',
-        description: '设备电源开关状态'
-      },
-      // 事件
-      {
-        identifier: 'alarm',
-        name: '告警事件',
-        type: 'struct',
-        category: 'event',
-        description: '设备告警事件'
-      },
-      {
-        identifier: 'fault',
-        name: '故障事件',
-        type: 'struct',
-        category: 'event',
-        description: '设备故障事件'
-      },
-      // 服务
-      {
-        identifier: 'restart',
-        name: '重启服务',
-        type: 'struct',
-        category: 'service',
-        description: '设备重启服务'
-      }
-    ]
+    thingModelTSL.value = await ThingModelApi.getThingModelTSLByProductId(props.productId)
+    parseThingModelData()
   } catch (error) {
-    console.error('获取物模型失败:', error)
+    console.error('获取物模型TSL失败:', error)
+    // 如果TSL获取失败,尝试获取物模型列表
+    await getThingModelList()
   } finally {
     loading.value = false
   }
 }
 
+// 获取物模型列表(备用方案)
+const getThingModelList = async () => {
+  if (!props.productId) {
+    propertyList.value = []
+    return
+  }
+
+  try {
+    const data = await ThingModelApi.getThingModelList({ productId: props.productId })
+    propertyList.value = data || []
+  } catch (error) {
+    console.error('获取物模型列表失败:', error)
+    propertyList.value = []
+  }
+}
+
+// 解析物模型TSL数据
+const parseThingModelData = () => {
+  const tsl = thingModelTSL.value
+  const properties: PropertySelectorItem[] = []
+
+  if (tsl) {
+    // 解析属性
+    if (tsl.properties && Array.isArray(tsl.properties)) {
+      tsl.properties.forEach((prop) => {
+        properties.push({
+          identifier: prop.identifier,
+          name: prop.name,
+          description: prop.description,
+          dataType: prop.dataType,
+          type: IoTThingModelTypeEnum.PROPERTY,
+          accessMode: prop.accessMode,
+          required: prop.required,
+          unit: getPropertyUnit(prop),
+          range: getPropertyRange(prop),
+          property: prop
+        })
+      })
+    }
+
+    // 解析事件
+    if (tsl.events && Array.isArray(tsl.events)) {
+      tsl.events.forEach((event) => {
+        properties.push({
+          identifier: event.identifier,
+          name: event.name,
+          description: event.description,
+          dataType: 'struct',
+          type: IoTThingModelTypeEnum.EVENT,
+          eventType: event.type,
+          required: event.required,
+          outputParams: event.outputParams,
+          event: event
+        })
+      })
+    }
+
+    // 解析服务
+    if (tsl.services && Array.isArray(tsl.services)) {
+      tsl.services.forEach((service) => {
+        properties.push({
+          identifier: service.identifier,
+          name: service.name,
+          description: service.description,
+          dataType: 'struct',
+          type: IoTThingModelTypeEnum.SERVICE,
+          callType: service.callType,
+          required: service.required,
+          inputParams: service.inputParams,
+          outputParams: service.outputParams,
+          service: service
+        })
+      })
+    }
+  }
+
+  propertyList.value = properties
+}
+
+// 获取属性单位
+const getPropertyUnit = (property: any) => {
+  if (!property) return undefined
+
+  // 数值型数据的单位
+  if (property.dataSpecs && property.dataSpecs.unit) {
+    return property.dataSpecs.unit
+  }
+
+  return undefined
+}
+
+// 获取属性范围描述
+const getPropertyRange = (property: any) => {
+  if (!property) return undefined
+
+  // 数值型数据的范围
+  if (property.dataSpecs) {
+    const specs = property.dataSpecs
+    if (specs.min !== undefined && specs.max !== undefined) {
+      return `${specs.min}~${specs.max}`
+    }
+  }
+
+  // 枚举型和布尔型数据的选项
+  if (property.dataSpecsList && Array.isArray(property.dataSpecsList)) {
+    return property.dataSpecsList.map((item: any) => `${item.name}(${item.value})`).join(', ')
+  }
+
+  return undefined
+}
+
+// 获取数据范围描述(保留兼容性)
+const getDataRange = (dataSpecs: any) => {
+  if (!dataSpecs) return undefined
+
+  if (dataSpecs.min !== undefined && dataSpecs.max !== undefined) {
+    return `${dataSpecs.min}~${dataSpecs.max}`
+  }
+
+  if (dataSpecs.dataSpecsList && Array.isArray(dataSpecs.dataSpecsList)) {
+    return dataSpecs.dataSpecsList.map((item: any) => `${item.name}(${item.value})`).join(', ')
+  }
+
+  return undefined
+}
+
 // 监听产品变化
 watch(() => props.productId, () => {
-  getPropertyList()
+  getThingModelTSL()
 }, { immediate: true })
 
 // 监听触发类型变化

+ 168 - 0
src/views/iot/rule/scene/components/selectors/types.ts

@@ -0,0 +1,168 @@
+// IoT物模型TSL数据类型定义
+
+/** 物模型TSL响应数据结构 */
+export interface IotThingModelTSLRespVO {
+  productId: number
+  productKey: string
+  properties: ThingModelProperty[]
+  events: ThingModelEvent[]
+  services: ThingModelService[]
+}
+
+/** 物模型属性 */
+export interface ThingModelProperty {
+  identifier: string
+  name: string
+  accessMode: string
+  required?: boolean
+  dataType: string
+  description?: string
+  dataSpecs?: ThingModelDataSpecs
+  dataSpecsList?: ThingModelDataSpecs[]
+}
+
+/** 物模型事件 */
+export interface ThingModelEvent {
+  identifier: string
+  name: string
+  required?: boolean
+  type: string
+  description?: string
+  outputParams?: ThingModelParam[]
+  method?: string
+}
+
+/** 物模型服务 */
+export interface ThingModelService {
+  identifier: string
+  name: string
+  required?: boolean
+  callType: string
+  description?: string
+  inputParams?: ThingModelParam[]
+  outputParams?: ThingModelParam[]
+  method?: string
+}
+
+/** 物模型参数 */
+export interface ThingModelParam {
+  identifier: string
+  name: string
+  direction: string
+  paraOrder?: number
+  dataType: string
+  dataSpecs?: ThingModelDataSpecs
+  dataSpecsList?: ThingModelDataSpecs[]
+}
+
+/** 数值型数据规范 */
+export interface ThingModelNumericDataSpec {
+  dataType: 'int' | 'float' | 'double'
+  max: string
+  min: string
+  step: string
+  precise?: string
+  defaultValue?: string
+  unit?: string
+  unitName?: string
+}
+
+/** 布尔/枚举型数据规范 */
+export interface ThingModelBoolOrEnumDataSpecs {
+  dataType: 'bool' | 'enum'
+  name: string
+  value: number
+}
+
+/** 文本/时间型数据规范 */
+export interface ThingModelDateOrTextDataSpecs {
+  dataType: 'text' | 'date'
+  length?: number
+  defaultValue?: string
+}
+
+/** 数组型数据规范 */
+export interface ThingModelArrayDataSpecs {
+  dataType: 'array'
+  size: number
+  childDataType: string
+  dataSpecsList?: ThingModelDataSpecs[]
+}
+
+/** 结构体型数据规范 */
+export interface ThingModelStructDataSpecs {
+  dataType: 'struct'
+  identifier: string
+  name: string
+  accessMode: string
+  required?: boolean
+  childDataType: string
+  dataSpecs?: ThingModelDataSpecs
+  dataSpecsList?: ThingModelDataSpecs[]
+}
+
+/** 数据规范联合类型 */
+export type ThingModelDataSpecs =
+  | ThingModelNumericDataSpec
+  | ThingModelBoolOrEnumDataSpecs
+  | ThingModelDateOrTextDataSpecs
+  | ThingModelArrayDataSpecs
+  | ThingModelStructDataSpecs
+
+/** 属性选择器内部使用的统一数据结构 */
+export interface PropertySelectorItem {
+  identifier: string
+  name: string
+  description?: string
+  dataType: string
+  type: number // IoTThingModelTypeEnum
+  accessMode?: string
+  required?: boolean
+  unit?: string
+  range?: string
+  eventType?: string
+  callType?: string
+  inputParams?: ThingModelParam[]
+  outputParams?: ThingModelParam[]
+  property?: ThingModelProperty
+  event?: ThingModelEvent
+  service?: ThingModelService
+}
+
+/** 数据类型枚举 */
+export enum DataTypeEnum {
+  INT = 'int',
+  FLOAT = 'float',
+  DOUBLE = 'double',
+  ENUM = 'enum',
+  BOOL = 'bool',
+  TEXT = 'text',
+  DATE = 'date',
+  STRUCT = 'struct',
+  ARRAY = 'array'
+}
+
+/** 访问模式枚举 */
+export enum AccessModeEnum {
+  READ = 'r',
+  READ_write = 'rw'
+}
+
+/** 事件类型枚举 */
+export enum EventTypeEnum {
+  INFO = 'info',
+  ALERT = 'alert',
+  ERROR = 'error'
+}
+
+/** 调用类型枚举 */
+export enum CallTypeEnum {
+  ASYNC = 'async',
+  SYNC = 'sync'
+}
+
+/** 参数方向枚举 */
+export enum ParamDirectionEnum {
+  INPUT = 'input',
+  OUTPUT = 'output'
+}