2025-07-17 21:31:12 +08:00
|
|
|
|
<!-- 执行器配置组件 -->
|
2025-07-18 23:55:51 +08:00
|
|
|
|
<!-- todo @puhui999:参考“触发器配置”,简化下。 -->
|
2025-07-17 21:31:12 +08:00
|
|
|
|
<template>
|
2025-07-26 14:20:07 +08:00
|
|
|
|
<el-card class="border border-[var(--el-border-color-light)] rounded-8px" shadow="never">
|
2025-07-17 21:31:12 +08:00
|
|
|
|
<template #header>
|
2025-07-26 14:20:07 +08:00
|
|
|
|
<div class="flex items-center justify-between">
|
|
|
|
|
|
<div class="flex items-center gap-8px">
|
|
|
|
|
|
<Icon icon="ep:setting" class="text-[var(--el-color-primary)] text-18px" />
|
|
|
|
|
|
<span class="text-16px font-600 text-[var(--el-text-color-primary)]">执行器配置</span>
|
2025-07-17 21:31:12 +08:00
|
|
|
|
<el-tag size="small" type="info">{{ actions.length }}/{{ maxActions }}</el-tag>
|
|
|
|
|
|
</div>
|
2025-07-26 14:20:07 +08:00
|
|
|
|
<div class="flex items-center gap-8px">
|
2025-07-17 21:31:12 +08:00
|
|
|
|
<el-button
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
@click="addAction"
|
|
|
|
|
|
:disabled="actions.length >= maxActions"
|
|
|
|
|
|
>
|
|
|
|
|
|
<Icon icon="ep:plus" />
|
|
|
|
|
|
添加执行器
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
2025-07-26 14:20:07 +08:00
|
|
|
|
<div class="p-0">
|
2025-07-17 21:31:12 +08:00
|
|
|
|
<!-- 空状态 -->
|
2025-07-26 14:20:07 +08:00
|
|
|
|
<div v-if="actions.length === 0">
|
2025-07-17 21:31:12 +08:00
|
|
|
|
<el-empty description="暂无执行器配置">
|
|
|
|
|
|
<el-button type="primary" @click="addAction">
|
|
|
|
|
|
<Icon icon="ep:plus" />
|
|
|
|
|
|
添加第一个执行器
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</el-empty>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 执行器列表 -->
|
2025-07-26 14:20:07 +08:00
|
|
|
|
<div v-else class="space-y-16px">
|
2025-08-02 10:31:24 +08:00
|
|
|
|
<div
|
|
|
|
|
|
v-for="(action, index) in actions"
|
|
|
|
|
|
:key="`action-${index}`"
|
|
|
|
|
|
class="p-16px border border-[var(--el-border-color-lighter)] rounded-6px bg-[var(--el-fill-color-blank)]"
|
|
|
|
|
|
>
|
2025-07-26 14:20:07 +08:00
|
|
|
|
<div class="flex items-center justify-between mb-16px">
|
|
|
|
|
|
<div class="flex items-center gap-8px">
|
|
|
|
|
|
<Icon icon="ep:setting" class="text-[var(--el-color-success)] text-16px" />
|
2025-07-17 21:31:12 +08:00
|
|
|
|
<span>执行器 {{ index + 1 }}</span>
|
2025-07-18 23:55:51 +08:00
|
|
|
|
<el-tag :type="getActionTypeTag(action.type)" size="small">
|
2025-07-17 21:31:12 +08:00
|
|
|
|
{{ getActionTypeName(action.type) }}
|
|
|
|
|
|
</el-tag>
|
|
|
|
|
|
</div>
|
2025-07-26 14:20:07 +08:00
|
|
|
|
<div>
|
2025-07-17 21:31:12 +08:00
|
|
|
|
<el-button
|
|
|
|
|
|
type="danger"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
text
|
|
|
|
|
|
@click="removeAction(index)"
|
|
|
|
|
|
v-if="actions.length > 1"
|
|
|
|
|
|
>
|
|
|
|
|
|
<Icon icon="ep:delete" />
|
|
|
|
|
|
删除
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2025-07-26 14:20:07 +08:00
|
|
|
|
<div class="space-y-16px">
|
2025-07-17 21:31:12 +08:00
|
|
|
|
<!-- 执行类型选择 -->
|
|
|
|
|
|
<ActionTypeSelector
|
|
|
|
|
|
:model-value="action.type"
|
|
|
|
|
|
@update:model-value="(value) => updateActionType(index, value)"
|
|
|
|
|
|
@change="onActionTypeChange(action, $event)"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 设备控制配置 -->
|
|
|
|
|
|
<DeviceControlConfig
|
|
|
|
|
|
v-if="isDeviceAction(action.type)"
|
|
|
|
|
|
:model-value="action"
|
|
|
|
|
|
@update:model-value="(value) => updateAction(index, value)"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 告警配置 -->
|
|
|
|
|
|
<AlertConfig
|
|
|
|
|
|
v-if="isAlertAction(action.type)"
|
|
|
|
|
|
:model-value="action.alertConfigId"
|
|
|
|
|
|
@update:model-value="(value) => updateActionAlertConfig(index, value)"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 添加提示 -->
|
2025-07-26 14:20:07 +08:00
|
|
|
|
<div v-if="actions.length > 0 && actions.length < maxActions" class="text-center py-16px">
|
|
|
|
|
|
<el-button type="primary" plain @click="addAction">
|
2025-07-17 21:31:12 +08:00
|
|
|
|
<Icon icon="ep:plus" />
|
|
|
|
|
|
继续添加执行器
|
|
|
|
|
|
</el-button>
|
2025-08-02 10:31:24 +08:00
|
|
|
|
<span class="block mt-8px text-12px text-[var(--el-text-color-secondary)]">
|
|
|
|
|
|
最多可添加 {{ maxActions }} 个执行器
|
|
|
|
|
|
</span>
|
2025-07-17 21:31:12 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import { useVModel } from '@vueuse/core'
|
|
|
|
|
|
import ActionTypeSelector from '../selectors/ActionTypeSelector.vue'
|
|
|
|
|
|
import DeviceControlConfig from '../configs/DeviceControlConfig.vue'
|
|
|
|
|
|
import AlertConfig from '../configs/AlertConfig.vue'
|
2025-08-02 10:31:24 +08:00
|
|
|
|
import { ActionFormData } from '@/api/iot/rule/scene/scene.types'
|
|
|
|
|
|
import { IotRuleSceneActionTypeEnum as ActionTypeEnum } from '@/views/iot/utils/constants'
|
2025-07-17 21:31:12 +08:00
|
|
|
|
|
|
|
|
|
|
/** 执行器配置组件 */
|
|
|
|
|
|
defineOptions({ name: 'ActionSection' })
|
|
|
|
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
|
|
actions: ActionFormData[]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
interface Emits {
|
|
|
|
|
|
(e: 'update:actions', value: ActionFormData[]): void
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const props = defineProps<Props>()
|
|
|
|
|
|
const emit = defineEmits<Emits>()
|
|
|
|
|
|
|
|
|
|
|
|
const actions = useVModel(props, 'actions', emit)
|
|
|
|
|
|
|
2025-07-27 22:44:06 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 创建默认的执行器数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
const createDefaultActionData = (): ActionFormData => {
|
|
|
|
|
|
return {
|
|
|
|
|
|
type: ActionTypeEnum.DEVICE_PROPERTY_SET, // 默认为设备属性设置
|
|
|
|
|
|
productId: undefined,
|
|
|
|
|
|
deviceId: undefined,
|
|
|
|
|
|
params: {},
|
|
|
|
|
|
alertConfigId: undefined
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-17 21:31:12 +08:00
|
|
|
|
// 配置常量
|
|
|
|
|
|
const maxActions = 5
|
|
|
|
|
|
|
|
|
|
|
|
// 执行器类型映射
|
|
|
|
|
|
const actionTypeNames = {
|
|
|
|
|
|
[ActionTypeEnum.DEVICE_PROPERTY_SET]: '属性设置',
|
|
|
|
|
|
[ActionTypeEnum.DEVICE_SERVICE_INVOKE]: '服务调用',
|
|
|
|
|
|
[ActionTypeEnum.ALERT_TRIGGER]: '触发告警',
|
|
|
|
|
|
[ActionTypeEnum.ALERT_RECOVER]: '恢复告警'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const actionTypeTags = {
|
|
|
|
|
|
[ActionTypeEnum.DEVICE_PROPERTY_SET]: 'primary',
|
|
|
|
|
|
[ActionTypeEnum.DEVICE_SERVICE_INVOKE]: 'success',
|
|
|
|
|
|
[ActionTypeEnum.ALERT_TRIGGER]: 'danger',
|
|
|
|
|
|
[ActionTypeEnum.ALERT_RECOVER]: 'warning'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 工具函数
|
|
|
|
|
|
const isDeviceAction = (type: number) => {
|
2025-08-02 10:31:24 +08:00
|
|
|
|
return [ActionTypeEnum.DEVICE_PROPERTY_SET, ActionTypeEnum.DEVICE_SERVICE_INVOKE].includes(
|
|
|
|
|
|
type as any
|
|
|
|
|
|
)
|
2025-07-17 21:31:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const isAlertAction = (type: number) => {
|
2025-08-02 10:31:24 +08:00
|
|
|
|
return [ActionTypeEnum.ALERT_TRIGGER, ActionTypeEnum.ALERT_RECOVER].includes(type as any)
|
2025-07-17 21:31:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const getActionTypeName = (type: number) => {
|
|
|
|
|
|
return actionTypeNames[type] || '未知类型'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const getActionTypeTag = (type: number) => {
|
|
|
|
|
|
return actionTypeTags[type] || 'info'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 事件处理
|
|
|
|
|
|
const addAction = () => {
|
|
|
|
|
|
if (actions.value.length >= maxActions) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-07-18 23:55:51 +08:00
|
|
|
|
|
2025-07-17 21:31:12 +08:00
|
|
|
|
const newAction = createDefaultActionData()
|
|
|
|
|
|
actions.value.push(newAction)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const removeAction = (index: number) => {
|
|
|
|
|
|
actions.value.splice(index, 1)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const updateActionType = (index: number, type: number) => {
|
|
|
|
|
|
actions.value[index].type = type
|
|
|
|
|
|
onActionTypeChange(actions.value[index], type)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const updateAction = (index: number, action: ActionFormData) => {
|
|
|
|
|
|
actions.value[index] = action
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const updateActionAlertConfig = (index: number, alertConfigId?: number) => {
|
|
|
|
|
|
actions.value[index].alertConfigId = alertConfigId
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const onActionTypeChange = (action: ActionFormData, type: number) => {
|
|
|
|
|
|
// 清理不相关的配置
|
|
|
|
|
|
if (isDeviceAction(type)) {
|
|
|
|
|
|
action.alertConfigId = undefined
|
|
|
|
|
|
if (!action.params) {
|
|
|
|
|
|
action.params = {}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if (isAlertAction(type)) {
|
|
|
|
|
|
action.productId = undefined
|
|
|
|
|
|
action.deviceId = undefined
|
|
|
|
|
|
action.params = undefined
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|