Prechádzať zdrojové kódy

perf:【IoT 物联网】场景联动触发器优化

puhui999 8 mesiacov pred
rodič
commit
274ecb5dca

+ 1 - 1
.vscode/settings.json

@@ -88,7 +88,7 @@
   },
   "editor.formatOnSave": true,
   "[vue]": {
-    "editor.defaultFormatter": "octref.vetur"
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
   },
   "i18n-ally.localesPaths": ["src/locales"],
   "i18n-ally.keystyle": "nested",

+ 7 - 6
src/api/iot/rule/scene/scene.types.ts

@@ -173,12 +173,13 @@ interface IotRuleScene extends TenantBaseDO {
   actions: ActionConfig[] // 执行器数组(必填,至少一个)
 }
 
-// 工具类型
-// TODO @puhui999:这些在瞅瞅~
-type TriggerType = (typeof IotRuleSceneTriggerTypeEnum)[keyof typeof IotRuleSceneTriggerTypeEnum]
-type ActionType = (typeof IotRuleSceneActionTypeEnum)[keyof typeof IotRuleSceneActionTypeEnum]
-type MessageType = (typeof IotDeviceMessageTypeEnum)[keyof typeof IotDeviceMessageTypeEnum]
-type OperatorType =
+// 工具类型 - 从枚举中提取类型
+export type TriggerType =
+  (typeof IotRuleSceneTriggerTypeEnum)[keyof typeof IotRuleSceneTriggerTypeEnum]
+export type ActionType =
+  (typeof IotRuleSceneActionTypeEnum)[keyof typeof IotRuleSceneActionTypeEnum]
+export type MessageType = (typeof IotDeviceMessageTypeEnum)[keyof typeof IotDeviceMessageTypeEnum]
+export type OperatorType =
   (typeof IotRuleSceneTriggerConditionParameterOperatorEnum)[keyof typeof IotRuleSceneTriggerConditionParameterOperatorEnum]['value']
 
 // 表单验证规则类型

+ 0 - 1102
src/views/iot/rule/scene/IoT场景联动规则表单设计思路文档.md

@@ -1,1102 +0,0 @@
-// TODO @puhui999:这些后续需要删除哈
-# IoT场景联动规则表单设计思路文档
-
-## 概述
-
-本文档详细描述了IoT场景联动规则表单的设计思路,包括表单结构、组件设计、数据流转和用户交互逻辑。通过Mermaid图直观展示各个组件之间的关系和数据流向。
-
-## 表单整体架构设计
-
-### 1. 表单主体结构
-
-表单采用分步骤设计,包含以下主要部分:
-
-- **基础信息配置**:场景名称、描述、状态
-- **触发器配置**:设备触发或定时触发
-- **执行器配置**:设备控制或告警配置
-- **预览与保存**:配置预览和最终保存
-
-### 2. 组件层次结构图
-
-```mermaid
-graph TB
-    A[RuleSceneForm<br/>主表单组件] --> B[BasicInfoSection<br/>基础信息]
-    A --> C[TriggerSection<br/>触发器配置]
-    A --> D[ActionSection<br/>执行器配置]
-    A --> E[PreviewSection<br/>预览区域]
-
-    %% 基础信息组件
-    B --> B1[NameInput<br/>场景名称输入]
-    B --> B2[DescriptionInput<br/>场景描述输入]
-    B --> B3[StatusRadio<br/>状态选择]
-
-    %% 触发器配置组件
-    C --> C1[TriggerTypeSelector<br/>触发类型选择器]
-    C --> C2[DeviceTriggerConfig<br/>设备触发配置]
-    C --> C3[TimerTriggerConfig<br/>定时触发配置]
-
-    %% 设备触发配置子组件
-    C2 --> C21[ProductSelector<br/>产品选择器]
-    C2 --> C22[DeviceSelector<br/>设备选择器]
-    C2 --> C23[PropertySelector<br/>属性选择器]
-    C2 --> C24[OperatorSelector<br/>操作符选择器]
-    C2 --> C25[ValueInput<br/>值输入]
-    C2 --> C26[ConditionGroupConfig<br/>条件分组配置]
-
-    %% 定时触发配置子组件
-    C3 --> C31[CronInput<br/>CRON表达式输入]
-    C3 --> C32[CronBuilder<br/>可视化CRON构建器]
-    C3 --> C33[NextExecutionPreview<br/>下次执行时间预览]
-
-    %% 执行器配置组件
-    D --> D1[ActionTypeSelector<br/>执行类型选择器]
-    D --> D2[DeviceControlConfig<br/>设备控制配置]
-    D --> D3[AlertConfig<br/>告警配置]
-
-    %% 设备控制配置子组件
-    D2 --> D21[TargetProductSelector<br/>目标产品选择器]
-    D2 --> D22[TargetDeviceSelector<br/>目标设备选择器]
-    D2 --> D23[ControlTypeSelector<br/>控制类型选择器]
-    D2 --> D24[ParamsConfig<br/>参数配置]
-
-    %% 告警配置子组件
-    D3 --> D31[AlertConfigSelector<br/>告警配置选择器]
-
-    %% 预览区域组件
-    E --> E1[ConfigPreview<br/>配置预览]
-    E --> E2[ValidationResult<br/>验证结果]
-    E --> E3[SaveButton<br/>保存按钮]
-
-    %% 样式定义
-    classDef mainComponent fill:#e1f5fe,stroke:#01579b,stroke-width:2px
-    classDef sectionComponent fill:#f3e5f5,stroke:#4a148c,stroke-width:2px
-    classDef subComponent fill:#e8f5e8,stroke:#1b5e20,stroke-width:2px
-
-    class A mainComponent
-    class B,C,D,E sectionComponent
-    class B1,B2,B3,C1,C2,C3,C21,C22,C23,C24,C25,C26,C31,C32,C33,D1,D2,D3,D21,D22,D23,D24,D31,E1,E2,E3 subComponent
-```
-
-### 3. 组件层次结构文本
-
-```text
-RuleSceneForm (主表单)
-├── BasicInfoSection (基础信息)
-│   ├── NameInput (场景名称输入)
-│   ├── DescriptionInput (场景描述输入)
-│   └── StatusRadio (状态选择)
-├── TriggerSection (触发器配置)
-│   ├── TriggerTypeSelector (触发类型选择)
-│   ├── DeviceTriggerConfig (设备触发配置)
-│   │   ├── ProductSelector (产品选择器)
-│   │   ├── DeviceSelector (设备选择器)
-│   │   ├── PropertySelector (属性选择器)
-│   │   ├── OperatorSelector (操作符选择器)
-│   │   ├── ValueInput (值输入)
-│   │   └── ConditionGroupConfig (条件分组配置)
-│   └── TimerTriggerConfig (定时触发配置)
-│       ├── CronInput (CRON表达式输入)
-│       ├── CronBuilder (可视化CRON构建器)
-│       └── NextExecutionPreview (下次执行时间预览)
-├── ActionSection (执行器配置)
-│   ├── ActionTypeSelector (执行类型选择)
-│   ├── DeviceControlConfig (设备控制配置)
-│   │   ├── TargetProductSelector (目标产品选择器)
-│   │   ├── TargetDeviceSelector (目标设备选择器)
-│   │   ├── ControlTypeSelector (控制类型选择器)
-│   │   └── ParamsConfig (参数配置)
-│   └── AlertConfig (告警配置)
-│       └── AlertConfigSelector (告警配置选择器)
-└── PreviewSection (预览区域)
-    ├── ConfigPreview (配置预览)
-    ├── ValidationResult (验证结果)
-    └── SaveButton (保存按钮)
-```
-
-## 表单数据结构设计
-
-### 1. 表单数据模型结构图
-
-```mermaid
-classDiagram
-    class RuleSceneFormData {
-        +number id?
-        +string name
-        +string description?
-        +number status
-        +TriggerFormData[] triggers
-        +ActionFormData[] actions
-        +validateForm() boolean
-        +toApiFormat() ApiRequestData
-    }
-
-    class TriggerFormData {
-        +number type
-        +number productId?
-        +number deviceId?
-        +string identifier?
-        +string operator?
-        +string value?
-        +string cronExpression?
-        +ConditionGroupFormData[] conditionGroups?
-        +validateTrigger() boolean
-        +isDeviceTrigger() boolean
-        +isTimerTrigger() boolean
-    }
-
-    class ActionFormData {
-        +number type
-        +number productId?
-        +number deviceId?
-        +Record params?
-        +number alertConfigId?
-        +validateAction() boolean
-        +isDeviceAction() boolean
-        +isAlertAction() boolean
-    }
-
-    class ConditionGroupFormData {
-        +ConditionFormData[] conditions
-        +string logicOperator
-        +validateGroup() boolean
-    }
-
-    class ConditionFormData {
-        +number type
-        +number productId
-        +number deviceId
-        +string identifier
-        +string operator
-        +string param
-        +validateCondition() boolean
-    }
-
-    class TriggerTypeEnum {
-        <<enumeration>>
-        DEVICE_STATE_UPDATE: 1
-        DEVICE_PROPERTY_POST: 2
-        DEVICE_EVENT_POST: 3
-        DEVICE_SERVICE_INVOKE: 4
-        TIMER: 100
-    }
-
-    class ActionTypeEnum {
-        <<enumeration>>
-        DEVICE_PROPERTY_SET: 1
-        DEVICE_SERVICE_INVOKE: 2
-        ALERT_TRIGGER: 100
-        ALERT_RECOVER: 101
-    }
-
-    class OperatorEnum {
-        <<enumeration>>
-        EQUALS: "="
-        NOT_EQUALS: "!="
-        GREATER_THAN: ">"
-        LESS_THAN: "<"
-        IN: "in"
-        BETWEEN: "between"
-    }
-
-    RuleSceneFormData "1" --> "*" TriggerFormData : contains
-    RuleSceneFormData "1" --> "*" ActionFormData : contains
-    TriggerFormData "1" --> "*" ConditionGroupFormData : contains
-    ConditionGroupFormData "1" --> "*" ConditionFormData : contains
-    TriggerFormData --> TriggerTypeEnum : uses
-    ActionFormData --> ActionTypeEnum : uses
-    ConditionFormData --> OperatorEnum : uses
-```
-
-### 2. 表单数据模型代码
-
-```typescript
-interface RuleSceneFormData {
-  // 基础信息
-  id?: number;
-  name: string;
-  description?: string;
-  status: number;
-
-  // 触发器配置
-  triggers: TriggerFormData[];
-
-  // 执行器配置
-  actions: ActionFormData[];
-}
-
-interface TriggerFormData {
-  type: number;
-  productId?: number;
-  deviceId?: number;
-  identifier?: string;
-  operator?: string;
-  value?: string;
-  cronExpression?: string;
-  conditionGroups?: ConditionGroupFormData[];
-}
-
-interface ActionFormData {
-  type: number;
-  productId?: number;
-  deviceId?: number;
-  params?: Record<string, any>;
-  alertConfigId?: number;
-}
-
-interface ConditionGroupFormData {
-  conditions: ConditionFormData[];
-  logicOperator: 'AND' | 'OR';
-}
-
-interface ConditionFormData {
-  type: number;
-  productId: number;
-  deviceId: number;
-  identifier: string;
-  operator: string;
-  param: string;
-}
-```
-
-### 2. 表单验证规则
-
-```typescript
-const validationRules = {
-  name: [
-    { required: true, message: '场景名称不能为空' },
-    { max: 50, message: '场景名称不能超过50个字符' }
-  ],
-  status: [
-    { required: true, message: '场景状态不能为空' },
-    { type: 'enum', enum: [0, 1], message: '状态值必须为0或1' }
-  ],
-  triggers: [
-    { required: true, message: '触发器配置不能为空' },
-    { type: 'array', min: 1, message: '至少需要一个触发器' }
-  ],
-  actions: [
-    { required: true, message: '执行器配置不能为空' },
-    { type: 'array', min: 1, message: '至少需要一个执行器' }
-  ]
-};
-```
-
-## 核心组件设计
-
-### 1. 基础信息组件 (BasicInfoSection)
-
-```vue
-<template>
-  <el-card class="basic-info-section">
-    <template #header>
-      <span>基础信息</span>
-    </template>
-    
-    <el-form :model="formData" :rules="rules" label-width="120px">
-      <el-form-item label="场景名称" prop="name">
-        <el-input 
-          v-model="formData.name" 
-          placeholder="请输入场景名称"
-          maxlength="50"
-          show-word-limit
-        />
-      </el-form-item>
-      
-      <el-form-item label="场景描述" prop="description">
-        <el-input 
-          v-model="formData.description" 
-          type="textarea"
-          placeholder="请输入场景描述"
-          :rows="3"
-          maxlength="200"
-          show-word-limit
-        />
-      </el-form-item>
-      
-      <el-form-item label="场景状态" prop="status">
-        <el-radio-group v-model="formData.status">
-          <el-radio :label="0">开启</el-radio>
-          <el-radio :label="1">关闭</el-radio>
-        </el-radio-group>
-      </el-form-item>
-    </el-form>
-  </el-card>
-</template>
-```
-
-### 2. 触发器配置组件 (TriggerSection)
-
-```vue
-<template>
-  <el-card class="trigger-section">
-    <template #header>
-      <div class="section-header">
-        <span>触发器配置</span>
-        <el-button type="primary" size="small" @click="addTrigger">
-          <el-icon><Plus /></el-icon>
-          添加触发器
-        </el-button>
-      </div>
-    </template>
-    
-    <div v-for="(trigger, index) in triggers" :key="index" class="trigger-item">
-      <div class="trigger-header">
-        <span>触发器 {{ index + 1 }}</span>
-        <el-button 
-          type="danger" 
-          size="small" 
-          text 
-          @click="removeTrigger(index)"
-          v-if="triggers.length > 1"
-        >
-          删除
-        </el-button>
-      </div>
-      
-      <!-- 触发类型选择 -->
-      <TriggerTypeSelector v-model="trigger.type" @change="onTriggerTypeChange(trigger, $event)" />
-      
-      <!-- 设备触发配置 -->
-      <DeviceTriggerConfig 
-        v-if="isDeviceTrigger(trigger.type)"
-        v-model="trigger"
-      />
-      
-      <!-- 定时触发配置 -->
-      <TimerTriggerConfig 
-        v-if="trigger.type === TriggerType.TIMER"
-        v-model="trigger.cronExpression"
-      />
-    </div>
-  </el-card>
-</template>
-```
-
-### 3. 执行器配置组件 (ActionSection)
-
-```vue
-<template>
-  <el-card class="action-section">
-    <template #header>
-      <div class="section-header">
-        <span>执行器配置</span>
-        <el-button type="primary" size="small" @click="addAction">
-          <el-icon><Plus /></el-icon>
-          添加执行器
-        </el-button>
-      </div>
-    </template>
-    
-    <div v-for="(action, index) in actions" :key="index" class="action-item">
-      <div class="action-header">
-        <span>执行器 {{ index + 1 }}</span>
-        <el-button 
-          type="danger" 
-          size="small" 
-          text 
-          @click="removeAction(index)"
-          v-if="actions.length > 1"
-        >
-          删除
-        </el-button>
-      </div>
-      
-      <!-- 执行类型选择 -->
-      <ActionTypeSelector v-model="action.type" @change="onActionTypeChange(action, $event)" />
-      
-      <!-- 设备控制配置 -->
-      <DeviceControlConfig 
-        v-if="isDeviceAction(action.type)"
-        v-model="action"
-      />
-      
-      <!-- 告警配置 -->
-      <AlertConfig 
-        v-if="isAlertAction(action.type)"
-        v-model="action.alertConfigId"
-      />
-    </div>
-  </el-card>
-</template>
-```
-
-## 表单交互流程设计
-
-### 1. 表单初始化流程图
-
-```mermaid
-flowchart TD
-    A[页面加载] --> B[初始化表单数据结构]
-    B --> C[获取基础数据]
-    C --> C1[加载产品列表]
-    C --> C2[加载告警配置列表]
-    C --> C3[加载用户权限信息]
-    C1 --> D[表单渲染]
-    C2 --> D
-    C3 --> D
-    D --> E[建立双向数据绑定]
-    E --> F[表单就绪]
-
-    %% 错误处理
-    C --> G{数据加载失败?}
-    G -->|是| H[显示错误信息]
-    G -->|否| D
-    H --> I[提供重试选项]
-    I --> C
-```
-
-### 2. 触发器配置流程图
-
-```mermaid
-flowchart TD
-    A[开始配置触发器] --> B[选择触发类型]
-    B --> C{触发类型}
-
-    %% 设备触发分支
-    C -->|设备触发| D[设备触发配置]
-    D --> D1[选择产品]
-    D1 --> D2[加载设备列表]
-    D2 --> D3[选择设备]
-    D3 --> D4[加载物模型]
-    D4 --> D5[选择属性/事件]
-    D5 --> D6[选择操作符]
-    D6 --> D7[输入比较值]
-    D7 --> D8[配置条件分组]
-    D8 --> E[触发器配置完成]
-
-    %% 定时触发分支
-    C -->|定时触发| F[定时触发配置]
-    F --> F1[输入CRON表达式]
-    F1 --> F2{表达式格式}
-    F2 -->|正确| F3[显示下次执行时间]
-    F2 -->|错误| F4[显示错误提示]
-    F4 --> F5[提供可视化编辑器]
-    F5 --> F1
-    F3 --> E
-
-    %% 验证
-    E --> G[验证触发器配置]
-    G --> H{验证通过?}
-    H -->|是| I[保存触发器配置]
-    H -->|否| J[显示验证错误]
-    J --> D
-```
-
-### 3. 执行器配置流程图
-
-```mermaid
-flowchart TD
-    A[开始配置执行器] --> B[选择执行类型]
-    B --> C{执行类型}
-
-    %% 设备控制分支
-    C -->|设备控制| D[设备控制配置]
-    D --> D1[选择目标产品]
-    D1 --> D2[加载目标设备列表]
-    D2 --> D3[选择目标设备]
-    D3 --> D4[选择控制类型]
-    D4 --> D5{控制类型}
-    D5 -->|属性设置| D6[配置属性参数]
-    D5 -->|服务调用| D7[配置服务参数]
-    D6 --> E[执行器配置完成]
-    D7 --> E
-
-    %% 告警分支
-    C -->|告警触发/恢复| F[告警配置]
-    F --> F1[选择告警配置项]
-    F1 --> F2[配置告警参数]
-    F2 --> E
-
-    %% 验证
-    E --> G[验证执行器配置]
-    G --> H{验证通过?}
-    H -->|是| I[保存执行器配置]
-    H -->|否| J[显示验证错误]
-    J --> D
-```
-
-### 4. 表单提交流程图
-
-```mermaid
-flowchart TD
-    A[用户点击保存] --> B[表单验证]
-    B --> C{验证通过?}
-    C -->|否| D[显示验证错误]
-    D --> E[用户修正错误]
-    E --> B
-
-    C -->|是| F[数据转换]
-    F --> G[转换为API格式]
-    G --> H[提交请求]
-    H --> I{请求成功?}
-
-    I -->|是| J[显示成功消息]
-    J --> K[跳转到列表页面]
-
-    I -->|否| L[显示错误消息]
-    L --> M[提供重试选项]
-    M --> H
-
-    %% 加载状态
-    H --> N[显示加载状态]
-    N --> I
-```
-
-### 5. 数据流转图
-
-```mermaid
-flowchart LR
-    A[用户输入] --> B[表单组件]
-    B --> C[数据验证]
-    C --> D[状态管理]
-    D --> E[API调用]
-    E --> F[后端处理]
-    F --> G[数据库存储]
-
-    %% 反向数据流
-    G --> H[响应数据]
-    H --> I[状态更新]
-    I --> J[UI更新]
-    J --> K[用户反馈]
-
-    %% 错误处理流
-    C --> L{验证失败?}
-    L -->|是| M[错误提示]
-    M --> A
-
-    E --> N{请求失败?}
-    N -->|是| O[错误处理]
-    O --> K
-```
-
-## 组件状态管理设计
-
-### 1. 状态管理架构图
-
-```mermaid
-graph TB
-    A[全局状态管理] --> B[表单状态]
-    A --> C[UI状态]
-    A --> D[数据缓存状态]
-
-    %% 表单状态
-    B --> B1[formData<br/>表单数据]
-    B --> B2[validationErrors<br/>验证错误]
-    B --> B3[isDirty<br/>数据变更标识]
-    B --> B4[isSubmitting<br/>提交状态]
-
-    %% UI状态
-    C --> C1[loading<br/>加载状态]
-    C --> C2[activeStep<br/>当前步骤]
-    C --> C3[expandedSections<br/>展开的区域]
-    C --> C4[modalVisible<br/>弹窗显示状态]
-
-    %% 数据缓存状态
-    D --> D1[productList<br/>产品列表]
-    D --> D2[deviceList<br/>设备列表]
-    D --> D3[thingModelList<br/>物模型列表]
-    D --> D4[alertConfigList<br/>告警配置列表]
-
-    %% 状态操作
-    E[状态操作] --> E1[updateFormData<br/>更新表单数据]
-    E --> E2[validateForm<br/>验证表单]
-    E --> E3[resetForm<br/>重置表单]
-    E --> E4[submitForm<br/>提交表单]
-
-    %% 样式定义
-    classDef stateClass fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
-    classDef actionClass fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
-
-    class A,B,C,D stateClass
-    class E,E1,E2,E3,E4 actionClass
-```
-
-### 2. 组件通信图
-
-```mermaid
-sequenceDiagram
-    participant U as User
-    participant F as FormComponent
-    participant T as TriggerSection
-    participant A as ActionSection
-    participant S as StateManager
-    participant API as BackendAPI
-
-    U->>F: 填写基础信息
-    F->>S: updateFormData(basicInfo)
-    S-->>F: 状态更新完成
-
-    U->>T: 配置触发器
-    T->>S: updateTriggers(triggerData)
-    S->>API: loadDeviceList(productId)
-    API-->>S: 返回设备列表
-    S-->>T: 更新设备选项
-
-    U->>A: 配置执行器
-    A->>S: updateActions(actionData)
-    S-->>A: 状态更新完成
-
-    U->>F: 点击保存
-    F->>S: validateForm()
-    S-->>F: 验证结果
-
-    alt 验证通过
-        F->>S: submitForm()
-        S->>API: saveRuleScene(formData)
-        API-->>S: 保存结果
-        S-->>F: 显示成功消息
-    else 验证失败
-        S-->>F: 显示错误信息
-    end
-```
-
-### 3. 数据流向图
-
-```mermaid
-flowchart LR
-    A[用户操作] --> B[组件事件]
-    B --> C[状态更新]
-    C --> D[数据验证]
-    D --> E{验证通过?}
-
-    E -->|是| F[更新状态]
-    E -->|否| G[显示错误]
-
-    F --> H[触发副作用]
-    H --> I[API调用]
-    I --> J[更新缓存]
-    J --> K[UI重新渲染]
-
-    G --> L[用户修正]
-    L --> A
-
-    %% 样式
-    classDef processClass fill:#e8f5e8,stroke:#2e7d32,stroke-width:2px
-    classDef decisionClass fill:#fff3e0,stroke:#f57c00,stroke-width:2px
-    classDef errorClass fill:#ffebee,stroke:#d32f2f,stroke-width:2px
-
-    class A,B,C,D,F,H,I,J,K,L processClass
-    class E decisionClass
-    class G errorClass
-```
-
-## 用户体验优化
-
-### 1. 智能提示和帮助
-
-- **字段说明**:为复杂字段提供详细说明和示例
-- **实时验证**:输入时实时验证数据格式
-- **智能推荐**:根据已选择的产品推荐相关设备
-- **预览功能**:实时预览配置效果
-
-### 2. 错误处理和反馈
-
-- **表单验证**:清晰的错误提示信息
-- **数据加载**:加载状态和错误重试机制
-- **保存反馈**:明确的成功/失败反馈
-
-### 3. 操作便利性
-
-- **批量操作**:支持批量添加/删除触发器和执行器
-- **模板功能**:提供常用场景模板
-- **导入导出**:支持配置的导入和导出
-
-## 响应式设计考虑
-
-### 1. 移动端适配
-
-- **布局调整**:在小屏幕上采用垂直布局
-- **操作优化**:增大点击区域,优化触摸操作
-- **内容精简**:在移动端隐藏非必要信息
-
-### 2. 不同屏幕尺寸适配
-
-- **大屏幕**:充分利用空间,并排显示更多内容
-- **中等屏幕**:平衡内容密度和可读性
-- **小屏幕**:优先显示核心功能
-
-## 性能优化策略
-
-### 1. 组件懒加载
-
-```javascript
-// 懒加载复杂组件
-const DeviceTriggerConfig = defineAsyncComponent(() => 
-  import('./components/DeviceTriggerConfig.vue')
-);
-```
-
-### 2. 数据缓存
-
-```javascript
-// 缓存产品和设备数据
-const productCache = new Map();
-const deviceCache = new Map();
-```
-
-### 3. 防抖处理
-
-```javascript
-// 搜索防抖
-const debouncedSearch = debounce(searchDevices, 300);
-```
-
-## 可访问性设计
-
-### 1. 键盘导航
-
-- 支持Tab键在表单元素间导航
-- 提供快捷键操作
-
-### 2. 屏幕阅读器支持
-
-- 为表单元素提供适当的标签
-- 使用ARIA属性增强可访问性
-
-### 3. 颜色和对比度
-
-- 确保足够的颜色对比度
-- 不仅依赖颜色传达信息
-
-## 表单验证策略
-
-### 1. 验证层次结构图
-
-```mermaid
-graph TB
-    A[表单验证] --> B[字段级验证]
-    A --> C[组件级验证]
-    A --> D[表单级验证]
-    A --> E[业务级验证]
-
-    %% 字段级验证
-    B --> B1[必填验证]
-    B --> B2[格式验证]
-    B --> B3[长度验证]
-    B --> B4[类型验证]
-
-    %% 组件级验证
-    C --> C1[触发器验证]
-    C --> C2[执行器验证]
-    C --> C3[条件组合验证]
-
-    %% 表单级验证
-    D --> D1[数据完整性验证]
-    D --> D2[逻辑一致性验证]
-    D --> D3[依赖关系验证]
-
-    %% 业务级验证
-    E --> E1[设备权限验证]
-    E --> E2[产品可用性验证]
-    E --> E3[规则冲突验证]
-
-    %% 样式定义
-    classDef levelClass fill:#e1f5fe,stroke:#01579b,stroke-width:2px
-    classDef validationClass fill:#f3e5f5,stroke:#4a148c,stroke-width:2px
-
-    class A levelClass
-    class B,C,D,E levelClass
-    class B1,B2,B3,B4,C1,C2,C3,D1,D2,D3,E1,E2,E3 validationClass
-```
-
-### 2. 验证时机图
-
-```mermaid
-stateDiagram-v2
-    [*] --> 字段输入
-    字段输入 --> 实时验证: onChange
-    实时验证 --> 显示错误: 验证失败
-    实时验证 --> 清除错误: 验证通过
-    显示错误 --> 字段输入: 用户修正
-    清除错误 --> 字段输入: 继续输入
-
-    字段输入 --> 失焦验证: onBlur
-    失焦验证 --> 显示警告: 格式错误
-    失焦验证 --> 正常状态: 格式正确
-    显示警告 --> 字段输入: 重新聚焦
-    正常状态 --> 字段输入: 重新聚焦
-
-    字段输入 --> 表单提交: 用户提交
-    表单提交 --> 全量验证
-    全量验证 --> 提交成功: 验证通过
-    全量验证 --> 显示错误: 验证失败
-    提交成功 --> [*]
-    显示错误 --> 字段输入: 用户修正
-```
-
-## 测试策略
-
-### 1. 测试金字塔图
-
-```mermaid
-graph TB
-    A[测试金字塔] --> B[单元测试<br/>Unit Tests<br/>70%]
-    A --> C[集成测试<br/>Integration Tests<br/>20%]
-    A --> D[端到端测试<br/>E2E Tests<br/>10%]
-
-    %% 单元测试详细
-    B --> B1[组件渲染测试]
-    B --> B2[数据验证逻辑测试]
-    B --> B3[用户交互测试]
-    B --> B4[工具函数测试]
-
-    %% 集成测试详细
-    C --> C1[表单提交流程测试]
-    C --> C2[API调用测试]
-    C --> C3[数据转换测试]
-    C --> C4[组件间通信测试]
-
-    %% 端到端测试详细
-    D --> D1[完整用户流程测试]
-    D --> D2[浏览器兼容性测试]
-    D --> D3[响应式设计测试]
-    D --> D4[性能测试]
-
-    %% 样式定义
-    classDef pyramidClass fill:#e8f5e8,stroke:#2e7d32,stroke-width:3px
-    classDef unitClass fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
-    classDef integrationClass fill:#fff3e0,stroke:#f57c00,stroke-width:2px
-    classDef e2eClass fill:#ffebee,stroke:#d32f2f,stroke-width:2px
-
-    class A pyramidClass
-    class B,B1,B2,B3,B4 unitClass
-    class C,C1,C2,C3,C4 integrationClass
-    class D,D1,D2,D3,D4 e2eClass
-```
-
-### 2. 测试用例覆盖图
-
-```mermaid
-mindmap
-  root((测试用例覆盖))
-    功能测试
-      基础信息
-        名称输入验证
-        描述输入验证
-        状态切换测试
-      触发器配置
-        设备触发配置
-        定时触发配置
-        条件组合测试
-      执行器配置
-        设备控制配置
-        告警配置测试
-      表单提交
-        验证流程测试
-        保存流程测试
-
-    异常测试
-      网络异常
-        API调用失败
-        超时处理
-      数据异常
-        格式错误处理
-        空数据处理
-      用户异常
-        权限不足
-        操作冲突
-
-    性能测试
-      加载性能
-        首屏加载时间
-        组件渲染性能
-      交互性能
-        表单响应速度
-        数据处理性能
-      内存性能
-        内存泄漏检测
-        组件销毁测试
-
-    兼容性测试
-      浏览器兼容
-        Chrome测试
-        Firefox测试
-        Safari测试
-        Edge测试
-      设备兼容
-        桌面端测试
-        平板端测试
-        移动端测试
-```
-
-## 表单设计架构总览
-
-### 完整架构图
-
-```mermaid
-graph TB
-    %% 用户界面层
-    subgraph "用户界面层 (UI Layer)"
-        A[RuleSceneForm 主表单]
-        B[BasicInfoSection 基础信息]
-        C[TriggerSection 触发器配置]
-        D[ActionSection 执行器配置]
-        E[PreviewSection 预览区域]
-    end
-
-    %% 状态管理层
-    subgraph "状态管理层 (State Management)"
-        F[FormState 表单状态]
-        G[ValidationState 验证状态]
-        H[UIState 界面状态]
-        I[CacheState 缓存状态]
-    end
-
-    %% 业务逻辑层
-    subgraph "业务逻辑层 (Business Logic)"
-        J[FormValidator 表单验证器]
-        K[DataTransformer 数据转换器]
-        L[ConfigBuilder 配置构建器]
-        M[RuleEngine 规则引擎]
-    end
-
-    %% 数据访问层
-    subgraph "数据访问层 (Data Access)"
-        N[ProductAPI 产品接口]
-        O[DeviceAPI 设备接口]
-        P[RuleSceneAPI 规则场景接口]
-        Q[AlertAPI 告警接口]
-    end
-
-    %% 工具层
-    subgraph "工具层 (Utilities)"
-        R[CronValidator CRON验证器]
-        S[TypeChecker 类型检查器]
-        T[ErrorHandler 错误处理器]
-        U[Logger 日志记录器]
-    end
-
-    %% 连接关系
-    A --> B
-    A --> C
-    A --> D
-    A --> E
-
-    B --> F
-    C --> F
-    D --> F
-    E --> F
-
-    F --> J
-    G --> J
-    H --> K
-    I --> L
-
-    J --> M
-    K --> M
-    L --> M
-
-    M --> N
-    M --> O
-    M --> P
-    M --> Q
-
-    J --> R
-    K --> S
-    M --> T
-    A --> U
-
-    %% 样式定义
-    classDef uiClass fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
-    classDef stateClass fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
-    classDef businessClass fill:#e8f5e8,stroke:#2e7d32,stroke-width:2px
-    classDef dataClass fill:#fff3e0,stroke:#f57c00,stroke-width:2px
-    classDef utilClass fill:#fce4ec,stroke:#c2185b,stroke-width:2px
-
-    class A,B,C,D,E uiClass
-    class F,G,H,I stateClass
-    class J,K,L,M businessClass
-    class N,O,P,Q dataClass
-    class R,S,T,U utilClass
-```
-
-### 设计原则总结
-
-```mermaid
-mindmap
-  root((表单设计原则))
-    用户体验
-      直观易用
-        清晰的视觉层次
-        一致的交互模式
-        智能的操作提示
-      响应迅速
-        实时验证反馈
-        快速数据加载
-        流畅的动画效果
-      错误友好
-        明确的错误信息
-        便捷的错误修正
-        优雅的异常处理
-
-    技术架构
-      组件化设计
-        高内聚低耦合
-        可复用的组件
-        清晰的组件边界
-      状态管理
-        集中式状态管理
-        可预测的状态变更
-        高效的状态同步
-      数据流控制
-        单向数据流
-        明确的数据来源
-        可追踪的数据变更
-
-    质量保证
-      代码质量
-        类型安全
-        代码规范
-        充分的注释
-      测试覆盖
-        单元测试
-        集成测试
-        端到端测试
-      性能优化
-        懒加载
-        缓存策略
-        防抖节流
-
-    可维护性
-      模块化结构
-        清晰的目录结构
-        合理的文件组织
-        明确的依赖关系
-      文档完善
-        API文档
-        组件文档
-        使用说明
-      扩展性设计
-        插件化架构
-        配置化开发
-        版本兼容性
-```
-
-## 总结
-
-IoT场景联动规则表单设计需要考虑:
-
-### 1. 核心设计要点
-
-- **复杂性管理**:通过组件化和分步骤设计降低复杂度
-- **用户体验**:提供直观的操作界面和智能提示
-- **数据完整性**:完善的验证机制确保数据质量
-- **扩展性**:模块化设计支持功能扩展
-- **性能优化**:合理的加载和缓存策略
-- **可访问性**:确保所有用户都能正常使用
-
-### 2. 技术实现要点
-
-- **状态管理**:采用集中式状态管理,确保数据流的可控性
-- **组件设计**:高内聚低耦合的组件架构,提高代码复用性
-- **验证策略**:多层次的验证机制,保证数据质量
-- **错误处理**:完善的错误处理和用户反馈机制
-- **性能优化**:懒加载、缓存、防抖等优化策略
-
-### 3. 质量保证
-
-- **测试覆盖**:完整的测试金字塔,确保代码质量
-- **文档完善**:详细的设计文档和使用说明
-- **代码规范**:统一的编码规范和类型安全
-
-通过以上设计思路和详细的Mermaid图表,可以构建一个功能完善、用户友好、技术先进的IoT场景联动规则配置表单系统。

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

@@ -1,469 +0,0 @@
-// TODO @puhui999:这些后续需要删除哈
-# 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设备的建模需求。

+ 6 - 24
src/views/iot/rule/scene/form/configs/DeviceTriggerConfig.vue

@@ -8,25 +8,13 @@
       @change="handleDeviceChange"
     />
 
-    <!-- TODO @puhui999:这里有点冗余,建议去掉 -->
-    <!-- 设备状态变更提示 -->
-    <div v-if="trigger.type === TriggerTypeEnum.DEVICE_STATE_UPDATE" class="mt-8px">
-      <el-alert title="设备状态变更触发" type="info" :closable="false" show-icon>
-        <template #default>
-          <p class="m-0">当选中的设备上线或离线时将自动触发场景规则</p>
-          <p class="m-0 mt-4px text-12px text-[var(--el-text-color-secondary)]">无需配置额外的触发条件</p>
-        </template>
-      </el-alert>
-    </div>
-
     <!-- 条件组配置 -->
-    <div v-else-if="needsConditions" class="space-y-12px">
+    <div v-if="needsConditions" class="space-y-12px">
       <div class="flex items-center justify-between mb-12px">
         <div class="flex items-center gap-8px">
           <span class="text-14px font-500 text-[var(--el-text-color-primary)]">触发条件</span>
-          <!-- TODO @puhui999:去掉数量限制 -->
           <el-tag size="small" type="info">
-            {{ trigger.conditionGroups?.length || 0 }}/{{ maxConditionGroups }}
+            {{ trigger.conditionGroups?.length || 0 }}个条件组
           </el-tag>
         </div>
         <div class="flex items-center gap-8px">
@@ -52,18 +40,13 @@
           :key="`group-${groupIndex}`"
           class="border border-[var(--el-border-color-lighter)] rounded-6px bg-[var(--el-fill-color-blank)]"
         >
-          <div class="flex items-center justify-between p-12px px-16px bg-[var(--el-fill-color-light)] border-b border-[var(--el-border-color-lighter)]">
+          <div
+            class="flex items-center justify-between p-12px px-16px bg-[var(--el-fill-color-light)] border-b border-[var(--el-border-color-lighter)]"
+          >
             <div class="flex items-center text-14px font-500 text-[var(--el-text-color-primary)]">
               <span>条件组 {{ groupIndex + 1 }}</span>
               <!-- TODO @puhui999:不用“且、或”哈。条件组之间,就是或;条件之间就是且 -->
-              <el-select
-                v-model="group.logicOperator"
-                size="small"
-                class="w-80px ml-12px"
-              >
-                <el-option label="且" value="AND" />
-                <el-option label="或" value="OR" />
-              </el-select>
+              <el-tag size="small" type="info" class="ml-8px">条件间为"且"关系</el-tag>
             </div>
             <el-button
               type="danger"
@@ -262,5 +245,4 @@ watch(
     updateValidationResult()
   }
 )
-// TODO @puhui999:unocss - 已完成转换
 </script>

+ 8 - 56
src/views/iot/rule/scene/form/configs/TimerTriggerConfig.vue

@@ -1,52 +1,23 @@
 <!-- 定时触发配置组件 -->
 <template>
   <div class="flex flex-col gap-16px">
-    <div class="flex items-center justify-between p-12px px-16px bg-[var(--el-fill-color-light)] rounded-6px border border-[var(--el-border-color-lighter)]">
-      <div class="flex items-center gap-8px">
-        <Icon icon="ep:timer" class="text-[var(--el-color-danger)] text-18px" />
-        <span class="text-14px font-500 text-[var(--el-text-color-primary)]">定时触发配置</span>
-      </div>
-      <div class="flex items-center gap-8px">
-        <el-button type="text" size="small" @click="showBuilder = !showBuilder">
-          <Icon :icon="showBuilder ? 'ep:edit' : 'ep:setting'" />
-          {{ showBuilder ? '手动编辑' : '可视化编辑' }}
-        </el-button>
-      </div>
+    <div class="flex items-center gap-8px p-12px px-16px bg-[var(--el-fill-color-light)] rounded-6px border border-[var(--el-border-color-lighter)]">
+      <Icon icon="ep:timer" class="text-[var(--el-color-danger)] text-18px" />
+      <span class="text-14px font-500 text-[var(--el-text-color-primary)]">定时触发配置</span>
     </div>
 
-    <!-- 可视化编辑器 -->
-    <!-- TODO @puhui999:是不是复用现有的 cron 组件;不然有点重复哈;维护比较复杂 -->
-    <div v-if="showBuilder" class="p-16px border border-[var(--el-border-color-lighter)] rounded-6px bg-[var(--el-fill-color-blank)]">
-      <CronBuilder v-model="localValue" @validate="handleValidate" />
-    </div>
-
-    <!-- 手动编辑 -->
-    <div v-else class="p-16px border border-[var(--el-border-color-lighter)] rounded-6px bg-[var(--el-fill-color-blank)]">
+    <!-- CRON表达式配置 -->
+    <div class="p-16px border border-[var(--el-border-color-lighter)] rounded-6px bg-[var(--el-fill-color-blank)]">
       <el-form-item label="CRON表达式" required>
-        <CronInput v-model="localValue" @validate="handleValidate" />
+        <Crontab v-model="localValue" />
       </el-form-item>
     </div>
-
-    <!-- 下次执行时间预览 -->
-    <NextExecutionPreview :cron-expression="localValue" />
-
-    <!-- 验证结果 -->
-    <div v-if="validationMessage" class="mt-8px">
-      <el-alert
-        :title="validationMessage"
-        :type="isValid ? 'success' : 'error'"
-        :closable="false"
-        show-icon
-      />
-    </div>
   </div>
 </template>
 
 <script setup lang="ts">
 import { useVModel } from '@vueuse/core'
-import CronBuilder from '../inputs/CronBuilder.vue'
-import CronInput from '../inputs/CronInput.vue'
-import NextExecutionPreview from '../previews/NextExecutionPreview.vue'
+import { Crontab } from '@/components/Crontab'
 
 /** 定时触发配置组件 */
 defineOptions({ name: 'TimerTriggerConfig' })
@@ -66,23 +37,4 @@ const emit = defineEmits<Emits>()
 const localValue = useVModel(props, 'modelValue', emit, {
   defaultValue: '0 0 12 * * ?'
 })
-
-// 状态
-const showBuilder = ref(true)
-const validationMessage = ref('')
-const isValid = ref(true)
-
-// 事件处理
-const handleValidate = (result: { valid: boolean; message: string }) => {
-  isValid.value = result.valid
-  validationMessage.value = result.message
-  emit('validate', result)
-}
-
-// 初始验证
-onMounted(() => {
-  handleValidate({ valid: true, message: '定时触发配置验证通过' })
-})
-</script>
-
-
+</script>

+ 0 - 242
src/views/iot/rule/scene/form/inputs/CronBuilder.vue

@@ -1,242 +0,0 @@
-<!-- CRON 可视化构建器组件 -->
-<!-- TODO @puhui999:看看能不能复用全局的 cron 组件 -->
-<template>
-  <div class="cron-builder">
-    <div class="builder-header">
-      <span class="header-title">可视化 CRON 编辑器</span>
-    </div>
-
-    <div class="builder-content">
-      <!-- 快捷选项 -->
-      <div class="quick-options">
-        <span class="options-label">常用配置:</span>
-        <el-button
-          v-for="option in quickOptions"
-          :key="option.label"
-          size="small"
-          @click="applyQuickOption(option)"
-        >
-          {{ option.label }}
-        </el-button>
-      </div>
-
-      <!-- 详细配置 -->
-      <div class="detailed-config">
-        <el-row :gutter="16">
-          <el-col :span="4">
-            <el-form-item label="秒">
-              <el-select v-model="cronParts.second" @change="updateCronExpression">
-                <el-option label="每秒" value="*" />
-                <el-option label="0秒" value="0" />
-              </el-select>
-            </el-form-item>
-          </el-col>
-          <el-col :span="4">
-            <el-form-item label="分钟">
-              <el-select v-model="cronParts.minute" @change="updateCronExpression">
-                <el-option label="每分钟" value="*" />
-                <el-option
-                  v-for="i in 60"
-                  :key="i - 1"
-                  :label="`${i - 1}分`"
-                  :value="String(i - 1)"
-                />
-              </el-select>
-            </el-form-item>
-          </el-col>
-          <el-col :span="4">
-            <el-form-item label="小时">
-              <el-select v-model="cronParts.hour" @change="updateCronExpression">
-                <el-option label="每小时" value="*" />
-                <el-option
-                  v-for="i in 24"
-                  :key="i - 1"
-                  :label="`${i - 1}时`"
-                  :value="String(i - 1)"
-                />
-              </el-select>
-            </el-form-item>
-          </el-col>
-          <el-col :span="4">
-            <el-form-item label="日">
-              <el-select v-model="cronParts.day" @change="updateCronExpression">
-                <el-option label="每日" value="*" />
-                <el-option v-for="i in 31" :key="i" :label="`${i}日`" :value="String(i)" />
-              </el-select>
-            </el-form-item>
-          </el-col>
-          <el-col :span="4">
-            <el-form-item label="月">
-              <el-select v-model="cronParts.month" @change="updateCronExpression">
-                <el-option label="每月" value="*" />
-                <el-option
-                  v-for="(month, index) in months"
-                  :key="index"
-                  :label="month"
-                  :value="String(index + 1)"
-                />
-              </el-select>
-            </el-form-item>
-          </el-col>
-          <el-col :span="4">
-            <el-form-item label="周">
-              <el-select v-model="cronParts.week" @change="updateCronExpression">
-                <el-option label="每周" value="*" />
-                <el-option
-                  v-for="(week, index) in weeks"
-                  :key="index"
-                  :label="week"
-                  :value="String(index)"
-                />
-              </el-select>
-            </el-form-item>
-          </el-col>
-        </el-row>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-import { useVModel } from '@vueuse/core'
-
-/** CRON 可视化构建器组件 */
-defineOptions({ name: 'CronBuilder' })
-
-interface Props {
-  modelValue: string
-}
-
-interface Emits {
-  (e: 'update:modelValue', value: string): void
-  (e: 'validate', result: { valid: boolean; message: string }): void
-}
-
-const props = defineProps<Props>()
-const emit = defineEmits<Emits>()
-
-const localValue = useVModel(props, 'modelValue', emit)
-
-// CRON 各部分
-const cronParts = reactive({
-  second: '0',
-  minute: '0',
-  hour: '12',
-  day: '*',
-  month: '*',
-  week: '?'
-})
-
-// 常量数据
-const months = [
-  '1月',
-  '2月',
-  '3月',
-  '4月',
-  '5月',
-  '6月',
-  '7月',
-  '8月',
-  '9月',
-  '10月',
-  '11月',
-  '12月'
-]
-const weeks = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
-
-// 快捷选项
-const quickOptions = [
-  { label: '每分钟', cron: '0 * * * * ?' },
-  { label: '每小时', cron: '0 0 * * * ?' },
-  { label: '每天中午', cron: '0 0 12 * * ?' },
-  { label: '每天凌晨', cron: '0 0 0 * * ?' },
-  { label: '工作日9点', cron: '0 0 9 * * MON-FRI' },
-  { label: '每周一', cron: '0 0 9 * * MON' }
-]
-
-// 方法
-const updateCronExpression = () => {
-  localValue.value = `${cronParts.second} ${cronParts.minute} ${cronParts.hour} ${cronParts.day} ${cronParts.month} ${cronParts.week}`
-  emit('validate', { valid: true, message: 'CRON表达式验证通过' })
-}
-
-const applyQuickOption = (option: any) => {
-  localValue.value = option.cron
-  parseCronExpression()
-  emit('validate', { valid: true, message: 'CRON表达式验证通过' })
-}
-
-const parseCronExpression = () => {
-  if (!localValue.value) return
-
-  const parts = localValue.value.split(' ')
-  if (parts.length >= 6) {
-    cronParts.second = parts[0] || '0'
-    cronParts.minute = parts[1] || '0'
-    cronParts.hour = parts[2] || '12'
-    cronParts.day = parts[3] || '*'
-    cronParts.month = parts[4] || '*'
-    cronParts.week = parts[5] || '?'
-  }
-}
-
-// 初始化
-onMounted(() => {
-  if (localValue.value) {
-    parseCronExpression()
-  } else {
-    updateCronExpression()
-  }
-})
-</script>
-
-<style scoped>
-.cron-builder {
-  border: 1px solid var(--el-border-color-light);
-  border-radius: 6px;
-  background: var(--el-fill-color-blank);
-}
-
-.builder-header {
-  padding: 12px 16px;
-  background: var(--el-fill-color-light);
-  border-bottom: 1px solid var(--el-border-color-lighter);
-}
-
-.header-title {
-  font-size: 14px;
-  font-weight: 500;
-  color: var(--el-text-color-primary);
-}
-
-.builder-content {
-  padding: 16px;
-}
-
-.quick-options {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  margin-bottom: 16px;
-  flex-wrap: wrap;
-}
-
-.options-label {
-  font-weight: 500;
-  color: var(--el-text-color-secondary);
-  white-space: nowrap;
-}
-
-.detailed-config {
-  margin-top: 16px;
-}
-
-:deep(.el-form-item) {
-  margin-bottom: 0;
-}
-
-:deep(.el-form-item__label) {
-  font-size: 12px;
-  color: var(--el-text-color-secondary);
-}
-</style>

+ 0 - 141
src/views/iot/rule/scene/form/inputs/CronInput.vue

@@ -1,141 +0,0 @@
-<!-- CRON 表达式输入组件 -->
-<!-- TODO @puhui999:看看能不能复用全局的 cron 组件 -->
-<template>
-  <div class="cron-input">
-    <el-input
-      v-model="localValue"
-      placeholder="请输入 CRON 表达式,如:0 0 12 * * ?"
-      @blur="handleBlur"
-      @input="handleInput"
-    >
-      <template #suffix>
-        <el-tooltip content="CRON 表达式帮助" placement="top">
-          <Icon icon="ep:question-filled" class="input-help" @click="showHelp = !showHelp" />
-        </el-tooltip>
-      </template>
-    </el-input>
-
-    <!-- 帮助信息 -->
-    <div v-if="showHelp" class="cron-help">
-      <el-alert title="CRON 表达式格式:秒 分 时 日 月 周" type="info" :closable="false" show-icon>
-        <template #default>
-          <div class="help-content">
-            <p><strong>示例:</strong></p>
-            <ul>
-              <li><code>0 0 12 * * ?</code> - 每天中午12点执行</li>
-              <li><code>0 */5 * * * ?</code> - 每5分钟执行一次</li>
-              <li><code>0 0 9-17 * * MON-FRI</code> - 工作日9-17点每小时执行</li>
-            </ul>
-            <p><strong>特殊字符:</strong></p>
-            <ul>
-              <li><code>*</code> - 匹配任意值</li>
-              <li><code>?</code> - 不指定值(用于日和周)</li>
-              <li><code>/</code> - 间隔触发,如 */5 表示每5个单位</li>
-              <li><code>-</code> - 范围,如 9-17 表示9到17</li>
-              <li><code>,</code> - 列举,如 MON,WED,FRI</li>
-            </ul>
-          </div>
-        </template>
-      </el-alert>
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-import { useVModel } from '@vueuse/core'
-import { validateCronExpression } from '../../utils/validation'
-
-/** CRON 表达式输入组件 */
-defineOptions({ name: 'CronInput' })
-
-interface Props {
-  modelValue: string
-}
-
-interface Emits {
-  (e: 'update:modelValue', value: string): void
-  (e: 'validate', result: { valid: boolean; message: string }): void
-}
-
-const props = defineProps<Props>()
-const emit = defineEmits<Emits>()
-
-const localValue = useVModel(props, 'modelValue', emit)
-
-// 状态
-const showHelp = ref(false)
-
-// 事件处理
-const handleInput = () => {
-  validateExpression()
-}
-
-const handleBlur = () => {
-  validateExpression()
-}
-
-const validateExpression = () => {
-  if (!localValue.value) {
-    emit('validate', { valid: false, message: '请输入CRON表达式' })
-    return
-  }
-
-  const isValid = validateCronExpression(localValue.value)
-  if (isValid) {
-    emit('validate', { valid: true, message: 'CRON表达式验证通过' })
-  } else {
-    emit('validate', { valid: false, message: 'CRON表达式格式不正确' })
-  }
-}
-
-// 监听值变化
-watch(
-  () => localValue.value,
-  () => {
-    validateExpression()
-  }
-)
-
-// 初始化
-onMounted(() => {
-  if (localValue.value) {
-    validateExpression()
-  }
-})
-</script>
-
-<style scoped>
-.cron-input {
-  width: 100%;
-}
-
-.input-help {
-  color: var(--el-text-color-placeholder);
-  cursor: pointer;
-  transition: color 0.2s;
-}
-
-.input-help:hover {
-  color: var(--el-color-primary);
-}
-
-.cron-help {
-  margin-top: 8px;
-}
-
-.help-content ul {
-  margin: 8px 0 0 0;
-  padding-left: 20px;
-}
-
-.help-content li {
-  margin-bottom: 4px;
-}
-
-.help-content code {
-  background: var(--el-fill-color-light);
-  padding: 2px 4px;
-  border-radius: 2px;
-  font-family: 'Courier New', monospace;
-}
-</style>

+ 0 - 178
src/views/iot/rule/scene/form/inputs/DescriptionInput.vue

@@ -1,178 +0,0 @@
-<!-- 场景描述输入组件 -->
-<template>
-  <div class="relative w-full">
-    <el-input
-      ref="inputRef"
-      v-model="localValue"
-      type="textarea"
-      placeholder="请输入场景描述(可选)"
-      :rows="3"
-      maxlength="200"
-      show-word-limit
-      resize="none"
-      @input="handleInput"
-    />
-
-    <!-- 描述模板 -->
-    <teleport to="body">
-      <div v-if="showTemplates" ref="templateDropdownRef" class="fixed z-1000 bg-white border border-[var(--el-border-color-light)] rounded-6px shadow-[var(--el-box-shadow)] min-w-300px max-w-400px" :style="dropdownStyle">
-        <div class="flex items-center justify-between p-12px border-b border-[var(--el-border-color-lighter)] bg-[var(--el-fill-color-light)]">
-          <span class="text-14px font-500 text-[var(--el-text-color-primary)]">描述模板</span>
-          <el-button type="text" size="small" @click="showTemplates = false">
-            <Icon icon="ep:close" />
-          </el-button>
-        </div>
-        <div class="max-h-300px overflow-y-auto">
-          <div
-            v-for="template in descriptionTemplates"
-            :key="template.title"
-            class="p-12px border-b border-[var(--el-border-color-lighter)] cursor-pointer transition-colors duration-200 hover:bg-[var(--el-fill-color-light)] last:border-b-0"
-            @click="applyTemplate(template)"
-          >
-            <div class="text-14px font-500 text-[var(--el-text-color-primary)] mb-4px">{{ template.title }}</div>
-            <div class="text-12px text-[var(--el-text-color-secondary)] leading-relaxed">{{ template.content }}</div>
-          </div>
-        </div>
-      </div>
-    </teleport>
-
-    <!-- TODO @puhui999:不用模版哈,简单点。。。 -->
-    <!-- 模板按钮 -->
-    <div v-if="!localValue && !showTemplates" class="absolute top-2px right-2px">
-      <el-button type="text" size="small" @click="toggleTemplates">
-        <Icon icon="ep:document" class="mr-1" />
-        使用模板
-      </el-button>
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-import { useVModel } from '@vueuse/core'
-
-/** 场景描述输入组件 */
-defineOptions({ name: 'DescriptionInput' })
-
-interface Props {
-  modelValue?: string
-}
-
-interface Emits {
-  (e: 'update:modelValue', value: string): void
-}
-
-const props = defineProps<Props>()
-const emit = defineEmits<Emits>()
-
-const localValue = useVModel(props, 'modelValue', emit, {
-  defaultValue: ''
-})
-
-const showTemplates = ref(false)
-const templateDropdownRef = ref()
-const inputRef = ref()
-const dropdownStyle = ref({})
-
-// 描述模板
-const descriptionTemplates = [
-  {
-    title: '温度控制场景',
-    content: '当环境温度超过设定阈值时,自动启动空调降温设备,确保环境温度保持在舒适范围内。'
-  },
-  {
-    title: '设备监控场景',
-    content: '实时监控关键设备的运行状态,当设备出现异常或离线时,立即发送告警通知相关人员。'
-  },
-  {
-    title: '节能控制场景',
-    content: '根据时间段和环境条件,自动调节设备功率或关闭非必要设备,实现智能节能管理。'
-  },
-  {
-    title: '安防联动场景',
-    content: '当检测到异常情况时,自动触发安防设备联动,包括报警器、摄像头录制等安全措施。'
-  },
-  {
-    title: '定时任务场景',
-    content: '按照预设的时间计划,定期执行设备检查、数据备份或系统维护等自动化任务。'
-  }
-]
-
-// 计算下拉框位置
-const calculateDropdownPosition = () => {
-  if (!inputRef.value) return
-
-  const inputElement = inputRef.value.$el || inputRef.value
-  const rect = inputElement.getBoundingClientRect()
-  const viewportHeight = window.innerHeight
-  const dropdownHeight = 300 // 预估下拉框高度
-
-  let top = rect.bottom + 4
-  let left = rect.left
-
-  // 如果下方空间不够,显示在上方
-  if (top + dropdownHeight > viewportHeight) {
-    top = rect.top - dropdownHeight - 4
-  }
-
-  // 确保不超出左右边界
-  const maxLeft = window.innerWidth - 400 // 下拉框最大宽度
-  if (left > maxLeft) {
-    left = maxLeft
-  }
-  if (left < 10) {
-    left = 10
-  }
-
-  dropdownStyle.value = {
-    top: `${top}px`,
-    left: `${left}px`
-  }
-}
-
-const handleInput = (value: string) => {
-  if (value.length > 0) {
-    showTemplates.value = false
-  }
-}
-
-const applyTemplate = (template: any) => {
-  localValue.value = template.content
-  showTemplates.value = false
-}
-
-const toggleTemplates = () => {
-  showTemplates.value = !showTemplates.value
-  if (showTemplates.value) {
-    nextTick(() => {
-      calculateDropdownPosition()
-    })
-  }
-}
-
-// 点击外部关闭下拉框
-const handleClickOutside = (event: Event) => {
-  if (
-    templateDropdownRef.value &&
-    !templateDropdownRef.value.contains(event.target as Node) &&
-    inputRef.value &&
-    !inputRef.value.$el.contains(event.target as Node)
-  ) {
-    showTemplates.value = false
-  }
-}
-
-// 监听窗口大小变化和点击事件
-onMounted(() => {
-  window.addEventListener('resize', calculateDropdownPosition)
-  window.addEventListener('scroll', calculateDropdownPosition)
-  document.addEventListener('click', handleClickOutside)
-})
-
-onUnmounted(() => {
-  window.removeEventListener('resize', calculateDropdownPosition)
-  window.removeEventListener('scroll', calculateDropdownPosition)
-  document.removeEventListener('click', handleClickOutside)
-})
-</script>
-
-

+ 0 - 111
src/views/iot/rule/scene/form/inputs/NameInput.vue

@@ -1,111 +0,0 @@
-<!-- 场景名称输入组件 -->
-<template>
-  <div class="relative w-full">
-    <el-input
-      v-model="localValue"
-      placeholder="请输入场景名称"
-      maxlength="50"
-      show-word-limit
-      clearable
-      @blur="handleBlur"
-      @input="handleInput"
-    >
-      <template #prefix>
-        <Icon icon="ep:edit" class="text-[var(--el-text-color-placeholder)]" />
-      </template>
-    </el-input>
-
-    <!-- 智能提示 -->
-    <!-- TODO @puhui999:暂时不用考虑智能推荐哈。用途不大 -->
-    <div v-if="showSuggestions && suggestions.length > 0" class="absolute top-full left-0 right-0 z-1000 bg-white border border-[var(--el-border-color-light)] rounded-4px shadow-[var(--el-box-shadow-light)] mt-4px">
-      <div class="p-8px px-12px border-b border-[var(--el-border-color-lighter)] bg-[var(--el-fill-color-light)]">
-        <span class="text-12px text-[var(--el-text-color-secondary)] font-500">推荐名称</span>
-      </div>
-      <div class="max-h-200px overflow-y-auto">
-        <div
-          v-for="suggestion in suggestions"
-          :key="suggestion"
-          class="p-8px px-12px cursor-pointer transition-colors duration-200 text-14px text-[var(--el-text-color-primary)] hover:bg-[var(--el-fill-color-light)] last:border-b-0"
-          @click="applySuggestion(suggestion)"
-        >
-          {{ suggestion }}
-        </div>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-import { useVModel } from '@vueuse/core'
-
-/** 场景名称输入组件 */
-defineOptions({ name: 'NameInput' })
-
-interface Props {
-  modelValue: string
-}
-
-interface Emits {
-  (e: 'update:modelValue', value: string): void
-}
-
-const props = defineProps<Props>()
-const emit = defineEmits<Emits>()
-
-const localValue = useVModel(props, 'modelValue', emit)
-
-// 智能提示相关
-const showSuggestions = ref(false)
-const suggestions = ref<string[]>([])
-
-// 常用场景名称模板
-const nameTemplates = [
-  '温度过高自动降温',
-  '设备离线告警通知',
-  '湿度异常自动调节',
-  '夜间安防模式启动',
-  '能耗超标自动关闭',
-  '故障设备自动重启',
-  '定时设备状态检查',
-  '环境数据异常告警'
-]
-
-const handleInput = (value: string) => {
-  if (value.length > 0 && value.length < 10) {
-    // 根据输入内容过滤建议
-    suggestions.value = nameTemplates
-      .filter(
-        (template) =>
-          template.includes(value) || (value.includes('温度') && template.includes('温度'))
-      )
-      .slice(0, 5)
-    showSuggestions.value = suggestions.value.length > 0
-  } else {
-    showSuggestions.value = false
-  }
-}
-
-const handleBlur = () => {
-  // 延迟隐藏建议,允许点击建议项
-  setTimeout(() => {
-    showSuggestions.value = false
-  }, 200)
-}
-
-const applySuggestion = (suggestion: string) => {
-  localValue.value = suggestion
-  showSuggestions.value = false
-}
-
-// 监听外部点击隐藏建议
-onMounted(() => {
-  document.addEventListener('click', (e) => {
-    const target = e.target as HTMLElement
-    if (!target.closest('.name-input')) {
-      showSuggestions.value = false
-    }
-  })
-})
-</script>
-
-

+ 0 - 158
src/views/iot/rule/scene/form/inputs/StatusRadio.vue

@@ -1,158 +0,0 @@
-<!-- 场景状态选择组件 -->
-<template>
-  <div class="status-radio">
-    <el-radio-group 
-      v-model="localValue" 
-      @change="handleChange"
-    >
-      <el-radio :value="0" class="status-option">
-        <div class="status-content">
-          <div class="status-indicator enabled"></div>
-          <div class="status-info">
-            <div class="status-label">启用</div>
-            <div class="status-desc">场景规则生效,满足条件时自动执行</div>
-          </div>
-        </div>
-      </el-radio>
-      
-      <el-radio :value="1" class="status-option">
-        <div class="status-content">
-          <div class="status-indicator disabled"></div>
-          <div class="status-info">
-            <div class="status-label">禁用</div>
-            <div class="status-desc">场景规则暂停,不会触发任何执行动作</div>
-          </div>
-        </div>
-      </el-radio>
-    </el-radio-group>
-
-  </div>
-</template>
-
-<script setup lang="ts">
-import { useVModel } from '@vueuse/core'
-
-/** 场景状态选择组件 */
-defineOptions({ name: 'StatusRadio' })
-
-interface Props {
-  modelValue: number
-}
-
-interface Emits {
-  (e: 'update:modelValue', value: number): void
-  (e: 'change', value: number): void
-}
-
-const props = defineProps<Props>()
-const emit = defineEmits<Emits>()
-
-const localValue = useVModel(props, 'modelValue', emit)
-
-const handleChange = (value: number) => {
-  emit('change', value)
-}
-</script>
-
-<style scoped>
-.status-radio {
-  width: 100%;
-}
-
-.status-radio :deep(.el-radio) {
-  margin-bottom: 8px;
-}
-
-.status-radio :deep(.el-radio:last-child) {
-  margin-bottom: 0;
-}
-
-:deep(.el-radio-group) {
-  display: flex;
-  flex-direction: row;
-  gap: 16px;
-  width: 100%;
-  align-items: flex-start;
-}
-
-:deep(.el-radio) {
-  margin-right: 0;
-  width: auto;
-  flex: 1;
-  height: auto;
-  align-items: flex-start;
-}
-
-.status-option {
-  width: auto;
-  flex: 1;
-}
-
-:deep(.el-radio__input) {
-  margin-top: 12px;
-  flex-shrink: 0;
-}
-
-:deep(.el-radio__label) {
-  width: 100%;
-  padding-left: 8px;
-}
-
-.status-content {
-  display: flex;
-  align-items: flex-start;
-  gap: 12px;
-  padding: 12px;
-  border: 1px solid var(--el-border-color-light);
-  border-radius: 6px;
-  transition: all 0.2s;
-  width: 100%;
-  margin-left: 0;
-}
-
-:deep(.el-radio.is-checked) .status-content {
-  border-color: var(--el-color-primary);
-  background: var(--el-color-primary-light-9);
-}
-
-.status-content:hover {
-  border-color: var(--el-color-primary-light-3);
-}
-
-.status-indicator {
-  width: 12px;
-  height: 12px;
-  border-radius: 50%;
-  margin-top: 4px;
-  flex-shrink: 0;
-}
-
-.status-indicator.enabled {
-  background: var(--el-color-success);
-  box-shadow: 0 0 0 2px var(--el-color-success-light-8);
-}
-
-.status-indicator.disabled {
-  background: var(--el-color-danger);
-  box-shadow: 0 0 0 2px var(--el-color-danger-light-8);
-}
-
-.status-info {
-  flex: 1;
-}
-
-.status-label {
-  font-size: 14px;
-  font-weight: 500;
-  color: var(--el-text-color-primary);
-  margin-bottom: 4px;
-}
-
-.status-desc {
-  font-size: 12px;
-  color: var(--el-text-color-secondary);
-  line-height: 1.4;
-}
-
-
-</style>

+ 27 - 11
src/views/iot/rule/scene/form/sections/BasicInfoSection.vue

@@ -16,23 +16,42 @@
     </template>
 
     <div class="p-0">
-      <el-row :gutter="24">
-        <!-- TODO @puhui999:NameInput、StatusRadio、DescriptionInput 是不是直接写在当前界面哈。有点散; -->
+      <el-row :gutter="24" class="mb-24px">
         <el-col :span="12">
           <el-form-item label="场景名称" prop="name" required>
-            <NameInput v-model="formData.name" />
+            <el-input
+              v-model="formData.name"
+              placeholder="请输入场景名称"
+              maxlength="50"
+              show-word-limit
+              clearable
+            />
           </el-form-item>
         </el-col>
-        <!-- TODO @puhui999:每个一行会好点? -->
         <el-col :span="12">
           <el-form-item label="场景状态" prop="status" required>
-            <StatusRadio v-model="formData.status" />
+            <el-radio-group v-model="formData.status">
+              <el-radio
+                v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
+                :key="dict.value"
+                :label="dict.value"
+              >
+                {{ dict.label }}
+              </el-radio>
+            </el-radio-group>
           </el-form-item>
         </el-col>
       </el-row>
-
       <el-form-item label="场景描述" prop="description">
-        <DescriptionInput v-model="formData.description" />
+        <el-input
+          v-model="formData.description"
+          type="textarea"
+          placeholder="请输入场景描述(可选)"
+          :rows="3"
+          maxlength="200"
+          show-word-limit
+          resize="none"
+        />
       </el-form-item>
     </div>
   </el-card>
@@ -40,9 +59,7 @@
 
 <script setup lang="ts">
 import { useVModel } from '@vueuse/core'
-import NameInput from '../inputs/NameInput.vue'
-import DescriptionInput from '../inputs/DescriptionInput.vue'
-import StatusRadio from '../inputs/StatusRadio.vue'
+import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
 import { RuleSceneFormData } from '@/api/iot/rule/scene/scene.types'
 
 /** 基础信息配置组件 */
@@ -61,7 +78,6 @@ const props = defineProps<Props>()
 const emit = defineEmits<Emits>()
 
 const formData = useVModel(props, 'modelValue', emit)
-// TODO @puhui999:看看能不能 unocss
 </script>
 
 <style scoped>

+ 47 - 99
src/views/iot/rule/scene/form/sections/TriggerSection.vue

@@ -6,15 +6,13 @@
         <div class="flex items-center gap-8px">
           <Icon icon="ep:lightning" class="text-[var(--el-color-primary)] text-18px" />
           <span class="text-16px font-600 text-[var(--el-text-color-primary)]">触发器配置</span>
-          <!-- TODO @puhui999:是不是去掉 maxTriggers;计数 -->
-          <el-tag size="small" type="info">{{ triggers.length }}/{{ maxTriggers }}</el-tag>
+          <el-tag size="small" type="info">{{ triggers.length }}个触发器</el-tag>
         </div>
         <div class="flex items-center gap-8px">
           <el-button
             type="primary"
             size="small"
             @click="addTrigger"
-            :disabled="triggers.length >= maxTriggers"
           >
             <Icon icon="ep:plus" />
             添加触发器
@@ -26,13 +24,7 @@
     <div class="p-0">
       <!-- 空状态 -->
       <div v-if="triggers.length === 0">
-        <el-empty description="暂无触发器配置">
-          <!-- TODO @puhui999:这个要不要去掉哈;入口统一点 -->
-          <el-button type="primary" @click="addTrigger">
-            <Icon icon="ep:plus" />
-            添加第一个触发器
-          </el-button>
-        </el-empty>
+        <el-empty description="暂无触发器配置,请点击右上角添加触发器按钮开始配置" />
       </div>
 
       <!-- 触发器列表 -->
@@ -62,18 +54,28 @@
 
           <div class="space-y-16px">
             <!-- 触发类型选择 -->
-            <TriggerTypeSelector
-              :model-value="trigger.type"
-              @update:model-value="(value) => updateTriggerType(index, value)"
-              @change="onTriggerTypeChange(trigger, $event)"
-            />
+            <el-form-item label="触发类型" required>
+              <el-select
+                :model-value="trigger.type"
+                @update:model-value="(value) => updateTriggerType(index, value)"
+                @change="onTriggerTypeChange(trigger, $event)"
+                placeholder="请选择触发类型"
+                class="w-full"
+              >
+                <el-option
+                  v-for="option in triggerTypeOptions"
+                  :key="option.value"
+                  :label="option.label"
+                  :value="option.value"
+                />
+              </el-select>
+            </el-form-item>
 
             <!-- 设备触发配置 -->
             <DeviceTriggerConfig
               v-if="isDeviceTrigger(trigger.type)"
               :model-value="trigger"
               @update:model-value="(value) => updateTrigger(index, value)"
-              @validate="(result) => handleTriggerValidate(index, result)"
             />
 
             <!-- 定时触发配置 -->
@@ -81,38 +83,16 @@
               v-if="trigger.type === TriggerTypeEnum.TIMER"
               :model-value="trigger.cronExpression"
               @update:model-value="(value) => updateTriggerCronExpression(index, value)"
-              @validate="(result) => handleTriggerValidate(index, result)"
             />
           </div>
         </div>
       </div>
-
-      <!-- 添加提示 -->
-      <!-- TODO @puhui999:这个要不要去掉哈;入口统一点 -->
-      <div v-if="triggers.length > 0 && triggers.length < maxTriggers" class="text-center py-16px">
-        <el-button type="primary" plain @click="addTrigger">
-          <Icon icon="ep:plus" />
-          继续添加触发器
-        </el-button>
-        <span class="block mt-8px text-12px text-[var(--el-text-color-secondary)]"> 最多可添加 {{ maxTriggers }} 个触发器 </span>
-      </div>
-
-      <!-- 验证结果 -->
-      <div v-if="validationMessage" class="validation-result">
-        <el-alert
-          :title="validationMessage"
-          :type="isValid ? 'success' : 'error'"
-          :closable="false"
-          show-icon
-        />
-      </div>
     </div>
   </el-card>
 </template>
 
 <script setup lang="ts">
 import { useVModel } from '@vueuse/core'
-import TriggerTypeSelector from '../selectors/TriggerTypeSelector.vue'
 import DeviceTriggerConfig from '../configs/DeviceTriggerConfig.vue'
 import TimerTriggerConfig from '../configs/TimerTriggerConfig.vue'
 import {
@@ -129,7 +109,6 @@ interface Props {
 
 interface Emits {
   (e: 'update:triggers', value: TriggerFormData[]): void
-  (e: 'validate', result: { valid: boolean; message: string }): void
 }
 
 const props = defineProps<Props>()
@@ -153,13 +132,33 @@ const createDefaultTriggerData = (): TriggerFormData => {
   }
 }
 
-// 配置常量
-const maxTriggers = 5
 
-// 验证状态
-const triggerValidations = ref<{ [key: number]: { valid: boolean; message: string } }>({})
-const validationMessage = ref('')
-const isValid = ref(true)
+
+
+
+// 触发器类型选项
+const triggerTypeOptions = [
+  {
+    value: TriggerTypeEnum.DEVICE_STATE_UPDATE,
+    label: '设备状态变更'
+  },
+  {
+    value: TriggerTypeEnum.DEVICE_PROPERTY_POST,
+    label: '设备属性上报'
+  },
+  {
+    value: TriggerTypeEnum.DEVICE_EVENT_POST,
+    label: '设备事件上报'
+  },
+  {
+    value: TriggerTypeEnum.DEVICE_SERVICE_INVOKE,
+    label: '设备服务调用'
+  },
+  {
+    value: TriggerTypeEnum.TIMER,
+    label: '定时触发'
+  }
+]
 
 // 触发器类型映射
 const triggerTypeNames = {
@@ -180,12 +179,13 @@ const triggerTypeTags = {
 
 // 工具函数
 const isDeviceTrigger = (type: number) => {
-  return [
+  const deviceTriggerTypes = [
     TriggerTypeEnum.DEVICE_STATE_UPDATE,
     TriggerTypeEnum.DEVICE_PROPERTY_POST,
     TriggerTypeEnum.DEVICE_EVENT_POST,
     TriggerTypeEnum.DEVICE_SERVICE_INVOKE
-  ].includes(type)
+  ] as number[]
+  return deviceTriggerTypes.includes(type)
 }
 
 const getTriggerTypeName = (type: number) => {
@@ -198,31 +198,12 @@ const getTriggerTypeTag = (type: number) => {
 
 // 事件处理
 const addTrigger = () => {
-  if (triggers.value.length >= maxTriggers) {
-    return
-  }
-
   const newTrigger = createDefaultTriggerData()
   triggers.value.push(newTrigger)
 }
 
 const removeTrigger = (index: number) => {
   triggers.value.splice(index, 1)
-  delete triggerValidations.value[index]
-
-  // 重新索引验证结果
-  const newValidations: { [key: number]: { valid: boolean; message: string } } = {}
-  Object.keys(triggerValidations.value).forEach((key) => {
-    const numKey = parseInt(key)
-    if (numKey > index) {
-      newValidations[numKey - 1] = triggerValidations.value[numKey]
-    } else if (numKey < index) {
-      newValidations[numKey] = triggerValidations.value[numKey]
-    }
-  })
-  triggerValidations.value = newValidations
-
-  updateValidationResult()
 }
 
 const updateTriggerType = (index: number, type: number) => {
@@ -259,39 +240,6 @@ const onTriggerTypeChange = (trigger: TriggerFormData, type: number) => {
     }
   }
 }
-
-const handleTriggerValidate = (index: number, result: { valid: boolean; message: string }) => {
-  triggerValidations.value[index] = result
-  updateValidationResult()
-}
-
-const updateValidationResult = () => {
-  const validations = Object.values(triggerValidations.value)
-  const allValid = validations.every((v) => v.valid)
-  const hasValidations = validations.length > 0
-
-  if (!hasValidations) {
-    isValid.value = true
-    validationMessage.value = ''
-  } else if (allValid) {
-    isValid.value = true
-    validationMessage.value = '所有触发器配置验证通过'
-  } else {
-    isValid.value = false
-    const errorMessages = validations.filter((v) => !v.valid).map((v) => v.message)
-    validationMessage.value = `触发器配置错误: ${errorMessages.join('; ')}`
-  }
-
-  emit('validate', { valid: isValid.value, message: validationMessage.value })
-}
-
-// 监听触发器数量变化
-watch(
-  () => triggers.value.length,
-  () => {
-    updateValidationResult()
-  }
-)
 </script>
 
 

+ 0 - 142
src/views/iot/rule/scene/form/selectors/TriggerTypeSelector.vue

@@ -1,142 +0,0 @@
-<!-- 触发器类型选择组件 -->
-<template>
-  <div class="w-full">
-    <el-form-item label="触发类型" required>
-      <el-select
-        v-model="localValue"
-        placeholder="请选择触发类型"
-        @change="handleChange"
-        class="w-full"
-      >
-        <el-option
-          v-for="option in triggerTypeOptions"
-          :key="option.value"
-          :label="option.label"
-          :value="option.value"
-        >
-          <div class="flex items-center justify-between w-full py-4px">
-            <div class="flex items-center gap-12px flex-1">
-              <!-- TODO @puhui999:貌似没对齐? -->
-              <Icon :icon="option.icon" class="text-18px text-[var(--el-color-primary)] flex-shrink-0" />
-              <div class="flex-1">
-                <div class="text-14px font-500 text-[var(--el-text-color-primary)] mb-2px">{{ option.label }}</div>
-                <div class="text-12px text-[var(--el-text-color-secondary)] leading-relaxed">{{ option.description }}</div>
-              </div>
-            </div>
-            <!-- TODO @puhui999:这个要不去掉? -->
-            <el-tag :type="option.tag" size="small">
-              {{ option.category }}
-            </el-tag>
-          </div>
-        </el-option>
-      </el-select>
-    </el-form-item>
-
-    <!-- 类型说明 -->
-    <!-- TODO @puhui999:这个去掉。感觉没啥内容哈; -->
-    <div v-if="selectedOption" class="mt-16px p-16px bg-[var(--el-fill-color-light)] rounded-6px border border-[var(--el-border-color-lighter)]">
-      <div class="flex items-center gap-8px mb-12px">
-        <Icon :icon="selectedOption.icon" class="text-20px text-[var(--el-color-primary)]" />
-        <span class="text-16px font-600 text-[var(--el-text-color-primary)]">{{ selectedOption.label }}</span>
-      </div>
-      <div class="ml-28px">
-        <p class="text-14px text-[var(--el-text-color-regular)] m-0 mb-12px leading-relaxed">{{ selectedOption.description }}</p>
-        <div class="flex flex-col gap-6px">
-          <div v-for="feature in selectedOption.features" :key="feature" class="flex items-center gap-6px">
-            <Icon icon="ep:check" class="text-12px text-[var(--el-color-success)] flex-shrink-0" />
-            <span class="text-12px text-[var(--el-text-color-secondary)]">{{ feature }}</span>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-import { useVModel } from '@vueuse/core'
-import { IotRuleSceneTriggerTypeEnum } from '@/api/iot/rule/scene/scene.types'
-
-/** 触发器类型选择组件 */
-defineOptions({ name: 'TriggerTypeSelector' })
-
-interface Props {
-  modelValue: number
-}
-
-interface Emits {
-  (e: 'update:modelValue', value: number): void
-  (e: 'change', value: number): void
-}
-
-const props = defineProps<Props>()
-const emit = defineEmits<Emits>()
-
-const localValue = useVModel(props, 'modelValue', emit)
-
-// 触发器类型选项
-const triggerTypeOptions = [
-  {
-    value: IotRuleSceneTriggerTypeEnum.DEVICE_STATE_UPDATE,
-    label: '设备状态变更',
-    description: '当设备上线、离线状态发生变化时触发',
-    icon: 'ep:connection',
-    tag: 'warning',
-    category: '设备状态',
-    features: ['监控设备连接状态', '实时响应设备变化', '无需配置额外条件']
-  },
-  {
-    value: IotRuleSceneTriggerTypeEnum.DEVICE_PROPERTY_POST,
-    label: '设备属性上报',
-    description: '当设备属性值满足指定条件时触发',
-    icon: 'ep:data-line',
-    tag: 'primary',
-    category: '数据监控',
-    features: ['监控设备属性变化', '支持多种比较条件', '可配置阈值范围']
-  },
-  {
-    value: IotRuleSceneTriggerTypeEnum.DEVICE_EVENT_POST,
-    label: '设备事件上报',
-    description: '当设备上报特定事件时触发',
-    icon: 'ep:bell',
-    tag: 'success',
-    category: '事件监控',
-    features: ['监控设备事件', '支持事件参数过滤', '实时事件响应']
-  },
-  {
-    value: IotRuleSceneTriggerTypeEnum.DEVICE_SERVICE_INVOKE,
-    label: '设备服务调用',
-    description: '当设备服务被调用时触发',
-    icon: 'ep:service',
-    tag: 'info',
-    category: '服务监控',
-    features: ['监控服务调用', '支持参数条件', '服务执行跟踪']
-  },
-  {
-    value: IotRuleSceneTriggerTypeEnum.TIMER,
-    label: '定时触发',
-    description: '按照设定的时间计划定时触发',
-    icon: 'ep:timer',
-    tag: 'danger',
-    category: '定时任务',
-    features: ['支持CRON表达式', '灵活的时间配置', '可视化时间设置']
-  }
-]
-
-// 计算属性
-const selectedOption = computed(() => {
-  return triggerTypeOptions.find((option) => option.value === localValue.value)
-})
-
-// 事件处理
-const handleChange = (value: number) => {
-  emit('change', value)
-}
-</script>
-
-<style scoped>
-/** TODO @puhui999:unocss 哈 - 已完成转换 */
-:deep(.el-select-dropdown__item) {
-  height: auto;
-  padding: 8px 20px;
-}
-</style>