Преглед на файлове

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

puhui999 преди 10 месеца
родител
ревизия
f70d55f088

+ 0 - 7
src/views/iot/rule/scene/form/configs/DeviceStatusConditionConfig.vue

@@ -1,13 +1,6 @@
 <!-- 设备状态条件配置组件 -->
 <template>
   <div class="flex flex-col gap-16px">
-    <div
-      class="flex items-center gap-8px p-12px px-16px bg-blue-50 rounded-6px border border-blue-200"
-    >
-      <Icon icon="ep:connection" class="text-blue-500 text-18px" />
-      <span class="text-14px font-500 text-blue-700">设备状态条件配置</span>
-    </div>
-
     <!-- 产品设备选择 -->
     <el-row :gutter="16">
       <el-col :span="12">

+ 33 - 36
src/views/iot/rule/scene/form/configs/DeviceTriggerConfig.vue

@@ -1,23 +1,8 @@
 <!-- 设备触发配置组件 - 优化版本 -->
 <template>
   <div class="flex flex-col gap-16px">
-    <!-- 产品和设备选择 -->
-    <ProductDeviceSelector
-      v-model:product-id="trigger.productId"
-      v-model:device-id="trigger.deviceId"
-      @change="handleDeviceChange"
-    />
-
-    <!-- 主条件配置 -->
-    <div v-if="needsConditions" class="space-y-16px">
-      <div
-        class="flex items-center gap-8px p-12px px-16px bg-blue-50 rounded-6px border border-blue-200"
-      >
-        <Icon icon="ep:star-filled" class="text-blue-500 text-18px" />
-        <span class="text-14px font-600 text-blue-700">主条件配置</span>
-        <el-tag size="small" type="primary">必须满足</el-tag>
-      </div>
-
+    <!-- 主条件配置 - 默认直接展示 -->
+    <div class="space-y-16px">
       <MainConditionConfig
         v-model="trigger.mainCondition"
         :trigger-type="trigger.type"
@@ -26,13 +11,10 @@
     </div>
 
     <!-- 条件组配置 -->
-    <div v-if="needsConditions && trigger.mainCondition" class="space-y-16px">
+    <div v-if="trigger.mainCondition" class="space-y-16px">
       <div class="flex items-center justify-between">
-        <div
-          class="flex items-center gap-8px p-12px px-16px bg-green-50 rounded-6px border border-green-200"
-        >
-          <Icon icon="ep:connection" class="text-green-500 text-18px" />
-          <span class="text-14px font-600 text-green-700">附加条件组</span>
+        <div class="flex items-center gap-8px">
+          <span class="text-14px font-500 text-[var(--el-text-color-primary)]">附加条件组</span>
           <el-tag size="small" type="success">与主条件为且关系</el-tag>
           <el-tag size="small" type="info">
             {{ trigger.conditionGroup?.subGroups?.length || 0 }}个子条件组
@@ -77,7 +59,7 @@
 
 <script setup lang="ts">
 import { useVModel } from '@vueuse/core'
-import ProductDeviceSelector from '../selectors/ProductDeviceSelector.vue'
+
 import MainConditionConfig from './MainConditionConfig.vue'
 import ConditionGroupContainerConfig from './ConditionGroupContainerConfig.vue'
 import {
@@ -111,9 +93,29 @@ const validationMessage = ref('')
 const isValid = ref(true)
 
 // 计算属性
-const needsConditions = computed(() => {
-  return trigger.value.type !== TriggerTypeEnum.DEVICE_STATE_UPDATE
-})
+
+// 初始化主条件
+const initMainCondition = () => {
+  if (!trigger.value.mainCondition) {
+    trigger.value.mainCondition = {
+      type: trigger.value.type, // 使用触发事件类型作为条件类型
+      productId: undefined,
+      deviceId: undefined,
+      identifier: '',
+      operator: '=',
+      param: ''
+    }
+  }
+}
+
+// 监听触发器类型变化,自动初始化主条件
+watch(
+  () => trigger.value.type,
+  () => {
+    initMainCondition()
+  },
+  { immediate: true }
+)
 
 // 新的事件处理函数
 const handleMainConditionValidate = (result: { valid: boolean; message: string }) => {
@@ -130,13 +132,8 @@ const addConditionGroup = () => {
 }
 
 // 事件处理
-const handleDeviceChange = ({ productId, deviceId }: { productId?: number; deviceId?: number }) => {
-  trigger.value.productId = productId
-  trigger.value.deviceId = deviceId
-  updateValidationResult()
-}
 
-const handleConditionGroupValidate = (result: { valid: boolean; message: string }) => {
+const handleConditionGroupValidate = () => {
   updateValidationResult()
 }
 
@@ -145,10 +142,10 @@ const removeConditionGroup = () => {
 }
 
 const updateValidationResult = () => {
-  // 基础验证
-  if (!trigger.value.productId || !trigger.value.deviceId) {
+  // 主条件验证
+  if (!mainConditionValidation.value.valid) {
     isValid.value = false
-    validationMessage.value = '请选择产品和设备'
+    validationMessage.value = mainConditionValidation.value.message
     emit('validate', { valid: false, message: validationMessage.value })
     return
   }

+ 14 - 36
src/views/iot/rule/scene/form/configs/MainConditionConfig.vue

@@ -20,52 +20,30 @@
     </div>
 
     <!-- 主条件配置 -->
-    <div
-      v-else
-      class="border border-[var(--el-border-color-lighter)] rounded-8px bg-[var(--el-fill-color-blank)] shadow-sm"
-    >
-      <div
-        class="flex items-center justify-between p-16px bg-gradient-to-r from-blue-50 to-indigo-50 border-b border-[var(--el-border-color-lighter)] rounded-t-6px"
-      >
-        <div class="flex items-center gap-12px">
-          <div
-            class="flex items-center gap-8px text-16px font-600 text-[var(--el-text-color-primary)]"
-          >
-            <div
-              class="w-24px h-24px bg-blue-500 text-white rounded-full flex items-center justify-center text-12px font-bold"
-            >
-              主
-            </div>
-            <span>主条件</span>
-          </div>
-          <el-tag size="small" type="primary" class="font-500">必须满足</el-tag>
+    <div v-else class="space-y-16px">
+      <div class="flex items-center justify-between">
+        <div class="flex items-center gap-8px">
+          <span class="text-14px font-500 text-[var(--el-text-color-primary)]">主条件</span>
+          <el-tag size="small" type="primary">必须满足</el-tag>
         </div>
-        <el-button
-          type="danger"
-          size="small"
-          text
-          @click="removeMainCondition"
-          class="hover:bg-red-50"
-        >
+        <el-button type="danger" size="small" text @click="removeMainCondition">
           <Icon icon="ep:delete" />
           删除
         </el-button>
       </div>
 
-      <div class="p-16px">
-        <ConditionConfig
-          :model-value="modelValue"
-          @update:model-value="updateCondition"
-          :trigger-type="triggerType"
-          @validate="handleValidate"
-        />
-      </div>
+      <MainConditionInnerConfig
+        :model-value="modelValue"
+        @update:model-value="updateCondition"
+        :trigger-type="triggerType"
+        @validate="handleValidate"
+      />
     </div>
   </div>
 </template>
 
 <script setup lang="ts">
-import ConditionConfig from './ConditionConfig.vue'
+import MainConditionInnerConfig from './MainConditionInnerConfig.vue'
 import {
   ConditionFormData,
   IotRuleSceneTriggerConditionTypeEnum
@@ -84,7 +62,7 @@ interface Emits {
   (e: 'validate', result: { valid: boolean; message: string }): void
 }
 
-const props = defineProps<Props>()
+defineProps<Props>()
 const emit = defineEmits<Emits>()
 
 // 事件处理

+ 282 - 0
src/views/iot/rule/scene/form/configs/MainConditionInnerConfig.vue

@@ -0,0 +1,282 @@
+<!-- 主条件内部配置组件 - 不显示条件类型选择 -->
+<template>
+  <div class="space-y-16px">
+    <!-- 触发事件类型显示 -->
+    <div class="flex items-center gap-8px mb-16px">
+      <span class="text-14px text-[var(--el-text-color-regular)]">触发事件类型:</span>
+      <el-tag size="small" type="primary">{{ getTriggerTypeText(triggerType) }}</el-tag>
+    </div>
+
+    <!-- 设备属性条件配置 -->
+    <div v-if="isDevicePropertyTrigger" class="space-y-16px">
+      <!-- 产品设备选择 -->
+      <el-row :gutter="16">
+        <el-col :span="12">
+          <el-form-item label="产品" required>
+            <ProductSelector
+              :model-value="condition.productId"
+              @update:model-value="(value) => updateConditionField('productId', value)"
+              @change="handleProductChange"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="设备" required>
+            <DeviceSelector
+              :model-value="condition.deviceId"
+              @update:model-value="(value) => updateConditionField('deviceId', value)"
+              :product-id="condition.productId"
+              @change="handleDeviceChange"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <!-- 属性配置 -->
+      <el-row :gutter="16">
+        <!-- 属性/事件/服务选择 -->
+        <el-col :span="6">
+          <el-form-item label="监控项" required>
+            <PropertySelector
+              :model-value="condition.identifier"
+              @update:model-value="(value) => updateConditionField('identifier', value)"
+              :trigger-type="triggerType"
+              :product-id="condition.productId"
+              :device-id="condition.deviceId"
+              @change="handlePropertyChange"
+            />
+          </el-form-item>
+        </el-col>
+
+        <!-- 操作符选择 -->
+        <el-col :span="6">
+          <el-form-item label="操作符" required>
+            <OperatorSelector
+              :model-value="condition.operator"
+              @update:model-value="(value) => updateConditionField('operator', value)"
+              :property-type="propertyType"
+              @change="handleOperatorChange"
+            />
+          </el-form-item>
+        </el-col>
+
+        <!-- 值输入 -->
+        <el-col :span="12">
+          <el-form-item label="比较值" required>
+            <ValueInput
+              :model-value="condition.param"
+              @update:model-value="(value) => updateConditionField('param', value)"
+              :property-type="propertyType"
+              :operator="condition.operator"
+              :property-config="propertyConfig"
+              @validate="handleValueValidate"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <!-- 条件预览 -->
+      <div v-if="conditionPreview" class="mt-12px">
+        <div class="text-12px text-[var(--el-text-color-secondary)]">
+          预览:{{ conditionPreview }}
+        </div>
+      </div>
+    </div>
+
+    <!-- 设备状态条件配置 -->
+    <div v-else-if="isDeviceStatusTrigger" class="space-y-16px">
+      <DeviceStatusConditionConfig
+        :model-value="condition"
+        @update:model-value="updateCondition"
+        @validate="handleValidate"
+      />
+    </div>
+
+    <!-- 其他触发类型的提示 -->
+    <div v-else class="text-center py-20px">
+      <p class="text-14px text-[var(--el-text-color-secondary)] mb-4px">
+        当前触发事件类型:{{ getTriggerTypeText(triggerType) }}
+      </p>
+      <p class="text-12px text-[var(--el-text-color-placeholder)]">
+        此触发类型暂不需要配置额外条件
+      </p>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import ProductSelector from '../selectors/ProductSelector.vue'
+import DeviceSelector from '../selectors/DeviceSelector.vue'
+import PropertySelector from '../selectors/PropertySelector.vue'
+import OperatorSelector from '../selectors/OperatorSelector.vue'
+import ValueInput from '../inputs/ValueInput.vue'
+import DeviceStatusConditionConfig from './DeviceStatusConditionConfig.vue'
+import { ConditionFormData } from '@/api/iot/rule/scene/scene.types'
+import { IotRuleSceneTriggerTypeEnum } from '@/api/iot/rule/scene/scene.types'
+import { useVModel } from '@vueuse/core'
+
+/** 主条件内部配置组件 */
+defineOptions({ name: 'MainConditionInnerConfig' })
+
+interface Props {
+  modelValue: ConditionFormData
+  triggerType: number
+}
+
+interface Emits {
+  (e: 'update:modelValue', value: ConditionFormData): void
+  (e: 'validate', result: { valid: boolean; message: string }): void
+}
+
+const props = defineProps<Props>()
+const emit = defineEmits<Emits>()
+
+// 响应式数据
+const condition = useVModel(props, 'modelValue', emit)
+const isValid = ref(true)
+const validationMessage = ref('')
+const propertyType = ref('')
+const propertyConfig = ref<any>(null)
+
+// 计算属性
+const isDevicePropertyTrigger = computed(() => {
+  return (
+    props.triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_PROPERTY_POST ||
+    props.triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_EVENT_POST ||
+    props.triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_SERVICE_INVOKE
+  )
+})
+
+const isDeviceStatusTrigger = computed(() => {
+  return props.triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_STATE_UPDATE
+})
+
+const conditionPreview = computed(() => {
+  if (!condition.value.productId || !condition.value.deviceId || !condition.value.identifier) {
+    return ''
+  }
+  return `设备[${condition.value.deviceId}]的${condition.value.identifier} ${condition.value.operator} ${condition.value.param}`
+})
+
+// 获取触发类型文本
+const getTriggerTypeText = (type: number) => {
+  switch (type) {
+    case IotRuleSceneTriggerTypeEnum.DEVICE_PROPERTY_POST:
+      return '设备属性上报'
+    case IotRuleSceneTriggerTypeEnum.DEVICE_EVENT_POST:
+      return '设备事件上报'
+    case IotRuleSceneTriggerTypeEnum.DEVICE_SERVICE_INVOKE:
+      return '设备服务调用'
+    case IotRuleSceneTriggerTypeEnum.DEVICE_STATE_UPDATE:
+      return '设备状态变化'
+    default:
+      return '未知类型'
+  }
+}
+
+// 事件处理
+const updateConditionField = (field: keyof ConditionFormData, value: any) => {
+  condition.value[field] = value
+  updateValidationResult()
+}
+
+const updateCondition = (value: ConditionFormData) => {
+  emit('update:modelValue', value)
+  updateValidationResult()
+}
+
+const handleProductChange = () => {
+  // 产品变化时清空设备和属性
+  condition.value.deviceId = undefined
+  condition.value.identifier = ''
+  updateValidationResult()
+}
+
+const handleDeviceChange = () => {
+  // 设备变化时清空属性
+  condition.value.identifier = ''
+  updateValidationResult()
+}
+
+const handlePropertyChange = (propertyInfo: any) => {
+  if (propertyInfo) {
+    propertyType.value = propertyInfo.type
+    propertyConfig.value = propertyInfo.config
+  }
+  updateValidationResult()
+}
+
+const handleOperatorChange = () => {
+  updateValidationResult()
+}
+
+const handleValueValidate = (result: { valid: boolean; message: string }) => {
+  updateValidationResult()
+}
+
+const handleValidate = (result: { valid: boolean; message: string }) => {
+  isValid.value = result.valid
+  validationMessage.value = result.message
+  emit('validate', result)
+}
+
+// 验证逻辑
+const updateValidationResult = () => {
+  if (isDevicePropertyTrigger.value) {
+    // 设备属性触发验证
+    if (!condition.value.productId) {
+      isValid.value = false
+      validationMessage.value = '请选择产品'
+      emit('validate', { valid: false, message: validationMessage.value })
+      return
+    }
+
+    if (!condition.value.deviceId) {
+      isValid.value = false
+      validationMessage.value = '请选择设备'
+      emit('validate', { valid: false, message: validationMessage.value })
+      return
+    }
+
+    if (!condition.value.identifier) {
+      isValid.value = false
+      validationMessage.value = '请选择监控项'
+      emit('validate', { valid: false, message: validationMessage.value })
+      return
+    }
+
+    if (!condition.value.operator) {
+      isValid.value = false
+      validationMessage.value = '请选择操作符'
+      emit('validate', { valid: false, message: validationMessage.value })
+      return
+    }
+
+    if (!condition.value.param) {
+      isValid.value = false
+      validationMessage.value = '请输入比较值'
+      emit('validate', { valid: false, message: validationMessage.value })
+      return
+    }
+  }
+
+  isValid.value = true
+  validationMessage.value = '主条件配置验证通过'
+  emit('validate', { valid: true, message: validationMessage.value })
+}
+
+// 监听变化
+watch(
+  () => [
+    condition.value.productId,
+    condition.value.deviceId,
+    condition.value.identifier,
+    condition.value.operator,
+    condition.value.param
+  ],
+  () => {
+    updateValidationResult()
+  },
+  { immediate: true }
+)
+</script>

+ 1 - 0
src/views/iot/rule/scene/form/selectors/DeviceSelector.vue

@@ -80,6 +80,7 @@ const getDeviceList = async () => {
     console.error('获取设备列表失败:', error)
     deviceList.value = []
   } finally {
+    deviceList.value.push({ id: 0, deviceName: '全部设备' })
     deviceLoading.value = false
   }
 }