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

This commit is contained in:
puhui999
2025-08-03 21:16:58 +08:00
parent 300b844346
commit 1a9511bb04
3 changed files with 105 additions and 117 deletions

View File

@ -37,6 +37,7 @@ import BasicInfoSection from './sections/BasicInfoSection.vue'
import TriggerSection from './sections/TriggerSection.vue'
import ActionSection from './sections/ActionSection.vue'
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 { ElMessage } from 'element-plus'
import { generateUUID } from '@/utils'
@ -55,6 +56,8 @@ defineOptions({ name: 'RuleSceneForm' })
const props = defineProps<{
/** 抽屉显示状态 */
modelValue: boolean
/** 编辑的场景联动规则数据 */
ruleScene?: IotRuleSceneDO
}>()
/** 组件事件定义 */
@ -218,12 +221,13 @@ const handleActionValidate = (result: { valid: boolean; message: string }) => {
actionValidation.value = result
}
// TODO @puhui999API 调用
/** 提交表单 */
const handleSubmit = async () => {
// 校验表单
if (!formRef.value) return
const valid = await formRef.value.validate()
if (!valid) return
// 验证触发器和执行器
if (!triggerValidation.value.valid) {
ElMessage.error(triggerValidation.value.message)
@ -237,28 +241,22 @@ const handleSubmit = async () => {
// 提交请求
submitLoading.value = true
try {
console.log(formData.value)
// 转换数据格式
const apiData = convertFormToVO(formData.value)
if (true) {
console.log('转换后', apiData)
return
}
console.log('提交数据:', apiData)
// 调用API保存数据
if (isEdit.value) {
// 更新场景联动规则
// await RuleSceneApi.updateRuleScene(apiData)
console.log('更新数据:', apiData)
await RuleSceneApi.updateRuleScene(apiData)
ElMessage.success('更新成功')
} 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
emit('success')
} catch (error) {
@ -275,28 +273,36 @@ const handleClose = () => {
/** 初始化表单数据 */
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) => {
if (visible) {
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>

View File

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

View File

@ -300,3 +300,24 @@ export const IotRuleSceneTriggerTimeOperatorEnum = {
AFTER_TODAY: { name: '在今日之后', value: 'after_today' }, // 在今日之后
TODAY: { name: '在今日之间', value: 'today' } // 在今日之间
} 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 || '未知类型'
}