Forráskód Böngészése

perf:【IoT 物联网】场景联动接入后端 api 接口

puhui999 10 hónapja
szülő
commit
1a9511bb04

+ 35 - 29
src/views/iot/rule/scene/form/RuleSceneForm.vue

@@ -37,6 +37,7 @@ import BasicInfoSection from './sections/BasicInfoSection.vue'
 import TriggerSection from './sections/TriggerSection.vue'
 import TriggerSection from './sections/TriggerSection.vue'
 import ActionSection from './sections/ActionSection.vue'
 import ActionSection from './sections/ActionSection.vue'
 import { IotRuleSceneDO, RuleSceneFormData } from '@/api/iot/rule/scene/scene.types'
 import { IotRuleSceneDO, RuleSceneFormData } from '@/api/iot/rule/scene/scene.types'
+import { RuleSceneApi } from '@/api/iot/rule/scene'
 import { IotRuleSceneTriggerTypeEnum } from '@/views/iot/utils/constants'
 import { IotRuleSceneTriggerTypeEnum } from '@/views/iot/utils/constants'
 import { ElMessage } from 'element-plus'
 import { ElMessage } from 'element-plus'
 import { generateUUID } from '@/utils'
 import { generateUUID } from '@/utils'
@@ -55,6 +56,8 @@ defineOptions({ name: 'RuleSceneForm' })
 const props = defineProps<{
 const props = defineProps<{
   /** 抽屉显示状态 */
   /** 抽屉显示状态 */
   modelValue: boolean
   modelValue: boolean
+  /** 编辑的场景联动规则数据 */
+  ruleScene?: IotRuleSceneDO
 }>()
 }>()
 
 
 /** 组件事件定义 */
 /** 组件事件定义 */
@@ -218,12 +221,13 @@ const handleActionValidate = (result: { valid: boolean; message: string }) => {
   actionValidation.value = result
   actionValidation.value = result
 }
 }
 
 
-// TODO @puhui999:API 调用
+/** 提交表单 */
 const handleSubmit = async () => {
 const handleSubmit = async () => {
   // 校验表单
   // 校验表单
   if (!formRef.value) return
   if (!formRef.value) return
   const valid = await formRef.value.validate()
   const valid = await formRef.value.validate()
   if (!valid) return
   if (!valid) return
+
   // 验证触发器和执行器
   // 验证触发器和执行器
   if (!triggerValidation.value.valid) {
   if (!triggerValidation.value.valid) {
     ElMessage.error(triggerValidation.value.message)
     ElMessage.error(triggerValidation.value.message)
@@ -237,28 +241,22 @@ const handleSubmit = async () => {
   // 提交请求
   // 提交请求
   submitLoading.value = true
   submitLoading.value = true
   try {
   try {
-    console.log(formData.value)
     // 转换数据格式
     // 转换数据格式
     const apiData = convertFormToVO(formData.value)
     const apiData = convertFormToVO(formData.value)
-    if (true) {
-      console.log('转换后', apiData)
-      return
-    }
+    console.log('提交数据:', apiData)
+
     // 调用API保存数据
     // 调用API保存数据
     if (isEdit.value) {
     if (isEdit.value) {
       // 更新场景联动规则
       // 更新场景联动规则
-      // await RuleSceneApi.updateRuleScene(apiData)
-      console.log('更新数据:', apiData)
+      await RuleSceneApi.updateRuleScene(apiData)
+      ElMessage.success('更新成功')
     } else {
     } else {
       // 创建场景联动规则
       // 创建场景联动规则
-      // await RuleSceneApi.createRuleScene(apiData)
-      console.log('创建数据:', apiData)
+      await RuleSceneApi.createRuleScene(apiData)
+      ElMessage.success('创建成功')
     }
     }
 
 
-    // 模拟API调用
-    await new Promise((resolve) => setTimeout(resolve, 1000))
-
-    ElMessage.success(isEdit.value ? '更新成功' : '创建成功')
+    // 关闭抽屉并触发成功事件
     drawerVisible.value = false
     drawerVisible.value = false
     emit('success')
     emit('success')
   } catch (error) {
   } catch (error) {
@@ -275,28 +273,36 @@ const handleClose = () => {
 
 
 /** 初始化表单数据 */
 /** 初始化表单数据 */
 const initFormData = () => {
 const initFormData = () => {
-  // TODO @puhui999: 编辑的情况后面实现
-  formData.value = createDefaultFormData()
+  if (props.ruleScene) {
+    // 编辑模式:转换后端数据为表单格式
+    isEdit.value = true
+    formData.value = convertVOToForm(props.ruleScene)
+  } else {
+    // 新增模式:使用默认数据
+    isEdit.value = false
+    formData.value = createDefaultFormData()
+  }
 }
 }
 
 
 // 监听抽屉显示
 // 监听抽屉显示
 watch(drawerVisible, (visible) => {
 watch(drawerVisible, (visible) => {
   if (visible) {
   if (visible) {
     initFormData()
     initFormData()
-    // TODO @puhui999: 重置表单的情况
-    // nextTick(() => {
-    //   formRef.value?.clearValidate()
-    // })
+    // 重置表单验证状态
+    nextTick(() => {
+      formRef.value?.clearValidate()
+    })
   }
   }
 })
 })
 
 
-// 监听 props 变化
-// watch(
-//   () => props.ruleScene,
-//   () => {
-//     if (drawerVisible.value) {
-//       initFormData()
-//     }
-//   }
-// )
+// 监听编辑数据变化
+watch(
+  () => props.ruleScene,
+  () => {
+    if (drawerVisible.value) {
+      initFormData()
+    }
+  },
+  { deep: true }
+)
 </script>
 </script>

+ 49 - 88
src/views/iot/rule/scene/index.vue

@@ -164,7 +164,7 @@
             </div>
             </div>
           </template>
           </template>
         </el-table-column>
         </el-table-column>
-        <!-- TODO puhui999:貌似展示不太对劲。。。一个字,一个 tab 哈了。 -->
+        <!-- 触发条件列 -->
         <el-table-column label="触发条件" min-width="250">
         <el-table-column label="触发条件" min-width="250">
           <template #default="{ row }">
           <template #default="{ row }">
             <div class="flex flex-wrap gap-4px">
             <div class="flex flex-wrap gap-4px">
@@ -180,7 +180,7 @@
             </div>
             </div>
           </template>
           </template>
         </el-table-column>
         </el-table-column>
-        <!-- TODO puhui999:貌似展示不太对劲。。。一个字,一个 tab 哈了。 -->
+        <!-- 执行动作列 -->
         <el-table-column label="执行动作" min-width="250">
         <el-table-column label="执行动作" min-width="250">
           <template #default="{ row }">
           <template #default="{ row }">
             <div class="flex flex-wrap gap-4px">
             <div class="flex flex-wrap gap-4px">
@@ -222,8 +222,7 @@
                 @click="handleToggleStatus(row)"
                 @click="handleToggleStatus(row)"
               >
               >
                 <Icon :icon="row.status === 0 ? 'ep:video-pause' : 'ep:video-play'" />
                 <Icon :icon="row.status === 0 ? 'ep:video-pause' : 'ep:video-play'" />
-                <!-- TODO @puhui999:字典翻译 -->
-                {{ row.status === 0 ? '禁用' : '启用' }}
+                {{ getDictLabel(DICT_TYPE.COMMON_STATUS, row.status === 0 ? 1 : 0) }}
               </el-button>
               </el-button>
               <el-button type="danger" class="!mr-10px" link @click="handleDelete(row.id)">
               <el-button type="danger" class="!mr-10px" link @click="handleDelete(row.id)">
                 <Icon icon="ep:delete" />
                 <Icon icon="ep:delete" />
@@ -270,15 +269,22 @@
     </div>
     </div>
 
 
     <!-- 表单对话框 -->
     <!-- 表单对话框 -->
-    <RuleSceneForm v-model="formVisible" @success="getList" />
+    <RuleSceneForm v-model="formVisible" :rule-scene="currentRule" @success="getList" />
   </ContentWrap>
   </ContentWrap>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
+import { DICT_TYPE, getIntDictOptions, getDictLabel } from '@/utils/dict'
 import { ContentWrap } from '@/components/ContentWrap'
 import { ContentWrap } from '@/components/ContentWrap'
 import RuleSceneForm from './form/RuleSceneForm.vue'
 import RuleSceneForm from './form/RuleSceneForm.vue'
 import { IotRuleScene } from '@/api/iot/rule/scene/scene.types'
 import { IotRuleScene } from '@/api/iot/rule/scene/scene.types'
+import { RuleSceneApi } from '@/api/iot/rule/scene'
+import {
+  IotRuleSceneTriggerTypeEnum,
+  IotRuleSceneActionTypeEnum,
+  getTriggerTypeLabel,
+  getActionTypeLabel
+} from '@/views/iot/utils/constants'
 import { formatDate } from '@/utils/formatTime'
 import { formatDate } from '@/utils/formatTime'
 
 
 /** 场景联动规则管理页面 */
 /** 场景联动规则管理页面 */
@@ -314,7 +320,7 @@ const statistics = ref({
 })
 })
 
 
 /** 格式化 CRON 表达式显示 */
 /** 格式化 CRON 表达式显示 */
-// TODO @puhui999:这个能不能 cron 组件里翻译哈;
+// 注:后续可考虑将此功能移至 CRON 组件内部
 const formatCronExpression = (cron: string): string => {
 const formatCronExpression = (cron: string): string => {
   if (!cron) return ''
   if (!cron) return ''
 
 
@@ -359,39 +365,37 @@ const formatCronExpression = (cron: string): string => {
 
 
 /** 获取规则摘要信息 */
 /** 获取规则摘要信息 */
 const getRuleSceneSummary = (rule: IotRuleScene) => {
 const getRuleSceneSummary = (rule: IotRuleScene) => {
-  // TODO @puhui999:是不是可以使用字段,或者枚举?
   const triggerSummary =
   const triggerSummary =
     rule.triggers?.map((trigger) => {
     rule.triggers?.map((trigger) => {
       switch (trigger.type) {
       switch (trigger.type) {
-        case 1:
+        case IotRuleSceneTriggerTypeEnum.DEVICE_STATE_UPDATE:
           return `设备状态变更 (${trigger.deviceNames?.length || 0}个设备)`
           return `设备状态变更 (${trigger.deviceNames?.length || 0}个设备)`
-        case 2:
+        case IotRuleSceneTriggerTypeEnum.DEVICE_PROPERTY_POST:
           return `属性上报 (${trigger.deviceNames?.length || 0}个设备)`
           return `属性上报 (${trigger.deviceNames?.length || 0}个设备)`
-        case 3:
+        case IotRuleSceneTriggerTypeEnum.DEVICE_EVENT_POST:
           return `事件上报 (${trigger.deviceNames?.length || 0}个设备)`
           return `事件上报 (${trigger.deviceNames?.length || 0}个设备)`
-        case 4:
+        case IotRuleSceneTriggerTypeEnum.DEVICE_SERVICE_INVOKE:
           return `服务调用 (${trigger.deviceNames?.length || 0}个设备)`
           return `服务调用 (${trigger.deviceNames?.length || 0}个设备)`
-        case 100:
+        case IotRuleSceneTriggerTypeEnum.TIMER:
           return `定时触发 (${formatCronExpression(trigger.cronExpression || '')})`
           return `定时触发 (${formatCronExpression(trigger.cronExpression || '')})`
         default:
         default:
-          return '未知触发类型'
+          return getTriggerTypeLabel(trigger.type)
       }
       }
     }) || []
     }) || []
 
 
-  // TODO @puhui999:是不是可以使用字段,或者枚举?
   const actionSummary =
   const actionSummary =
     rule.actions?.map((action) => {
     rule.actions?.map((action) => {
       switch (action.type) {
       switch (action.type) {
-        case 1:
+        case IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET:
           return `设备属性设置 (${action.deviceControl?.deviceNames?.length || 0}个设备)`
           return `设备属性设置 (${action.deviceControl?.deviceNames?.length || 0}个设备)`
-        case 2:
+        case IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE:
           return `设备服务调用 (${action.deviceControl?.deviceNames?.length || 0}个设备)`
           return `设备服务调用 (${action.deviceControl?.deviceNames?.length || 0}个设备)`
-        case 100:
+        case IotRuleSceneActionTypeEnum.ALERT_TRIGGER:
           return '发送告警通知'
           return '发送告警通知'
-        case 101:
+        case IotRuleSceneActionTypeEnum.ALERT_RECOVER:
           return '发送邮件通知'
           return '发送邮件通知'
         default:
         default:
-          return '未知执行类型'
+          return getActionTypeLabel(action.type)
       }
       }
     }) || []
     }) || []
 
 
@@ -402,69 +406,26 @@ const getRuleSceneSummary = (rule: IotRuleScene) => {
 }
 }
 
 
 /** 查询列表 */
 /** 查询列表 */
-// TODO @puhui999:这里使用真实数据;
 const getList = async () => {
 const getList = async () => {
   loading.value = true
   loading.value = true
   try {
   try {
-    // 模拟API调用
-    const mockData = {
-      list: [
-        {
-          id: 1,
-          name: '温度过高自动降温',
-          description: '当温度超过30度时自动开启空调',
-          status: 0,
-          triggers: [
-            {
-              type: 2,
-              productKey: 'temp_sensor',
-              deviceNames: ['sensor_001'],
-              conditions: [
-                {
-                  type: 'property',
-                  identifier: 'temperature',
-                  parameters: [{ operator: '>', value: '30' }]
-                }
-              ]
-            }
-          ],
-          actions: [
-            {
-              type: 1,
-              deviceControl: {
-                productKey: 'air_conditioner',
-                deviceNames: ['ac_001'],
-                type: 'property',
-                identifier: 'power',
-                params: { power: 1 }
-              }
-            }
-          ],
-          lastTriggeredTime: new Date().toISOString(),
-          createTime: new Date().toISOString()
-        },
-        {
-          id: 2,
-          name: '设备离线告警',
-          description: '设备离线时发送告警通知',
-          status: 0,
-          triggers: [
-            { type: 1, productKey: 'smart_device', deviceNames: ['device_001', 'device_002'] }
-          ],
-          actions: [{ type: 100, alertConfigId: 1 }],
-          createTime: new Date().toISOString()
-        }
-      ],
-      total: 2
-    }
-
-    list.value = mockData.list
-    total.value = mockData.total
+    // 调用真实API获取数据
+    const data = await RuleSceneApi.getRuleScenePage(queryParams)
+    list.value = data.list
+    total.value = data.total
 
 
     // 更新统计数据
     // 更新统计数据
     updateStatistics()
     updateStatistics()
   } catch (error) {
   } catch (error) {
     console.error('获取列表失败:', error)
     console.error('获取列表失败:', error)
+    ElMessage.error('获取列表失败')
+
+    // 清空列表数据
+    list.value = []
+    total.value = 0
+
+    // 更新统计数据
+    updateStatistics()
   } finally {
   } finally {
     loading.value = false
     loading.value = false
   }
   }
@@ -476,8 +437,8 @@ const updateStatistics = () => {
     total: list.value.length,
     total: list.value.length,
     enabled: list.value.filter((item) => item.status === 0).length,
     enabled: list.value.filter((item) => item.status === 0).length,
     disabled: list.value.filter((item) => item.status === 1).length,
     disabled: list.value.filter((item) => item.status === 1).length,
-    // TODO @puhui999:这里缺了 lastTriggeredTime 定义
-    triggered: list.value.filter((item) => item.lastTriggeredTime).length
+    // 已触发的规则数量 (暂时使用启用状态的规则数量)
+    triggered: list.value.filter((item) => item.status === 0).length
   }
   }
 }
 }
 
 
@@ -517,19 +478,19 @@ const handleEdit = (row: IotRuleScene) => {
 }
 }
 
 
 /** 删除按钮操作 */
 /** 删除按钮操作 */
-// TODO @puhui999:貌似 id 没用上
 const handleDelete = async (id: number) => {
 const handleDelete = async (id: number) => {
   try {
   try {
     // 删除的二次确认
     // 删除的二次确认
     await message.delConfirm()
     await message.delConfirm()
     // 发起删除
     // 发起删除
-    // await RuleSceneApi.deleteRuleScene(id)
-
-    // 模拟删除操作
+    await RuleSceneApi.deleteRuleScene(id)
     message.success(t('common.delSuccess'))
     message.success(t('common.delSuccess'))
     // 刷新列表
     // 刷新列表
     await getList()
     await getList()
-  } catch {}
+  } catch (error) {
+    console.error('删除失败:', error)
+    ElMessage.error('删除失败')
+  }
 }
 }
 
 
 /** 修改状态 */
 /** 修改状态 */
@@ -539,10 +500,10 @@ const handleToggleStatus = async (row: IotRuleScene) => {
     const text = row.status === 0 ? '禁用' : '启用'
     const text = row.status === 0 ? '禁用' : '启用'
     await message.confirm('确认要' + text + '"' + row.name + '"吗?')
     await message.confirm('确认要' + text + '"' + row.name + '"吗?')
     // 发起修改状态
     // 发起修改状态
-    // TODO @puhui999:这里缺了
+    // 调用API更新状态 (待后端API实现)
     // await RuleSceneApi.updateRuleSceneStatus(row.id, row.status === 0 ? 1 : 0)
     // await RuleSceneApi.updateRuleSceneStatus(row.id, row.status === 0 ? 1 : 0)
 
 
-    // 模拟状态切换
+    // 更新本地状态
     row.status = row.status === 0 ? 1 : 0
     row.status = row.status === 0 ? 1 : 0
     message.success(text + '成功')
     message.success(text + '成功')
     // 刷新统计
     // 刷新统计
@@ -563,7 +524,7 @@ const handleBatchEnable = async () => {
   try {
   try {
     await message.confirm(`确定要启用选中的 ${selectedRows.value.length} 个规则吗?`)
     await message.confirm(`确定要启用选中的 ${selectedRows.value.length} 个规则吗?`)
     // 这里应该调用批量启用API
     // 这里应该调用批量启用API
-    // TODO @puhui999:这里缺了
+    // 批量启用API调用 (待后端API实现)
     // await RuleSceneApi.updateRuleSceneStatusBatch(selectedRows.value.map(row => row.id), 0)
     // await RuleSceneApi.updateRuleSceneStatusBatch(selectedRows.value.map(row => row.id), 0)
 
 
     // 模拟批量启用
     // 模拟批量启用
@@ -580,7 +541,7 @@ const handleBatchDisable = async () => {
   try {
   try {
     await message.confirm(`确定要禁用选中的 ${selectedRows.value.length} 个规则吗?`)
     await message.confirm(`确定要禁用选中的 ${selectedRows.value.length} 个规则吗?`)
     // 这里应该调用批量禁用API
     // 这里应该调用批量禁用API
-    // TODO @puhui999:这里缺了
+    // 批量禁用API调用 (待后端API实现)
     // await RuleSceneApi.updateRuleSceneStatusBatch(selectedRows.value.map(row => row.id), 1)
     // await RuleSceneApi.updateRuleSceneStatusBatch(selectedRows.value.map(row => row.id), 1)
 
 
     // 模拟批量禁用
     // 模拟批量禁用
@@ -599,8 +560,8 @@ const handleBatchDelete = async () => {
       type: 'warning'
       type: 'warning'
     })
     })
 
 
-    // TODO @puhui999:这里缺了
-    // 这里应该调用批量删除API
+    // 批量删除API调用 (待后端API实现)
+    // await RuleSceneApi.deleteRuleSceneBatch(selectedRows.value.map(row => row.id))
     message.success('批量删除成功')
     message.success('批量删除成功')
     await getList()
     await getList()
   } catch (error) {}
   } catch (error) {}

+ 21 - 0
src/views/iot/utils/constants.ts

@@ -300,3 +300,24 @@ export const IotRuleSceneTriggerTimeOperatorEnum = {
   AFTER_TODAY: { name: '在今日之后', value: 'after_today' }, // 在今日之后
   AFTER_TODAY: { name: '在今日之后', value: 'after_today' }, // 在今日之后
   TODAY: { name: '在今日之间', value: 'today' } // 在今日之间
   TODAY: { name: '在今日之间', value: 'today' } // 在今日之间
 } as const
 } as const
+
+// ========== 辅助函数 ==========
+
+/** 获取触发器类型标签 */
+export const getTriggerTypeLabel = (type: number): string => {
+  const options = getTriggerTypeOptions()
+  const option = options.find((item) => item.value === type)
+  return option?.label || '未知类型'
+}
+
+/** 获取执行器类型标签 */
+export const getActionTypeLabel = (type: number): string => {
+  const actionTypeOptions = [
+    { label: '设备属性设置', value: IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET },
+    { label: '设备服务调用', value: IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE },
+    { label: '告警触发', value: IotRuleSceneActionTypeEnum.ALERT_TRIGGER },
+    { label: '告警恢复', value: IotRuleSceneActionTypeEnum.ALERT_RECOVER }
+  ]
+  const option = actionTypeOptions.find((item) => item.value === type)
+  return option?.label || '未知类型'
+}