Просмотр исходного кода

【功能完善】IoT: 场景联动

puhui999 1 год назад
Родитель
Сommit
f46540759f

+ 2 - 1
src/api/iot/rule/scene/scene.types.ts

@@ -58,7 +58,8 @@ interface TenantBaseDO {
 
 // 触发条件参数
 interface TriggerConditionParameter {
-  identifier: string // 标识符(属性、事件、服务)
+  identifier0: string // 标识符(事件、服务)
+  identifier: string // 标识符(属性)
   operator: string // 操作符
   value: string // 比较值
 }

+ 1 - 1
src/views/iot/device/device/components/DeviceTableSelect.vue

@@ -117,7 +117,7 @@
           <template #default="scope">
             <el-radio
               v-model="selectedId"
-              :label="scope.row.id"
+              :value="scope.row.id"
               @change="() => handleRadioChange(scope.row)"
             >
               &nbsp;

+ 1 - 1
src/views/iot/product/product/components/ProductTableSelect.vue

@@ -57,7 +57,7 @@
           <template #default="scope">
             <el-radio
               v-model="selectedId"
-              :label="scope.row.id"
+              :value="scope.row.id"
               @change="() => handleRadioChange(scope.row)"
             >
               &nbsp;

+ 1 - 1
src/views/iot/rule/scene/RuleSceneForm.vue

@@ -19,7 +19,7 @@
               <el-radio
                 v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
                 :key="dict.value"
-                :label="dict.value"
+                :value="dict.value"
               >
                 {{ dict.label }}
               </el-radio>

+ 71 - 69
src/views/iot/rule/scene/components/action/ActionExecutor.vue

@@ -1,81 +1,83 @@
 <template>
-  <div class="m-10px">
-    <!-- 产品设备回显区域 -->
-    <div class="relative bg-[#eff3f7] h-50px flex items-center px-10px">
-      <div class="flex items-center mr-60px">
-        <span class="mr-10px">执行动作</span>
-        <el-select
-          v-model="actionConfig.type"
-          class="!w-240px"
-          clearable
-          placeholder="请选择执行类型"
+  <div>
+    <div class="m-10px">
+      <!-- 产品设备回显区域 -->
+      <div class="relative bg-[#eff3f7] h-50px flex items-center px-10px">
+        <div class="flex items-center mr-60px">
+          <span class="mr-10px">执行动作</span>
+          <el-select
+            v-model="actionConfig.type"
+            class="!w-240px"
+            clearable
+            placeholder="请选择执行类型"
+          >
+            <el-option
+              v-for="dict in getIntDictOptions(DICT_TYPE.IOT_RULE_SCENE_ACTION_TYPE_ENUM)"
+              :key="dict.value"
+              :label="dict.label"
+              :value="dict.value"
+            />
+          </el-select>
+        </div>
+        <div
+          v-if="actionConfig.type === IotRuleSceneActionTypeEnum.DEVICE_CONTROL"
+          class="flex items-center mr-60px"
         >
-          <el-option
-            v-for="dict in getIntDictOptions(DICT_TYPE.IOT_RULE_SCENE_ACTION_TYPE_ENUM)"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-        </el-select>
-      </div>
-      <div
-        v-if="actionConfig.type === IotRuleSceneActionTypeEnum.DEVICE_CONTROL"
-        class="flex items-center mr-60px"
-      >
-        <span class="mr-10px">产品</span>
-        <el-button type="primary" @click="handleSelectProduct" size="small" plain>
-          {{ product ? product.name : '选择产品' }}
-        </el-button>
+          <span class="mr-10px">产品</span>
+          <el-button type="primary" @click="handleSelectProduct" size="small" plain>
+            {{ product ? product.name : '选择产品' }}
+          </el-button>
+        </div>
+        <div
+          v-if="actionConfig.type === IotRuleSceneActionTypeEnum.DEVICE_CONTROL"
+          class="flex items-center mr-60px"
+        >
+          <span class="mr-10px">设备</span>
+          <el-button type="primary" @click="handleSelectDevice" size="small" plain>
+            {{ isEmpty(deviceList) ? '选择设备' : deviceList.map((d) => d.deviceName).join(',') }}
+          </el-button>
+        </div>
+        <!-- 删除执行器 -->
+        <div class="absolute top-auto right-16px bottom-auto">
+          <el-tooltip content="删除执行器" placement="top">
+            <slot></slot>
+          </el-tooltip>
+        </div>
       </div>
-      <div
+
+      <!-- 设备控制执行器 -->
+      <DeviceControlAction
         v-if="actionConfig.type === IotRuleSceneActionTypeEnum.DEVICE_CONTROL"
-        class="flex items-center mr-60px"
-      >
-        <span class="mr-10px">设备</span>
-        <el-button type="primary" @click="handleSelectDevice" size="small" plain>
-          {{ isEmpty(deviceList) ? '选择设备' : deviceList.map((d) => d.deviceName).join(',') }}
-        </el-button>
-      </div>
-      <!-- 删除执行器 -->
-      <div class="absolute top-auto right-16px bottom-auto">
-        <el-tooltip content="删除执行器" placement="top">
-          <slot></slot>
-        </el-tooltip>
-      </div>
+        :model-value="actionConfig.deviceControl"
+        :product-id="product?.id"
+        :product-key="product?.productKey"
+        @update:model-value="(val) => (actionConfig.deviceControl = val)"
+      />
+
+      <!-- 告警执行器 -->
+      <AlertAction
+        v-else-if="actionConfig.type === IotRuleSceneActionTypeEnum.ALERT"
+        :model-value="actionConfig.alert"
+        @update:model-value="(val) => (actionConfig.alert = val)"
+      />
+
+      <!-- 数据桥接执行器 -->
+      <DataBridgeAction
+        v-else-if="actionConfig.type === IotRuleSceneActionTypeEnum.DATA_BRIDGE"
+        :model-value="actionConfig.dataBridgeId"
+        @update:model-value="(val) => (actionConfig.dataBridgeId = val)"
+      />
     </div>
 
-    <!-- 设备控制执行器 -->
-    <DeviceControlAction
-      v-if="actionConfig.type === IotRuleSceneActionTypeEnum.DEVICE_CONTROL"
-      :model-value="actionConfig.deviceControl"
+    <!-- 产品、设备的选择 -->
+    <ProductTableSelect ref="productTableSelectRef" @success="handleProductSelect" />
+    <DeviceTableSelect
+      ref="deviceTableSelectRef"
+      multiple
       :product-id="product?.id"
-      :product-key="product?.productKey"
-      @update:model-value="(val) => (actionConfig.deviceControl = val)"
-    />
-
-    <!-- 告警执行器 -->
-    <AlertAction
-      v-else-if="actionConfig.type === IotRuleSceneActionTypeEnum.ALERT"
-      :model-value="actionConfig.alert"
-      @update:model-value="(val) => (actionConfig.alert = val)"
-    />
-
-    <!-- 数据桥接执行器 -->
-    <DataBridgeAction
-      v-else-if="actionConfig.type === IotRuleSceneActionTypeEnum.DATA_BRIDGE"
-      :model-value="actionConfig.dataBridgeId"
-      @update:model-value="(val) => (actionConfig.dataBridgeId = val)"
+      @success="handleDeviceSelect"
     />
   </div>
-
-  <!-- 产品、设备的选择 -->
-  <ProductTableSelect ref="productTableSelectRef" @success="handleProductSelect" />
-  <DeviceTableSelect
-    ref="deviceTableSelectRef"
-    multiple
-    :product-id="product?.id"
-    @success="handleDeviceSelect"
-  />
 </template>
 
 <script setup lang="ts">

+ 126 - 124
src/views/iot/rule/scene/components/listener/DeviceListener.vue

@@ -1,140 +1,143 @@
 <template>
-  <div class="m-10px">
-    <div class="relative bg-[#eff3f7] h-50px flex items-center px-10px">
-      <div class="flex items-center mr-60px">
-        <span class="mr-10px">触发条件</span>
-        <el-select
-          v-model="triggerConfig.type"
-          class="!w-240px"
-          clearable
-          placeholder="请选择触发条件"
-        >
-          <el-option
-            v-for="dict in getIntDictOptions(DICT_TYPE.IOT_RULE_SCENE_TRIGGER_TYPE_ENUM)"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-        </el-select>
-      </div>
-      <div
-        v-if="triggerConfig.type === IotRuleSceneTriggerTypeEnum.DEVICE"
-        class="flex items-center mr-60px"
-      >
-        <span class="mr-10px">产品</span>
-        <el-button type="primary" @click="productTableSelectRef?.open()" size="small" plain>
-          {{ product ? product.name : '选择产品' }}
-        </el-button>
-      </div>
-      <div
-        v-if="triggerConfig.type === IotRuleSceneTriggerTypeEnum.DEVICE"
-        class="flex items-center mr-60px"
-      >
-        <span class="mr-10px">设备</span>
-        <el-button type="primary" @click="openDeviceSelect" size="small" plain>
-          {{ isEmpty(deviceList) ? '选择设备' : triggerConfig.deviceNames.join(',') }}
-        </el-button>
-      </div>
-      <!-- 删除触发器 -->
-      <div class="absolute top-auto right-16px bottom-auto">
-        <el-tooltip content="删除触发器" placement="top">
-          <slot></slot>
-        </el-tooltip>
-      </div>
-    </div>
-    <!-- 设备触发器条件 -->
-    <template v-if="triggerConfig.type === IotRuleSceneTriggerTypeEnum.DEVICE">
-      <div
-        class="bg-[#dbe5f6] flex p-10px"
-        v-for="(condition, index) in triggerConfig.conditions"
-        :key="index"
-      >
-        <div class="flex flex-col items-center justify-center mr-10px h-a">
+  <div>
+    <div class="m-10px">
+      <div class="relative bg-[#eff3f7] h-50px flex items-center px-10px">
+        <div class="flex items-center mr-60px">
+          <span class="mr-10px">触发条件</span>
           <el-select
-            v-model="condition.type"
-            @change="condition.parameters = []"
-            class="!w-160px"
+            v-model="triggerConfig.type"
+            class="!w-240px"
             clearable
-            placeholder=""
+            placeholder="请选择触发条件"
           >
-            <el-option label="属性" :value="IotDeviceMessageTypeEnum.PROPERTY" />
-            <el-option label="服务" :value="IotDeviceMessageTypeEnum.SERVICE" />
-            <el-option label="事件" :value="IotDeviceMessageTypeEnum.EVENT" />
+            <el-option
+              v-for="dict in getIntDictOptions(DICT_TYPE.IOT_RULE_SCENE_TRIGGER_TYPE_ENUM)"
+              :key="dict.value"
+              :label="dict.label"
+              :value="dict.value"
+            />
           </el-select>
         </div>
-        <div class="">
-          <DeviceListenerCondition
-            v-for="(parameter, index2) in condition.parameters"
-            :key="index2"
-            :model-value="parameter"
-            :thingModels="thingModels(condition)"
-            @update:model-value="(val) => (condition.parameters[index2] = val)"
-            class="mb-10px last:mb-0"
-          >
-            <el-tooltip content="删除参数" placement="top">
+        <div
+          v-if="triggerConfig.type === IotRuleSceneTriggerTypeEnum.DEVICE"
+          class="flex items-center mr-60px"
+        >
+          <span class="mr-10px">产品</span>
+          <el-button type="primary" @click="productTableSelectRef?.open()" size="small" plain>
+            {{ product ? product.name : '选择产品' }}
+          </el-button>
+        </div>
+        <div
+          v-if="triggerConfig.type === IotRuleSceneTriggerTypeEnum.DEVICE"
+          class="flex items-center mr-60px"
+        >
+          <span class="mr-10px">设备</span>
+          <el-button type="primary" @click="openDeviceSelect" size="small" plain>
+            {{ isEmpty(deviceList) ? '选择设备' : triggerConfig.deviceNames.join(',') }}
+          </el-button>
+        </div>
+        <!-- 删除触发器 -->
+        <div class="absolute top-auto right-16px bottom-auto">
+          <el-tooltip content="删除触发器" placement="top">
+            <slot></slot>
+          </el-tooltip>
+        </div>
+      </div>
+      <!-- 设备触发器条件 -->
+      <template v-if="triggerConfig.type === IotRuleSceneTriggerTypeEnum.DEVICE">
+        <div
+          class="bg-[#dbe5f6] flex p-10px"
+          v-for="(condition, index) in triggerConfig.conditions"
+          :key="index"
+        >
+          <div class="flex flex-col items-center justify-center mr-10px h-a">
+            <el-select
+              v-model="condition.type"
+              @change="condition.parameters = []"
+              class="!w-160px"
+              clearable
+              placeholder=""
+            >
+              <el-option label="属性" :value="IotDeviceMessageTypeEnum.PROPERTY" />
+              <el-option label="服务" :value="IotDeviceMessageTypeEnum.SERVICE" />
+              <el-option label="事件" :value="IotDeviceMessageTypeEnum.EVENT" />
+            </el-select>
+          </div>
+          <div class="w-70%">
+            <DeviceListenerCondition
+              v-for="(parameter, index2) in condition.parameters"
+              :key="index2"
+              :model-value="parameter"
+              :condition-type="condition.type"
+              :thingModels="thingModels(condition)"
+              @update:model-value="(val) => (condition.parameters[index2] = val)"
+              class="mb-10px last:mb-0"
+            >
+              <el-tooltip content="删除参数" placement="top">
+                <el-button
+                  type="danger"
+                  circle
+                  size="small"
+                  @click="removeConditionParameter(condition.parameters, index2)"
+                >
+                  <Icon icon="ep:delete" />
+                </el-button>
+              </el-tooltip>
+            </DeviceListenerCondition>
+          </div>
+          <!-- 添加参数 -->
+          <div class="flex flex-1 flex-col items-center justify-center w-60px h-a">
+            <el-tooltip content="添加参数" placement="top">
               <el-button
-                type="danger"
+                type="primary"
                 circle
                 size="small"
-                @click="removeConditionParameter(condition.parameters, index2)"
+                @click="addConditionParameter(condition.parameters)"
               >
+                <Icon icon="ep:plus" />
+              </el-button>
+            </el-tooltip>
+          </div>
+          <!-- 删除条件 -->
+          <div
+            class="device-listener-condition flex flex-1 flex-col items-center justify-center w-a h-a"
+          >
+            <el-tooltip content="删除条件" placement="top">
+              <el-button type="danger" size="small" @click="removeCondition(index)">
                 <Icon icon="ep:delete" />
               </el-button>
             </el-tooltip>
-          </DeviceListenerCondition>
-        </div>
-        <!-- 添加参数 -->
-        <div class="flex flex-1 flex-col items-center justify-center w-60px h-a">
-          <el-tooltip content="添加参数" placement="top">
-            <el-button
-              type="primary"
-              circle
-              size="small"
-              @click="addConditionParameter(condition.parameters)"
-            >
-              <Icon icon="ep:plus" />
-            </el-button>
-          </el-tooltip>
-        </div>
-        <!-- 删除条件 -->
-        <div
-          class="device-listener-condition flex flex-1 flex-col items-center justify-center w-a h-a"
-        >
-          <el-tooltip content="删除条件" placement="top">
-            <el-button type="danger" size="small" @click="removeCondition(index)">
-              <Icon icon="ep:delete" />
-            </el-button>
-          </el-tooltip>
+          </div>
         </div>
+      </template>
+      <!-- 定时触发 -->
+      <div
+        v-if="triggerConfig.type === IotRuleSceneTriggerTypeEnum.TIMER"
+        class="bg-[#dbe5f6] flex items-center justify-between p-10px"
+      >
+        <span class="w-120px">CRON 表达式</span>
+        <crontab v-model="triggerConfig.cronExpression" />
       </div>
-    </template>
-    <!-- 定时触发 -->
-    <div
-      v-if="triggerConfig.type === IotRuleSceneTriggerTypeEnum.TIMER"
-      class="bg-[#dbe5f6] flex items-center justify-between p-10px"
-    >
-      <span class="w-120px">CRON 表达式</span>
-      <crontab v-model="triggerConfig.cronExpression" />
+      <!-- 设备触发才可以设置多个触发条件 -->
+      <el-text
+        v-if="triggerConfig.type === IotRuleSceneTriggerTypeEnum.DEVICE"
+        class="ml-10px!"
+        type="primary"
+        @click="addCondition"
+      >
+        添加触发条件
+      </el-text>
     </div>
-    <!-- 设备触发才可以设置多个触发条件 -->
-    <el-text
-      v-if="triggerConfig.type === IotRuleSceneTriggerTypeEnum.DEVICE"
-      class="ml-10px!"
-      type="primary"
-      @click="addCondition"
-    >
-      添加触发条件
-    </el-text>
-  </div>
 
-  <!-- 产品、设备的选择 -->
-  <ProductTableSelect ref="productTableSelectRef" @success="handleProductSelect" />
-  <DeviceTableSelect
-    ref="deviceTableSelectRef"
-    multiple
-    :product-id="product?.id"
-    @success="handleDeviceSelect"
-  />
+    <!-- 产品、设备的选择 -->
+    <ProductTableSelect ref="productTableSelectRef" @success="handleProductSelect" />
+    <DeviceTableSelect
+      ref="deviceTableSelectRef"
+      multiple
+      :product-id="product?.id"
+      @success="handleDeviceSelect"
+    />
+  </div>
 </template>
 
 <script setup lang="ts">
@@ -272,12 +275,11 @@ const thingModels = computed(() => (condition: TriggerCondition) => {
   }
   switch (condition.type) {
     case IotDeviceMessageTypeEnum.PROPERTY:
-      return thingModelTSL.value.properties
-    // TODO puhui999: 服务和事件后续考虑
+      return thingModelTSL.value?.properties || []
     case IotDeviceMessageTypeEnum.SERVICE:
-      return thingModelTSL.value.services
+      return thingModelTSL.value?.services || []
     case IotDeviceMessageTypeEnum.EVENT:
-      return thingModelTSL.value.events
+      return thingModelTSL.value?.events || []
   }
   return []
 })

+ 47 - 40
src/views/iot/rule/scene/components/listener/DeviceListenerCondition.vue

@@ -1,13 +1,30 @@
 <template>
-  <div class="device-listener-condition">
+  <div class="flex items-center w-1/1">
+    <!-- 选择服务 -->
+    <el-select
+      v-if="
+        [IotDeviceMessageTypeEnum.SERVICE, IotDeviceMessageTypeEnum.EVENT].includes(conditionType)
+      "
+      v-model="conditionParameter.identifier0"
+      class="!w-150px mr-10px"
+      clearable
+      placeholder="请选择服务"
+    >
+      <el-option
+        v-for="thingModel in thingModels"
+        :key="thingModel.identifier"
+        :label="thingModel.name"
+        :value="thingModel.identifier"
+      />
+    </el-select>
     <el-select
       v-model="conditionParameter.identifier"
-      class="!w-240px mr-10px"
+      class="!w-150px mr-10px"
       clearable
       placeholder="请选择物模型"
     >
       <el-option
-        v-for="thingModel in thingModels"
+        v-for="thingModel in getThingModels"
         :key="thingModel.identifier"
         :label="thingModel.name"
         :value="thingModel.identifier"
@@ -15,21 +32,18 @@
     </el-select>
     <ConditionSelector
       v-model="conditionParameter.operator"
-      :data-type="getDataType"
-      class="!w-180px mr-10px"
+      :data-type="model?.dataType"
+      class="!w-150px mr-10px"
     />
-    <!-- TODO puhui999: 输入值范围校验? -->
-    <el-input
+    <ThingModelParamInput
       v-if="
         conditionParameter.operator !==
         IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_NULL.value
       "
+      class="!w-200px mr-10px"
       v-model="conditionParameter.value"
-      class="!w-240px mr-10px"
-      placeholder="请输入值"
-    >
-      <template v-if="getUnitName" #append> {{ getUnitName }} </template>
-    </el-input>
+      :thing-model="model"
+    />
     <!-- 按钮插槽 -->
     <slot></slot>
   </div>
@@ -38,43 +52,36 @@
 <script setup lang="ts">
 import ConditionSelector from './ConditionSelector.vue'
 import {
+  IotDeviceMessageTypeEnum,
   IotRuleSceneTriggerConditionParameterOperatorEnum,
   TriggerConditionParameter
 } from '@/api/iot/rule/scene/scene.types'
 import { useVModel } from '@vueuse/core'
+import ThingModelParamInput from '@/views/iot/rule/scene/components/ThingModelParamInput.vue'
 
+/** 设备触发条件 */
 defineOptions({ name: 'DeviceListenerCondition' })
-const props = defineProps<{ modelValue: any; thingModels: any }>()
+const props = defineProps<{ modelValue: any; conditionType: any; thingModels: any }>()
 const emits = defineEmits(['update:modelValue'])
 const conditionParameter = useVModel(props, 'modelValue', emits) as Ref<TriggerConditionParameter>
 
-/** 获得物模型属性类型 */
-const getDataType = computed(() => {
-  const model = props.thingModels?.find(
-    (item: any) => item.identifier === conditionParameter.value.identifier
-  )
-  // 属性
-  if (model?.dataSpecs) {
-    return model.dataSpecs.dataType
+/** 属性就是 thingModels,服务和事件取对应的 outputParams */
+const getThingModels = computed(() => {
+  switch (props.conditionType) {
+    case IotDeviceMessageTypeEnum.PROPERTY:
+      return props.thingModels || []
+    case IotDeviceMessageTypeEnum.SERVICE:
+    case IotDeviceMessageTypeEnum.EVENT:
+      return (
+        props.thingModels.find(
+          (item: any) => item.identifier === conditionParameter.value.identifier0
+        )?.outputParams || []
+      )
   }
-  return ''
 })
-/** 获得属性单位 */
-const getUnitName = computed(() => {
-  const model = props.thingModels?.find(
-    (item: any) => item.identifier === conditionParameter.value.identifier
-  )
-  // 属性
-  if (model?.dataSpecs) {
-    return model.dataSpecs.unitName
-  }
-  // TODO puhui999: 先不考虑服务和事件的情况
-  // 服务和事件
-  // if (model?.outputParams) {
-  //   return model.dataSpecs.unitName
-  // }
-  return ''
-})
-</script>
 
-<style scoped lang="scss"></style>
+/** 获得物模型属性、类型 */
+const model = computed(() =>
+  getThingModels.value.find((item: any) => item.identifier === conditionParameter.value.identifier)
+)
+</script>