2025-03-28 15:54:12 +08:00
|
|
|
|
<template>
|
2025-03-29 20:27:38 +08:00
|
|
|
|
<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>
|
2025-07-01 21:04:40 +08:00
|
|
|
|
<div v-if="isDeviceAction" class="flex items-center mr-60px">
|
2025-03-29 20:27:38 +08:00
|
|
|
|
<span class="mr-10px">产品</span>
|
|
|
|
|
|
<el-button type="primary" @click="handleSelectProduct" size="small" plain>
|
|
|
|
|
|
{{ product ? product.name : '选择产品' }}
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</div>
|
2025-07-17 21:31:12 +08:00
|
|
|
|
<!-- TODO @puhui999:单选设备,默认不选就是全部设备 -->
|
2025-07-01 21:04:40 +08:00
|
|
|
|
<div v-if="isDeviceAction" class="flex items-center mr-60px">
|
2025-03-29 20:27:38 +08:00
|
|
|
|
<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>
|
2025-03-28 15:54:12 +08:00
|
|
|
|
</div>
|
2025-03-29 20:27:38 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 设备控制执行器 -->
|
2025-07-06 10:45:12 +08:00
|
|
|
|
<!-- TODO @puhui999:服务调用时,选择好某个物模型,剩余直接填写一个 JSON 哈;不用逐个添加~可以试试【设备详情-设备调试】那,有服务调用的模拟 -->
|
2025-03-29 20:27:38 +08:00
|
|
|
|
<DeviceControlAction
|
2025-07-01 21:04:40 +08:00
|
|
|
|
v-if="isDeviceAction"
|
|
|
|
|
|
:action-type="actionConfig.type"
|
2025-03-29 20:27:38 +08:00
|
|
|
|
:model-value="actionConfig.deviceControl"
|
|
|
|
|
|
:product-id="product?.id"
|
|
|
|
|
|
:product-key="product?.productKey"
|
|
|
|
|
|
@update:model-value="(val) => (actionConfig.deviceControl = val)"
|
|
|
|
|
|
/>
|
2025-03-28 15:54:12 +08:00
|
|
|
|
|
2025-07-05 22:05:44 +08:00
|
|
|
|
<!-- 告警执行器 -->
|
|
|
|
|
|
<div v-else-if="isAlertAction">
|
|
|
|
|
|
<!-- 告警触发 - 无需额外配置 -->
|
|
|
|
|
|
<div
|
|
|
|
|
|
v-if="actionConfig.type === IotRuleSceneActionTypeEnum.ALERT_TRIGGER"
|
|
|
|
|
|
class="bg-[#dbe5f6] flex items-center justify-center p-10px"
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-icon class="mr-5px text-blue-500"><Icon icon="ep:info-filled" /></el-icon>
|
|
|
|
|
|
<span class="text-gray-600">触发告警通知,系统将自动发送告警信息</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 告警恢复 - 需要选择告警配置 -->
|
|
|
|
|
|
<div v-else-if="actionConfig.type === IotRuleSceneActionTypeEnum.ALERT_RECOVER">
|
|
|
|
|
|
<div class="bg-[#dbe5f6] flex items-center justify-center p-10px mb-10px">
|
|
|
|
|
|
<el-icon class="mr-5px text-blue-500"><Icon icon="ep:info-filled" /></el-icon>
|
2025-07-06 10:45:12 +08:00
|
|
|
|
<!-- TODO @puhui999:这种类型的提示,感觉放在 SELECT 那,后面有个所有的提示,会不会更好呀;因为可以少占用行呢; -->
|
2025-07-05 22:05:44 +08:00
|
|
|
|
<span class="text-gray-600">恢复指定的告警配置状态</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="p-10px">
|
2025-07-06 10:45:12 +08:00
|
|
|
|
<el-form-item label="选择告警配置" required label-width="110">
|
2025-07-05 22:05:44 +08:00
|
|
|
|
<el-select
|
|
|
|
|
|
v-model="actionConfig.alertConfigId"
|
|
|
|
|
|
class="!w-240px"
|
|
|
|
|
|
clearable
|
|
|
|
|
|
placeholder="请选择要恢复的告警配置"
|
|
|
|
|
|
:loading="alertConfigLoading"
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-option
|
|
|
|
|
|
v-for="config in alertConfigList"
|
|
|
|
|
|
:key="config.id"
|
|
|
|
|
|
:label="config.name"
|
|
|
|
|
|
:value="config.id"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-07-05 10:54:43 +08:00
|
|
|
|
</div>
|
2025-03-29 20:27:38 +08:00
|
|
|
|
</div>
|
2025-03-28 15:54:12 +08:00
|
|
|
|
|
2025-03-29 20:27:38 +08:00
|
|
|
|
<!-- 产品、设备的选择 -->
|
|
|
|
|
|
<ProductTableSelect ref="productTableSelectRef" @success="handleProductSelect" />
|
|
|
|
|
|
<DeviceTableSelect
|
|
|
|
|
|
ref="deviceTableSelectRef"
|
|
|
|
|
|
multiple
|
|
|
|
|
|
:product-id="product?.id"
|
|
|
|
|
|
@success="handleDeviceSelect"
|
2025-03-28 15:54:12 +08:00
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import { useVModel } from '@vueuse/core'
|
2025-03-29 12:52:26 +08:00
|
|
|
|
import { isEmpty } from '@/utils/is'
|
2025-03-28 15:54:12 +08:00
|
|
|
|
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
|
|
|
|
|
import ProductTableSelect from '@/views/iot/product/product/components/ProductTableSelect.vue'
|
|
|
|
|
|
import DeviceTableSelect from '@/views/iot/device/device/components/DeviceTableSelect.vue'
|
|
|
|
|
|
import DeviceControlAction from './DeviceControlAction.vue'
|
2025-03-29 12:07:27 +08:00
|
|
|
|
import { ProductApi, ProductVO } from '@/api/iot/product/product'
|
|
|
|
|
|
import { DeviceApi, DeviceVO } from '@/api/iot/device/device'
|
2025-07-05 22:05:44 +08:00
|
|
|
|
import { AlertConfigApi, AlertConfig } from '@/api/iot/alert/config'
|
2025-03-28 15:54:12 +08:00
|
|
|
|
import {
|
|
|
|
|
|
ActionConfig,
|
|
|
|
|
|
ActionDeviceControl,
|
2025-03-28 18:20:53 +08:00
|
|
|
|
IotDeviceMessageIdentifierEnum,
|
2025-03-28 17:22:52 +08:00
|
|
|
|
IotDeviceMessageTypeEnum,
|
2025-03-28 18:20:53 +08:00
|
|
|
|
IotRuleSceneActionTypeEnum
|
2025-03-28 15:54:12 +08:00
|
|
|
|
} from '@/api/iot/rule/scene/scene.types'
|
|
|
|
|
|
|
|
|
|
|
|
/** 场景联动之执行器组件 */
|
|
|
|
|
|
defineOptions({ name: 'ActionExecutor' })
|
|
|
|
|
|
|
|
|
|
|
|
const props = defineProps<{ modelValue: any }>()
|
|
|
|
|
|
const emits = defineEmits(['update:modelValue'])
|
|
|
|
|
|
const actionConfig = useVModel(props, 'modelValue', emits) as Ref<ActionConfig>
|
|
|
|
|
|
|
|
|
|
|
|
const message = useMessage()
|
|
|
|
|
|
|
2025-07-01 21:04:40 +08:00
|
|
|
|
/** 计算属性:判断是否为设备相关执行类型 */
|
|
|
|
|
|
const isDeviceAction = computed(() => {
|
|
|
|
|
|
return [
|
|
|
|
|
|
IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET,
|
|
|
|
|
|
IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE
|
|
|
|
|
|
].includes(actionConfig.value.type as any)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
/** 计算属性:判断是否为告警相关执行类型 */
|
|
|
|
|
|
const isAlertAction = computed(() => {
|
|
|
|
|
|
return [
|
|
|
|
|
|
IotRuleSceneActionTypeEnum.ALERT_TRIGGER,
|
|
|
|
|
|
IotRuleSceneActionTypeEnum.ALERT_RECOVER
|
|
|
|
|
|
].includes(actionConfig.value.type as any)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2025-03-29 13:04:50 +08:00
|
|
|
|
/** 初始化执行器结构 */
|
2025-03-28 15:54:12 +08:00
|
|
|
|
const initActionConfig = () => {
|
|
|
|
|
|
if (!actionConfig.value) {
|
2025-07-01 21:04:40 +08:00
|
|
|
|
actionConfig.value = { type: IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET } as ActionConfig
|
2025-03-28 15:54:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 设备控制执行器初始化
|
2025-07-01 21:04:40 +08:00
|
|
|
|
if (isDeviceAction.value && !actionConfig.value.deviceControl) {
|
2025-03-28 17:22:52 +08:00
|
|
|
|
actionConfig.value.deviceControl = {
|
|
|
|
|
|
productKey: '',
|
|
|
|
|
|
deviceNames: [],
|
2025-07-01 21:04:40 +08:00
|
|
|
|
type:
|
|
|
|
|
|
actionConfig.value.type === IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET
|
|
|
|
|
|
? IotDeviceMessageTypeEnum.PROPERTY
|
|
|
|
|
|
: IotDeviceMessageTypeEnum.SERVICE,
|
|
|
|
|
|
identifier:
|
|
|
|
|
|
actionConfig.value.type === IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET
|
|
|
|
|
|
? IotDeviceMessageIdentifierEnum.PROPERTY_SET
|
|
|
|
|
|
: IotDeviceMessageIdentifierEnum.SERVICE_INVOKE,
|
2025-03-28 17:22:52 +08:00
|
|
|
|
data: {}
|
|
|
|
|
|
} as ActionDeviceControl
|
2025-03-28 15:54:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-05 22:05:44 +08:00
|
|
|
|
// 告警执行器初始化
|
2025-07-05 10:54:43 +08:00
|
|
|
|
if (isAlertAction.value) {
|
2025-07-05 22:05:44 +08:00
|
|
|
|
if (actionConfig.value.type === IotRuleSceneActionTypeEnum.ALERT_TRIGGER) {
|
|
|
|
|
|
// 告警触发 - 无需额外配置
|
|
|
|
|
|
actionConfig.value.alertConfigId = undefined
|
|
|
|
|
|
} else if (actionConfig.value.type === IotRuleSceneActionTypeEnum.ALERT_RECOVER) {
|
|
|
|
|
|
// 告警恢复 - 需要选择告警配置
|
|
|
|
|
|
if (!actionConfig.value.alertConfigId) {
|
|
|
|
|
|
actionConfig.value.alertConfigId = undefined
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-03-28 15:54:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-29 13:04:50 +08:00
|
|
|
|
/** 产品和设备选择 */
|
2025-03-28 15:54:12 +08:00
|
|
|
|
const productTableSelectRef = ref<InstanceType<typeof ProductTableSelect>>()
|
|
|
|
|
|
const deviceTableSelectRef = ref<InstanceType<typeof DeviceTableSelect>>()
|
|
|
|
|
|
const product = ref<ProductVO>()
|
|
|
|
|
|
const deviceList = ref<DeviceVO[]>([])
|
|
|
|
|
|
|
2025-07-05 22:05:44 +08:00
|
|
|
|
/** 告警配置相关 */
|
|
|
|
|
|
const alertConfigList = ref<AlertConfig[]>([])
|
|
|
|
|
|
const alertConfigLoading = ref(false)
|
|
|
|
|
|
|
2025-03-28 17:22:52 +08:00
|
|
|
|
/** 处理选择产品 */
|
|
|
|
|
|
const handleSelectProduct = () => {
|
|
|
|
|
|
productTableSelectRef.value?.open()
|
2025-03-28 15:54:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-28 17:22:52 +08:00
|
|
|
|
/** 处理选择设备 */
|
|
|
|
|
|
const handleSelectDevice = () => {
|
2025-03-28 15:54:12 +08:00
|
|
|
|
if (!product.value) {
|
|
|
|
|
|
message.warning('请先选择一个产品')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
deviceTableSelectRef.value?.open()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-28 17:22:52 +08:00
|
|
|
|
/** 处理产品选择成功 */
|
|
|
|
|
|
const handleProductSelect = (val: ProductVO) => {
|
|
|
|
|
|
product.value = val
|
|
|
|
|
|
if (actionConfig.value.deviceControl) {
|
|
|
|
|
|
actionConfig.value.deviceControl.productKey = val.productKey
|
|
|
|
|
|
}
|
|
|
|
|
|
// 重置设备选择
|
|
|
|
|
|
deviceList.value = []
|
|
|
|
|
|
if (actionConfig.value.deviceControl) {
|
|
|
|
|
|
actionConfig.value.deviceControl.deviceNames = []
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 处理设备选择成功 */
|
|
|
|
|
|
const handleDeviceSelect = (val: DeviceVO[]) => {
|
|
|
|
|
|
deviceList.value = val
|
|
|
|
|
|
if (actionConfig.value.deviceControl) {
|
|
|
|
|
|
actionConfig.value.deviceControl.deviceNames = val.map((item) => item.deviceName)
|
2025-03-28 15:54:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-03-28 18:20:53 +08:00
|
|
|
|
|
2025-07-05 22:05:44 +08:00
|
|
|
|
/** 获取告警配置列表 */
|
|
|
|
|
|
const getAlertConfigList = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
alertConfigLoading.value = true
|
|
|
|
|
|
alertConfigList.value = await AlertConfigApi.getSimpleAlertConfigList()
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取告警配置列表失败:', error)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
alertConfigLoading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-29 13:04:50 +08:00
|
|
|
|
/** 监听执行类型变化,初始化对应配置 */
|
2025-03-28 18:20:53 +08:00
|
|
|
|
watch(
|
|
|
|
|
|
() => actionConfig.value.type,
|
2025-07-05 22:05:44 +08:00
|
|
|
|
(newType) => {
|
2025-03-28 18:20:53 +08:00
|
|
|
|
initActionConfig()
|
2025-07-05 22:05:44 +08:00
|
|
|
|
// 如果是告警恢复类型,需要加载告警配置列表
|
|
|
|
|
|
if (newType === IotRuleSceneActionTypeEnum.ALERT_RECOVER) {
|
|
|
|
|
|
getAlertConfigList()
|
|
|
|
|
|
}
|
2025-03-28 18:20:53 +08:00
|
|
|
|
},
|
|
|
|
|
|
{ immediate: true }
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2025-03-30 10:18:33 +08:00
|
|
|
|
/** 初始化产品回显信息 */
|
2025-03-29 12:07:27 +08:00
|
|
|
|
const initProductInfo = async () => {
|
|
|
|
|
|
if (!actionConfig.value.deviceControl?.productKey) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const productData = await ProductApi.getProductByKey(
|
|
|
|
|
|
actionConfig.value.deviceControl.productKey
|
|
|
|
|
|
)
|
|
|
|
|
|
if (productData) {
|
|
|
|
|
|
product.value = productData
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取产品信息失败:', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 初始化设备回显信息
|
|
|
|
|
|
*/
|
|
|
|
|
|
const initDeviceInfo = async () => {
|
|
|
|
|
|
if (
|
|
|
|
|
|
!actionConfig.value.deviceControl?.productKey ||
|
|
|
|
|
|
!actionConfig.value.deviceControl?.deviceNames?.length
|
|
|
|
|
|
) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const deviceData = await DeviceApi.getDevicesByProductKeyAndNames(
|
|
|
|
|
|
actionConfig.value.deviceControl.productKey,
|
|
|
|
|
|
actionConfig.value.deviceControl.deviceNames
|
|
|
|
|
|
)
|
|
|
|
|
|
if (deviceData && deviceData.length > 0) {
|
|
|
|
|
|
deviceList.value = deviceData
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取设备信息失败:', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-28 18:20:53 +08:00
|
|
|
|
/** 初始化 */
|
2025-03-29 12:07:27 +08:00
|
|
|
|
onMounted(async () => {
|
2025-03-28 18:20:53 +08:00
|
|
|
|
initActionConfig()
|
2025-03-29 12:07:27 +08:00
|
|
|
|
|
|
|
|
|
|
// 初始化产品和设备回显
|
|
|
|
|
|
if (actionConfig.value.deviceControl) {
|
|
|
|
|
|
await initProductInfo()
|
|
|
|
|
|
await initDeviceInfo()
|
|
|
|
|
|
}
|
2025-03-28 18:20:53 +08:00
|
|
|
|
})
|
2025-03-28 15:54:12 +08:00
|
|
|
|
</script>
|