Procházet zdrojové kódy

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

puhui999 před 10 měsíci
rodič
revize
a5d458b96d

+ 4 - 6
src/views/iot/rule/scene/form/configs/ConditionConfig.vue

@@ -190,22 +190,20 @@ const handleValidate = (result: { valid: boolean; message: string }) => {
   emit('validate', result)
 }
 
-const handleProductChange = (productId: number) => {
+const handleProductChange = (_: number) => {
   // 产品变化时清空设备和属性
-  condition.value.productId = productId
+  condition.value.deviceId = undefined
   condition.value.identifier = ''
   updateValidationResult()
 }
 
-const handleDeviceChange = (deviceId: number) => {
+const handleDeviceChange = (_: number) => {
   // 设备变化时清空属性
-  condition.value.deviceId = deviceId
+  condition.value.identifier = ''
   updateValidationResult()
 }
 
 const handlePropertyChange = (propertyInfo: { type: string; config: any }) => {
-  debugger
-  console.log(propertyInfo)
   propertyType.value = propertyInfo.type
   propertyConfig.value = propertyInfo.config
 

+ 0 - 253
src/views/iot/rule/scene/form/configs/ConditionGroupConfig.vue

@@ -1,253 +0,0 @@
-<!-- 条件组配置组件 -->
-<template>
-  <div class="p-16px">
-    <!-- 条件组说明 -->
-    <div
-      v-if="group.conditions && group.conditions.length > 1"
-      class="mb-12px flex items-center justify-center"
-    >
-      <div
-        class="flex items-center gap-6px px-10px py-4px bg-green-50 border border-green-200 rounded-full text-11px text-green-600"
-      >
-        <Icon icon="ep:info-filled" />
-        <span>组内所有条件必须同时满足(且关系)</span>
-      </div>
-    </div>
-
-    <div class="space-y-12px">
-      <!-- 条件列表 -->
-      <div v-if="group.conditions && group.conditions.length > 0" class="space-y-12px">
-        <div
-          v-for="(condition, index) in group.conditions"
-          :key="`condition-${index}`"
-          class="p-12px border border-[var(--el-border-color-lighter)] rounded-6px bg-[var(--el-fill-color-light)] shadow-sm hover:shadow-md transition-shadow"
-        >
-          <div class="flex items-center justify-between mb-12px">
-            <div class="flex items-center gap-8px">
-              <div class="flex items-center gap-6px">
-                <div
-                  class="w-18px h-18px bg-green-500 text-white rounded-full flex items-center justify-center text-10px font-bold"
-                >
-                  {{ index + 1 }}
-                </div>
-                <span class="text-14px font-500 text-[var(--el-text-color-primary)]">条件</span>
-              </div>
-              <el-tag size="small" type="primary">
-                {{ getConditionTypeName(condition.type) }}
-              </el-tag>
-            </div>
-            <el-button
-              type="danger"
-              size="small"
-              text
-              @click="removeCondition(index)"
-              v-if="group.conditions!.length > 1"
-            >
-              <Icon icon="ep:delete" />
-              删除
-            </el-button>
-          </div>
-
-          <div class="p-12px bg-[var(--el-fill-color-blank)] rounded-4px">
-            <ConditionConfig
-              :model-value="condition"
-              @update:model-value="(value) => updateCondition(index, value)"
-              :trigger-type="triggerType"
-              :product-id="productId"
-              :device-id="deviceId"
-              @validate="(result) => handleConditionValidate(index, result)"
-            />
-          </div>
-
-          <!-- 条件间的"且"连接符 -->
-          <div
-            v-if="index < group.conditions!.length - 1"
-            class="flex items-center justify-center py-8px"
-          >
-            <div class="flex items-center gap-6px">
-              <!-- 连接线 -->
-              <div class="w-24px h-1px bg-green-300"></div>
-              <!-- 且标签 -->
-              <div class="px-12px py-4px bg-green-100 border-2 border-green-300 rounded-full">
-                <span class="text-12px font-600 text-green-600">且</span>
-              </div>
-              <!-- 连接线 -->
-              <div class="w-24px h-1px bg-green-300"></div>
-            </div>
-          </div>
-        </div>
-      </div>
-
-      <!-- 空状态 -->
-      <div v-else class="py-40px text-center">
-        <el-empty description="暂无条件配置" :image-size="80">
-          <template #description>
-            <div class="space-y-8px">
-              <p class="text-[var(--el-text-color-secondary)]">暂无条件配置</p>
-              <p class="text-12px text-[var(--el-text-color-placeholder)]">
-                条件组需要至少包含一个条件才能生效
-              </p>
-            </div>
-          </template>
-        </el-empty>
-      </div>
-
-      <!-- 添加条件按钮 -->
-      <div
-        v-if="
-          group.conditions && group.conditions.length > 0 && group.conditions.length < maxConditions
-        "
-        class="text-center py-16px"
-      >
-        <el-button type="primary" plain @click="addCondition">
-          <Icon icon="ep:plus" />
-          继续添加条件
-        </el-button>
-        <span class="block mt-8px text-12px text-[var(--el-text-color-secondary)]">
-          最多可添加 {{ maxConditions }} 个条件
-        </span>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-import { useVModel } from '@vueuse/core'
-import ConditionConfig from './ConditionConfig.vue'
-import { ConditionFormData, ConditionGroupFormData } from '@/api/iot/rule/scene/scene.types'
-import { IotRuleSceneTriggerTypeEnum } from '@/views/iot/utils/constants'
-
-/** 条件组配置组件 */
-defineOptions({ name: 'ConditionGroupConfig' })
-
-interface Props {
-  modelValue: ConditionGroupFormData
-  triggerType: number
-  productId?: number
-  deviceId?: number
-  maxConditions?: number
-}
-
-interface Emits {
-  (e: 'update:modelValue', value: ConditionGroupFormData): void
-
-  (e: 'validate', result: { valid: boolean; message: string }): void
-}
-
-const props = defineProps<Props>()
-const emit = defineEmits<Emits>()
-
-const group = useVModel(props, 'modelValue', emit)
-
-// 配置常量
-const maxConditions = computed(() => props.maxConditions || 3)
-
-// 验证状态
-const conditionValidations = ref<{ [key: number]: { valid: boolean; message: string } }>({})
-const validationMessage = ref('')
-const isValid = ref(true)
-
-// 条件类型映射
-const conditionTypeNames = {
-  [IotRuleSceneTriggerTypeEnum.DEVICE_PROPERTY_POST]: '属性条件',
-  [IotRuleSceneTriggerTypeEnum.DEVICE_EVENT_POST]: '事件条件',
-  [IotRuleSceneTriggerTypeEnum.DEVICE_SERVICE_INVOKE]: '服务条件'
-}
-
-// 工具函数
-const getConditionTypeName = (type: number) => {
-  return conditionTypeNames[type] || '未知条件'
-}
-
-// 事件处理
-const updateCondition = (index: number, condition: ConditionFormData) => {
-  if (group.value.conditions) {
-    group.value.conditions[index] = condition
-  }
-}
-
-const addCondition = () => {
-  if (!group.value.conditions) {
-    group.value.conditions = []
-  }
-
-  if (group.value.conditions.length >= maxConditions.value) {
-    return
-  }
-
-  const newCondition: ConditionFormData = {
-    type: 2, // 默认为设备属性条件
-    productId: props.productId || 0,
-    deviceId: props.deviceId || 0,
-    identifier: '',
-    operator: '=',
-    param: ''
-  }
-
-  group.value.conditions.push(newCondition)
-}
-
-const removeCondition = (index: number) => {
-  if (group.value.conditions) {
-    group.value.conditions.splice(index, 1)
-    delete conditionValidations.value[index]
-
-    // 重新索引验证结果
-    const newValidations: { [key: number]: { valid: boolean; message: string } } = {}
-    Object.keys(conditionValidations.value).forEach((key) => {
-      const numKey = parseInt(key)
-      if (numKey > index) {
-        newValidations[numKey - 1] = conditionValidations.value[numKey]
-      } else if (numKey < index) {
-        newValidations[numKey] = conditionValidations.value[numKey]
-      }
-    })
-    conditionValidations.value = newValidations
-
-    updateValidationResult()
-  }
-}
-
-const handleConditionValidate = (index: number, result: { valid: boolean; message: string }) => {
-  conditionValidations.value[index] = result
-  updateValidationResult()
-}
-
-const updateValidationResult = () => {
-  if (!group.value.conditions || group.value.conditions.length === 0) {
-    isValid.value = false
-    validationMessage.value = '请至少添加一个条件'
-    emit('validate', { valid: false, message: validationMessage.value })
-    return
-  }
-
-  const validations = Object.values(conditionValidations.value)
-  const allValid = validations.every((v) => v.valid)
-
-  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(
-  () => group.value.conditions?.length,
-  () => {
-    updateValidationResult()
-  }
-)
-
-// 初始化
-onMounted(() => {
-  if (!group.value.conditions || group.value.conditions.length === 0) {
-    addCondition()
-  }
-})
-</script>

+ 1 - 0
src/views/iot/rule/scene/form/configs/ConditionGroupContainerConfig.vue

@@ -137,6 +137,7 @@ const props = defineProps<{
   modelValue: ConditionGroupContainerFormData
   triggerType: number
 }>()
+
 const emit = defineEmits<{
   (e: 'update:modelValue', value: ConditionGroupContainerFormData): void
   (e: 'validate', result: { valid: boolean; message: string }): void

+ 4 - 8
src/views/iot/rule/scene/form/configs/CurrentTimeConditionConfig.vue

@@ -102,18 +102,14 @@ import {
 /** 当前时间条件配置组件 */
 defineOptions({ name: 'CurrentTimeConditionConfig' })
 
-interface Props {
+const props = defineProps<{
   modelValue: ConditionFormData
-}
+}>()
 
-interface Emits {
+const emit = defineEmits<{
   (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)
 

+ 10 - 9
src/views/iot/rule/scene/form/configs/DeviceControlConfig.vue

@@ -27,9 +27,13 @@
           <template #default>
             <div class="space-y-8px">
               <p class="m-0 text-14px text-[var(--el-text-color-primary)]">属性设置示例:</p>
-              <pre class="m-0 p-8px bg-[var(--el-fill-color-light)] rounded-4px text-12px text-[var(--el-text-color-regular)] overflow-x-auto"><code>{ "temperature": 25, "power": true }</code></pre>
+              <pre
+                class="m-0 p-8px bg-[var(--el-fill-color-light)] rounded-4px text-12px text-[var(--el-text-color-regular)] overflow-x-auto"
+              ><code>{ "temperature": 25, "power": true }</code></pre>
               <p class="m-0 text-14px text-[var(--el-text-color-primary)]">服务调用示例:</p>
-              <pre class="m-0 p-8px bg-[var(--el-fill-color-light)] rounded-4px text-12px text-[var(--el-text-color-regular)] overflow-x-auto"><code>{ "method": "restart", "params": { "delay": 5 } }</code></pre>
+              <pre
+                class="m-0 p-8px bg-[var(--el-fill-color-light)] rounded-4px text-12px text-[var(--el-text-color-regular)] overflow-x-auto"
+              ><code>{ "method": "restart", "params": { "delay": 5 } }</code></pre>
             </div>
           </template>
         </el-alert>
@@ -56,17 +60,14 @@ import { ActionFormData } from '@/api/iot/rule/scene/scene.types'
 /** 设备控制配置组件 */
 defineOptions({ name: 'DeviceControlConfig' })
 
-interface Props {
+const props = defineProps<{
   modelValue: ActionFormData
-}
+}>()
 
-interface Emits {
+const emit = defineEmits<{
   (e: 'update:modelValue', value: ActionFormData): void
   (e: 'validate', result: { valid: boolean; message: string }): void
-}
-
-const props = defineProps<Props>()
-const emit = defineEmits<Emits>()
+}>()
 
 const action = useVModel(props, 'modelValue', emit)
 

+ 23 - 70
src/views/iot/rule/scene/form/configs/DeviceStatusConditionConfig.vue

@@ -26,85 +26,57 @@
 
     <!-- 状态和操作符选择 -->
     <el-row :gutter="16">
-      <!-- 状态选择 -->
+      <!-- 操作符选择 -->
       <el-col :span="12">
-        <el-form-item label="设备状态" required>
+        <el-form-item label="操作符" required>
           <el-select
-            :model-value="condition.param"
-            @update:model-value="(value) => updateConditionField('param', value)"
-            placeholder="请选择设备状态"
+            :model-value="condition.operator"
+            @update:model-value="(value) => updateConditionField('operator', value)"
+            placeholder="请选择操作符"
             class="w-full"
           >
             <el-option
-              v-for="option in deviceStatusOptions"
+              v-for="option in statusOperatorOptions"
               :key="option.value"
               :label="option.label"
               :value="option.value"
             >
-              <div class="flex items-center gap-8px">
-                <Icon :icon="option.icon" :class="option.iconClass" />
+              <div class="flex items-center justify-between w-full">
                 <span>{{ option.label }}</span>
-                <el-tag :type="option.tag" size="small">{{ option.description }}</el-tag>
+                <span class="text-12px text-[var(--el-text-color-secondary)]">{{
+                  option.description
+                }}</span>
               </div>
             </el-option>
           </el-select>
         </el-form-item>
       </el-col>
 
-      <!-- 操作符选择 -->
+      <!-- 状态选择 -->
       <el-col :span="12">
-        <el-form-item label="操作符" required>
+        <el-form-item label="设备状态" required>
           <el-select
-            :model-value="condition.operator"
-            @update:model-value="(value) => updateConditionField('operator', value)"
-            placeholder="请选择操作符"
+            :model-value="condition.param"
+            @update:model-value="(value) => updateConditionField('param', value)"
+            placeholder="请选择设备状态"
             class="w-full"
           >
             <el-option
-              v-for="option in statusOperatorOptions"
+              v-for="option in deviceStatusOptions"
               :key="option.value"
               :label="option.label"
               :value="option.value"
             >
-              <div class="flex items-center justify-between w-full">
+              <div class="flex items-center gap-8px">
+                <Icon :icon="option.icon" :class="option.iconClass" />
                 <span>{{ option.label }}</span>
-                <span class="text-12px text-[var(--el-text-color-secondary)]">{{
-                  option.description
-                }}</span>
+                <el-tag :type="option.tag" size="small">{{ option.description }}</el-tag>
               </div>
             </el-option>
           </el-select>
         </el-form-item>
       </el-col>
     </el-row>
-
-    <!-- 条件预览 -->
-    <!-- TODO puhui999:可以去掉。。。因为表单选择了,可以看懂的呀。 -->
-    <div
-      v-if="conditionPreview"
-      class="p-12px bg-[var(--el-fill-color-light)] rounded-6px border border-[var(--el-border-color-lighter)]"
-    >
-      <div class="flex items-center gap-8px mb-8px">
-        <Icon icon="ep:view" class="text-[var(--el-color-info)] text-16px" />
-        <span class="text-14px font-500 text-[var(--el-text-color-primary)]">条件预览</span>
-      </div>
-      <div class="pl-24px">
-        <code
-          class="text-12px text-[var(--el-color-primary)] bg-[var(--el-fill-color-blank)] p-8px rounded-4px font-mono"
-          >{{ conditionPreview }}</code
-        >
-      </div>
-    </div>
-
-    <!-- 验证结果 -->
-    <div v-if="validationMessage" class="mt-8px">
-      <el-alert
-        :title="validationMessage"
-        :type="isValid ? 'success' : 'error'"
-        :closable="false"
-        show-icon
-      />
-    </div>
   </div>
 </template>
 
@@ -117,17 +89,14 @@ import { ConditionFormData } from '@/api/iot/rule/scene/scene.types'
 /** 设备状态条件配置组件 */
 defineOptions({ name: 'DeviceStatusConditionConfig' })
 
-interface Props {
+const props = defineProps<{
   modelValue: ConditionFormData
-}
+}>()
 
-interface Emits {
+const emit = defineEmits<{
   (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)
 
@@ -169,22 +138,6 @@ const statusOperatorOptions = [
 const validationMessage = ref('')
 const isValid = ref(true)
 
-// 计算属性
-const conditionPreview = computed(() => {
-  if (!condition.value.param || !condition.value.operator) {
-    return ''
-  }
-
-  const statusLabel =
-    deviceStatusOptions.find((opt) => opt.value === condition.value.param)?.label ||
-    condition.value.param
-  const operatorLabel =
-    statusOperatorOptions.find((opt) => opt.value === condition.value.operator)?.label ||
-    condition.value.operator
-
-  return `设备状态 ${operatorLabel} ${statusLabel}`
-})
-
 // 事件处理
 const updateConditionField = (field: keyof ConditionFormData, value: any) => {
   condition.value[field] = value

+ 2 - 4
src/views/iot/rule/scene/form/configs/DeviceTriggerConfig.vue

@@ -68,14 +68,13 @@ import { IotRuleSceneTriggerTypeEnum as TriggerTypeEnum } from '@/views/iot/util
 /** 设备触发配置组件 */
 defineOptions({ name: 'DeviceTriggerConfig' })
 
-// Props 和 Emits 定义
 const props = defineProps<{
   modelValue: TriggerFormData
 }>()
 
 const emit = defineEmits<{
-  'update:modelValue': [value: TriggerFormData]
-  validate: [result: { valid: boolean; message: string }]
+  (e: 'update:modelValue', value: TriggerFormData): void
+  (e: 'validate', value: { valid: boolean; message: string }): void
 }>()
 
 const trigger = useVModel(props, 'modelValue', emit)
@@ -126,7 +125,6 @@ const addConditionGroup = () => {
 }
 
 // 事件处理
-
 const handleConditionGroupValidate = () => {
   updateValidationResult()
 }

+ 1 - 0
src/views/iot/rule/scene/form/configs/MainConditionConfig.vue

@@ -51,6 +51,7 @@ defineProps<{
   modelValue?: ConditionFormData
   triggerType: number
 }>()
+
 const emit = defineEmits<{
   (e: 'update:modelValue', value?: ConditionFormData): void
   (e: 'validate', result: { valid: boolean; message: string }): void

+ 4 - 8
src/views/iot/rule/scene/form/configs/MainConditionInnerConfig.vue

@@ -104,19 +104,15 @@ import { useVModel } from '@vueuse/core'
 /** 主条件内部配置组件 */
 defineOptions({ name: 'MainConditionInnerConfig' })
 
-interface Props {
+const props = defineProps<{
   modelValue: ConditionFormData
   triggerType: number
-}
+}>()
 
-interface Emits {
+const emit = defineEmits<{
   (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)

+ 4 - 8
src/views/iot/rule/scene/form/configs/SubConditionGroupConfig.vue

@@ -98,20 +98,16 @@ import {
 /** 子条件组配置组件 */
 defineOptions({ name: 'SubConditionGroupConfig' })
 
-interface Props {
+const props = defineProps<{
   modelValue: SubConditionGroupFormData
   triggerType: number
   maxConditions?: number
-}
+}>()
 
-interface Emits {
+const emit = defineEmits<{
   (e: 'update:modelValue', value: SubConditionGroupFormData): void
-
   (e: 'validate', result: { valid: boolean; message: string }): void
-}
-
-const props = defineProps<Props>()
-const emit = defineEmits<Emits>()
+}>()
 
 const subGroup = useVModel(props, 'modelValue', emit)
 

+ 4 - 9
src/views/iot/rule/scene/form/configs/TimerTriggerConfig.vue

@@ -25,18 +25,13 @@ import { Crontab } from '@/components/Crontab'
 /** 定时触发配置组件 */
 defineOptions({ name: 'TimerTriggerConfig' })
 
-// TODO @puhui999:下面的 Props、Emits 可以合并到变量那;
-interface Props {
+const props = defineProps<{
   modelValue?: string
-}
-
-interface Emits {
+}>()
+const emit = defineEmits<{
   (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, {
   defaultValue: '0 0 12 * * ?'

+ 4 - 7
src/views/iot/rule/scene/form/selectors/ConditionTypeSelector.vue

@@ -29,17 +29,14 @@ import { IotRuleSceneTriggerConditionTypeEnum } from '@/api/iot/rule/scene/scene
 /** 条件类型选择器组件 */
 defineOptions({ name: 'ConditionTypeSelector' })
 
-interface Props {
+defineProps<{
   modelValue?: number
-}
+}>()
 
-interface Emits {
+const emit = defineEmits<{
   (e: 'update:modelValue', value: number): void
   (e: 'change', value: number): void
-}
-
-const props = defineProps<Props>()
-const emit = defineEmits<Emits>()
+}>()
 
 // 条件类型选项
 const conditionTypeOptions = [

+ 7 - 10
src/views/iot/rule/scene/form/selectors/DeviceSelector.vue

@@ -18,9 +18,9 @@
     >
       <div class="flex items-center justify-between w-full py-4px">
         <div class="flex-1">
-          <div class="text-14px font-500 text-[var(--el-text-color-primary)] mb-2px">{{
-            device.deviceName
-          }}</div>
+          <div class="text-14px font-500 text-[var(--el-text-color-primary)] mb-2px"
+            >{{ device.deviceName }}
+          </div>
           <div class="text-12px text-[var(--el-text-color-secondary)]">{{ device.deviceKey }}</div>
         </div>
         <div class="flex items-center gap-4px">
@@ -42,18 +42,15 @@ import { DeviceApi } from '@/api/iot/device/device'
 /** 设备选择器组件 */
 defineOptions({ name: 'DeviceSelector' })
 
-interface Props {
+const props = defineProps<{
   modelValue?: number
   productId?: number
-}
+}>()
 
-interface Emits {
+const emit = defineEmits<{
   (e: 'update:modelValue', value?: number): void
   (e: 'change', value?: number): void
-}
-
-const props = defineProps<Props>()
-const emit = defineEmits<Emits>()
+}>()
 
 // 状态
 const deviceLoading = ref(false)

+ 4 - 8
src/views/iot/rule/scene/form/selectors/OperatorSelector.vue

@@ -39,19 +39,15 @@ import { useVModel } from '@vueuse/core'
 /** 操作符选择器组件 */
 defineOptions({ name: 'OperatorSelector' })
 
-interface Props {
+const props = defineProps<{
   modelValue?: string
   propertyType?: string
-}
+}>()
 
-interface Emits {
+const emit = defineEmits<{
   (e: 'update:modelValue', value: string): void
-
   (e: 'change', value: string): void
-}
-
-const props = defineProps<Props>()
-const emit = defineEmits<Emits>()
+}>()
 
 const localValue = useVModel(props, 'modelValue', emit)
 

+ 32 - 13
src/views/iot/rule/scene/form/selectors/ProductDeviceSelector.vue

@@ -22,13 +22,14 @@
             >
               <div class="flex items-center justify-between w-full py-4px">
                 <div class="flex-1">
-                  <div class="text-14px font-500 text-[var(--el-text-color-primary)] mb-2px">{{ product.name }}</div>
-                  <div class="text-12px text-[var(--el-text-color-secondary)]">{{ product.productKey }}</div>
+                  <div class="text-14px font-500 text-[var(--el-text-color-primary)] mb-2px"
+                    >{{ product.name }}
+                  </div>
+                  <div class="text-12px text-[var(--el-text-color-secondary)]"
+                    >{{ product.productKey }}
+                  </div>
                 </div>
-                <!-- TODO @puhui999:是不是用字典 -->
-                <el-tag size="small" :type="product.status === 0 ? 'success' : 'danger'">
-                  {{ product.status === 0 ? '正常' : '禁用' }}
-                </el-tag>
+                <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="product.status" />
               </div>
             </el-option>
           </el-select>
@@ -70,8 +71,12 @@
             >
               <div class="flex items-center justify-between w-full py-4px">
                 <div class="flex-1">
-                  <div class="text-14px font-500 text-[var(--el-text-color-primary)] mb-2px">{{ device.deviceName }}</div>
-                  <div class="text-12px text-[var(--el-text-color-secondary)]">{{ device.nickname || '无备注' }}</div>
+                  <div class="text-14px font-500 text-[var(--el-text-color-primary)] mb-2px"
+                    >{{ device.deviceName }}
+                  </div>
+                  <div class="text-12px text-[var(--el-text-color-secondary)]"
+                    >{{ device.nickname || '无备注' }}
+                  </div>
                 </div>
                 <el-tag size="small" :type="getDeviceStatusTag(device.state)">
                   {{ getDeviceStatusText(device.state) }}
@@ -84,7 +89,10 @@
     </el-row>
 
     <!-- 选择结果展示 -->
-    <div v-if="localProductId && localDeviceId !== undefined" class="mt-16px p-12px bg-[var(--el-fill-color-light)] rounded-6px border border-[var(--el-border-color-lighter)]">
+    <div
+      v-if="localProductId && localDeviceId !== undefined"
+      class="mt-16px p-12px bg-[var(--el-fill-color-light)] rounded-6px border border-[var(--el-border-color-lighter)]"
+    >
       <div class="flex items-center gap-6px mb-8px">
         <Icon icon="ep:check" class="text-[var(--el-color-success)] text-16px" />
         <span class="text-14px font-500 text-[var(--el-text-color-primary)]">已选择设备</span>
@@ -92,14 +100,22 @@
       <div class="flex flex-col gap-6px ml-22px">
         <div class="flex items-center gap-8px">
           <span class="text-12px text-[var(--el-text-color-secondary)] min-w-40px">产品:</span>
-          <span class="text-12px text-[var(--el-text-color-primary)] font-500">{{ selectedProduct?.name }}</span>
+          <span class="text-12px text-[var(--el-text-color-primary)] font-500">{{
+            selectedProduct?.name
+          }}</span>
           <el-tag size="small" type="primary">{{ selectedProduct?.productKey }}</el-tag>
         </div>
         <div class="flex items-center gap-8px">
           <span class="text-12px text-[var(--el-text-color-secondary)] min-w-40px">设备:</span>
-          <span v-if="deviceSelectionMode === 'all'" class="text-12px text-[var(--el-text-color-primary)] font-500">全部设备</span>
-          <span v-else class="text-12px text-[var(--el-text-color-primary)] font-500">{{ selectedDevice?.deviceName }}</span>
-          <el-tag v-if="deviceSelectionMode === 'all'" size="small" type="warning"> 全部 </el-tag>
+          <span
+            v-if="deviceSelectionMode === 'all'"
+            class="text-12px text-[var(--el-text-color-primary)] font-500"
+            >全部设备</span
+          >
+          <span v-else class="text-12px text-[var(--el-text-color-primary)] font-500">{{
+            selectedDevice?.deviceName
+          }}</span>
+          <el-tag v-if="deviceSelectionMode === 'all'" size="small" type="warning"> 全部</el-tag>
           <el-tag v-else size="small" :type="getDeviceStatusTag(selectedDevice?.state)">
             {{ getDeviceStatusText(selectedDevice?.state) }}
           </el-tag>
@@ -113,6 +129,7 @@
 import { useVModel } from '@vueuse/core'
 import { ProductApi } from '@/api/iot/product/product'
 import { DeviceApi } from '@/api/iot/device/device'
+import { DICT_TYPE } from '@/utils/dict'
 
 /** 产品设备选择器组件 */
 defineOptions({ name: 'ProductDeviceSelector' })
@@ -124,7 +141,9 @@ interface Props {
 
 interface Emits {
   (e: 'update:productId', value?: number): void
+
   (e: 'update:deviceId', value?: number): void
+
   (e: 'change', value: { productId?: number; deviceId?: number }): void
 }
 

+ 12 - 16
src/views/iot/rule/scene/form/selectors/ProductSelector.vue

@@ -17,16 +17,14 @@
     >
       <div class="flex items-center justify-between w-full py-4px">
         <div class="flex-1">
-          <div class="text-14px font-500 text-[var(--el-text-color-primary)] mb-2px">{{
-            product.name
-          }}</div>
-          <div class="text-12px text-[var(--el-text-color-secondary)]">{{
-            product.productKey
-          }}</div>
+          <div class="text-14px font-500 text-[var(--el-text-color-primary)] mb-2px"
+            >{{ product.name }}
+          </div>
+          <div class="text-12px text-[var(--el-text-color-secondary)]"
+            >{{ product.productKey }}
+          </div>
         </div>
-        <el-tag size="small" :type="product.status === 0 ? 'success' : 'danger'">
-          {{ product.status === 0 ? '正常' : '禁用' }}
-        </el-tag>
+        <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="product.status" />
       </div>
     </el-option>
   </el-select>
@@ -34,21 +32,19 @@
 
 <script setup lang="ts">
 import { ProductApi } from '@/api/iot/product/product'
+import { DICT_TYPE } from '@/utils/dict'
 
 /** 产品选择器组件 */
 defineOptions({ name: 'ProductSelector' })
 
-interface Props {
+defineProps<{
   modelValue?: number
-}
+}>()
 
-interface Emits {
+const emit = defineEmits<{
   (e: 'update:modelValue', value?: number): void
   (e: 'change', value?: number): void
-}
-
-defineProps<Props>()
-const emit = defineEmits<Emits>()
+}>()
 
 // 状态
 const productLoading = ref(false)