【功能完善】IoT: 场景联动

This commit is contained in:
puhui999 2025-03-29 20:27:38 +08:00
parent 4eb7188ecf
commit f46540759f
7 changed files with 250 additions and 238 deletions

View File

@ -58,7 +58,8 @@ interface TenantBaseDO {
// 触发条件参数
interface TriggerConditionParameter {
identifier: string // 标识符(属性、事件、服务)
identifier0: string // 标识符(事件、服务)
identifier: string // 标识符(属性)
operator: string // 操作符
value: string // 比较值
}

View File

@ -117,7 +117,7 @@
<template #default="scope">
<el-radio
v-model="selectedId"
:label="scope.row.id"
:value="scope.row.id"
@change="() => handleRadioChange(scope.row)"
>
&nbsp;

View File

@ -57,7 +57,7 @@
<template #default="scope">
<el-radio
v-model="selectedId"
:label="scope.row.id"
:value="scope.row.id"
@change="() => handleRadioChange(scope.row)"
>
&nbsp;

View File

@ -19,7 +19,7 @@
<el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value"
:label="dict.value"
:value="dict.value"
>
{{ dict.label }}
</el-radio>

View File

@ -1,81 +1,83 @@
<template>
<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="请选择执行类型"
<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>
<div
v-if="actionConfig.type === IotRuleSceneActionTypeEnum.DEVICE_CONTROL"
class="flex items-center mr-60px"
>
<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>
<span class="mr-10px">产品</span>
<el-button type="primary" @click="handleSelectProduct" size="small" plain>
{{ product ? product.name : '选择产品' }}
</el-button>
</div>
<div
v-if="actionConfig.type === IotRuleSceneActionTypeEnum.DEVICE_CONTROL"
class="flex items-center mr-60px"
>
<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>
</div>
<div
<!-- 设备控制执行器 -->
<DeviceControlAction
v-if="actionConfig.type === IotRuleSceneActionTypeEnum.DEVICE_CONTROL"
class="flex items-center mr-60px"
>
<span class="mr-10px">产品</span>
<el-button type="primary" @click="handleSelectProduct" size="small" plain>
{{ product ? product.name : '选择产品' }}
</el-button>
</div>
<div
v-if="actionConfig.type === IotRuleSceneActionTypeEnum.DEVICE_CONTROL"
class="flex items-center mr-60px"
>
<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>
:model-value="actionConfig.deviceControl"
:product-id="product?.id"
:product-key="product?.productKey"
@update:model-value="(val) => (actionConfig.deviceControl = val)"
/>
<!-- 告警执行器 -->
<AlertAction
v-else-if="actionConfig.type === IotRuleSceneActionTypeEnum.ALERT"
:model-value="actionConfig.alert"
@update:model-value="(val) => (actionConfig.alert = val)"
/>
<!-- 数据桥接执行器 -->
<DataBridgeAction
v-else-if="actionConfig.type === IotRuleSceneActionTypeEnum.DATA_BRIDGE"
:model-value="actionConfig.dataBridgeId"
@update:model-value="(val) => (actionConfig.dataBridgeId = val)"
/>
</div>
<!-- 设备控制执行器 -->
<DeviceControlAction
v-if="actionConfig.type === IotRuleSceneActionTypeEnum.DEVICE_CONTROL"
:model-value="actionConfig.deviceControl"
<!-- 产品设备的选择 -->
<ProductTableSelect ref="productTableSelectRef" @success="handleProductSelect" />
<DeviceTableSelect
ref="deviceTableSelectRef"
multiple
:product-id="product?.id"
:product-key="product?.productKey"
@update:model-value="(val) => (actionConfig.deviceControl = val)"
/>
<!-- 告警执行器 -->
<AlertAction
v-else-if="actionConfig.type === IotRuleSceneActionTypeEnum.ALERT"
:model-value="actionConfig.alert"
@update:model-value="(val) => (actionConfig.alert = val)"
/>
<!-- 数据桥接执行器 -->
<DataBridgeAction
v-else-if="actionConfig.type === IotRuleSceneActionTypeEnum.DATA_BRIDGE"
:model-value="actionConfig.dataBridgeId"
@update:model-value="(val) => (actionConfig.dataBridgeId = val)"
@success="handleDeviceSelect"
/>
</div>
<!-- 产品设备的选择 -->
<ProductTableSelect ref="productTableSelectRef" @success="handleProductSelect" />
<DeviceTableSelect
ref="deviceTableSelectRef"
multiple
:product-id="product?.id"
@success="handleDeviceSelect"
/>
</template>
<script setup lang="ts">

View File

@ -1,140 +1,143 @@
<template>
<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="triggerConfig.type"
class="!w-240px"
clearable
placeholder="请选择触发条件"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.IOT_RULE_SCENE_TRIGGER_TYPE_ENUM)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</div>
<div
v-if="triggerConfig.type === IotRuleSceneTriggerTypeEnum.DEVICE"
class="flex items-center mr-60px"
>
<span class="mr-10px">产品</span>
<el-button type="primary" @click="productTableSelectRef?.open()" size="small" plain>
{{ product ? product.name : '选择产品' }}
</el-button>
</div>
<div
v-if="triggerConfig.type === IotRuleSceneTriggerTypeEnum.DEVICE"
class="flex items-center mr-60px"
>
<span class="mr-10px">设备</span>
<el-button type="primary" @click="openDeviceSelect" size="small" plain>
{{ isEmpty(deviceList) ? '选择设备' : triggerConfig.deviceNames.join(',') }}
</el-button>
</div>
<!-- 删除触发器 -->
<div class="absolute top-auto right-16px bottom-auto">
<el-tooltip content="删除触发器" placement="top">
<slot></slot>
</el-tooltip>
</div>
</div>
<!-- 设备触发器条件 -->
<template v-if="triggerConfig.type === IotRuleSceneTriggerTypeEnum.DEVICE">
<div
class="bg-[#dbe5f6] flex p-10px"
v-for="(condition, index) in triggerConfig.conditions"
:key="index"
>
<div class="flex flex-col items-center justify-center mr-10px h-a">
<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="condition.type"
@change="condition.parameters = []"
class="!w-160px"
v-model="triggerConfig.type"
class="!w-240px"
clearable
placeholder=""
placeholder="请选择触发条件"
>
<el-option label="属性" :value="IotDeviceMessageTypeEnum.PROPERTY" />
<el-option label="服务" :value="IotDeviceMessageTypeEnum.SERVICE" />
<el-option label="事件" :value="IotDeviceMessageTypeEnum.EVENT" />
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.IOT_RULE_SCENE_TRIGGER_TYPE_ENUM)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</div>
<div class="">
<DeviceListenerCondition
v-for="(parameter, index2) in condition.parameters"
:key="index2"
:model-value="parameter"
:thingModels="thingModels(condition)"
@update:model-value="(val) => (condition.parameters[index2] = val)"
class="mb-10px last:mb-0"
>
<el-tooltip content="删除参数" placement="top">
<div
v-if="triggerConfig.type === IotRuleSceneTriggerTypeEnum.DEVICE"
class="flex items-center mr-60px"
>
<span class="mr-10px">产品</span>
<el-button type="primary" @click="productTableSelectRef?.open()" size="small" plain>
{{ product ? product.name : '选择产品' }}
</el-button>
</div>
<div
v-if="triggerConfig.type === IotRuleSceneTriggerTypeEnum.DEVICE"
class="flex items-center mr-60px"
>
<span class="mr-10px">设备</span>
<el-button type="primary" @click="openDeviceSelect" size="small" plain>
{{ isEmpty(deviceList) ? '选择设备' : triggerConfig.deviceNames.join(',') }}
</el-button>
</div>
<!-- 删除触发器 -->
<div class="absolute top-auto right-16px bottom-auto">
<el-tooltip content="删除触发器" placement="top">
<slot></slot>
</el-tooltip>
</div>
</div>
<!-- 设备触发器条件 -->
<template v-if="triggerConfig.type === IotRuleSceneTriggerTypeEnum.DEVICE">
<div
class="bg-[#dbe5f6] flex p-10px"
v-for="(condition, index) in triggerConfig.conditions"
:key="index"
>
<div class="flex flex-col items-center justify-center mr-10px h-a">
<el-select
v-model="condition.type"
@change="condition.parameters = []"
class="!w-160px"
clearable
placeholder=""
>
<el-option label="属性" :value="IotDeviceMessageTypeEnum.PROPERTY" />
<el-option label="服务" :value="IotDeviceMessageTypeEnum.SERVICE" />
<el-option label="事件" :value="IotDeviceMessageTypeEnum.EVENT" />
</el-select>
</div>
<div class="w-70%">
<DeviceListenerCondition
v-for="(parameter, index2) in condition.parameters"
:key="index2"
:model-value="parameter"
:condition-type="condition.type"
:thingModels="thingModels(condition)"
@update:model-value="(val) => (condition.parameters[index2] = val)"
class="mb-10px last:mb-0"
>
<el-tooltip content="删除参数" placement="top">
<el-button
type="danger"
circle
size="small"
@click="removeConditionParameter(condition.parameters, index2)"
>
<Icon icon="ep:delete" />
</el-button>
</el-tooltip>
</DeviceListenerCondition>
</div>
<!-- 添加参数 -->
<div class="flex flex-1 flex-col items-center justify-center w-60px h-a">
<el-tooltip content="添加参数" placement="top">
<el-button
type="danger"
type="primary"
circle
size="small"
@click="removeConditionParameter(condition.parameters, index2)"
@click="addConditionParameter(condition.parameters)"
>
<Icon icon="ep:plus" />
</el-button>
</el-tooltip>
</div>
<!-- 删除条件 -->
<div
class="device-listener-condition flex flex-1 flex-col items-center justify-center w-a h-a"
>
<el-tooltip content="删除条件" placement="top">
<el-button type="danger" size="small" @click="removeCondition(index)">
<Icon icon="ep:delete" />
</el-button>
</el-tooltip>
</DeviceListenerCondition>
</div>
<!-- 添加参数 -->
<div class="flex flex-1 flex-col items-center justify-center w-60px h-a">
<el-tooltip content="添加参数" placement="top">
<el-button
type="primary"
circle
size="small"
@click="addConditionParameter(condition.parameters)"
>
<Icon icon="ep:plus" />
</el-button>
</el-tooltip>
</div>
<!-- 删除条件 -->
<div
class="device-listener-condition flex flex-1 flex-col items-center justify-center w-a h-a"
>
<el-tooltip content="删除条件" placement="top">
<el-button type="danger" size="small" @click="removeCondition(index)">
<Icon icon="ep:delete" />
</el-button>
</el-tooltip>
</div>
</div>
</template>
<!-- 定时触发 -->
<div
v-if="triggerConfig.type === IotRuleSceneTriggerTypeEnum.TIMER"
class="bg-[#dbe5f6] flex items-center justify-between p-10px"
>
<span class="w-120px">CRON 表达式</span>
<crontab v-model="triggerConfig.cronExpression" />
</div>
</template>
<!-- 定时触发 -->
<div
v-if="triggerConfig.type === IotRuleSceneTriggerTypeEnum.TIMER"
class="bg-[#dbe5f6] flex items-center justify-between p-10px"
>
<span class="w-120px">CRON 表达式</span>
<crontab v-model="triggerConfig.cronExpression" />
<!-- 设备触发才可以设置多个触发条件 -->
<el-text
v-if="triggerConfig.type === IotRuleSceneTriggerTypeEnum.DEVICE"
class="ml-10px!"
type="primary"
@click="addCondition"
>
添加触发条件
</el-text>
</div>
<!-- 设备触发才可以设置多个触发条件 -->
<el-text
v-if="triggerConfig.type === IotRuleSceneTriggerTypeEnum.DEVICE"
class="ml-10px!"
type="primary"
@click="addCondition"
>
添加触发条件
</el-text>
</div>
<!-- 产品设备的选择 -->
<ProductTableSelect ref="productTableSelectRef" @success="handleProductSelect" />
<DeviceTableSelect
ref="deviceTableSelectRef"
multiple
:product-id="product?.id"
@success="handleDeviceSelect"
/>
<!-- 产品设备的选择 -->
<ProductTableSelect ref="productTableSelectRef" @success="handleProductSelect" />
<DeviceTableSelect
ref="deviceTableSelectRef"
multiple
:product-id="product?.id"
@success="handleDeviceSelect"
/>
</div>
</template>
<script setup lang="ts">
@ -272,12 +275,11 @@ const thingModels = computed(() => (condition: TriggerCondition) => {
}
switch (condition.type) {
case IotDeviceMessageTypeEnum.PROPERTY:
return thingModelTSL.value.properties
// TODO puhui999:
return thingModelTSL.value?.properties || []
case IotDeviceMessageTypeEnum.SERVICE:
return thingModelTSL.value.services
return thingModelTSL.value?.services || []
case IotDeviceMessageTypeEnum.EVENT:
return thingModelTSL.value.events
return thingModelTSL.value?.events || []
}
return []
})

View File

@ -1,10 +1,14 @@
<template>
<div class="device-listener-condition">
<div class="flex items-center w-1/1">
<!-- 选择服务 -->
<el-select
v-model="conditionParameter.identifier"
class="!w-240px mr-10px"
v-if="
[IotDeviceMessageTypeEnum.SERVICE, IotDeviceMessageTypeEnum.EVENT].includes(conditionType)
"
v-model="conditionParameter.identifier0"
class="!w-150px mr-10px"
clearable
placeholder="请选择物模型"
placeholder="请选择服务"
>
<el-option
v-for="thingModel in thingModels"
@ -13,23 +17,33 @@
:value="thingModel.identifier"
/>
</el-select>
<el-select
v-model="conditionParameter.identifier"
class="!w-150px mr-10px"
clearable
placeholder="请选择物模型"
>
<el-option
v-for="thingModel in getThingModels"
:key="thingModel.identifier"
:label="thingModel.name"
:value="thingModel.identifier"
/>
</el-select>
<ConditionSelector
v-model="conditionParameter.operator"
:data-type="getDataType"
class="!w-180px mr-10px"
:data-type="model?.dataType"
class="!w-150px mr-10px"
/>
<!-- TODO puhui999: 输入值范围校验 -->
<el-input
<ThingModelParamInput
v-if="
conditionParameter.operator !==
IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_NULL.value
"
class="!w-200px mr-10px"
v-model="conditionParameter.value"
class="!w-240px mr-10px"
placeholder="请输入值"
>
<template v-if="getUnitName" #append> {{ getUnitName }} </template>
</el-input>
:thing-model="model"
/>
<!-- 按钮插槽 -->
<slot></slot>
</div>
@ -38,43 +52,36 @@
<script setup lang="ts">
import ConditionSelector from './ConditionSelector.vue'
import {
IotDeviceMessageTypeEnum,
IotRuleSceneTriggerConditionParameterOperatorEnum,
TriggerConditionParameter
} from '@/api/iot/rule/scene/scene.types'
import { useVModel } from '@vueuse/core'
import ThingModelParamInput from '@/views/iot/rule/scene/components/ThingModelParamInput.vue'
/** 设备触发条件 */
defineOptions({ name: 'DeviceListenerCondition' })
const props = defineProps<{ modelValue: any; thingModels: any }>()
const props = defineProps<{ modelValue: any; conditionType: any; thingModels: any }>()
const emits = defineEmits(['update:modelValue'])
const conditionParameter = useVModel(props, 'modelValue', emits) as Ref<TriggerConditionParameter>
/** 获得物模型属性类型 */
const getDataType = computed(() => {
const model = props.thingModels?.find(
(item: any) => item.identifier === conditionParameter.value.identifier
)
//
if (model?.dataSpecs) {
return model.dataSpecs.dataType
/** 属性就是 thingModels服务和事件取对应的 outputParams */
const getThingModels = computed(() => {
switch (props.conditionType) {
case IotDeviceMessageTypeEnum.PROPERTY:
return props.thingModels || []
case IotDeviceMessageTypeEnum.SERVICE:
case IotDeviceMessageTypeEnum.EVENT:
return (
props.thingModels.find(
(item: any) => item.identifier === conditionParameter.value.identifier0
)?.outputParams || []
)
}
return ''
})
/** 获得属性单位 */
const getUnitName = computed(() => {
const model = props.thingModels?.find(
(item: any) => item.identifier === conditionParameter.value.identifier
)
//
if (model?.dataSpecs) {
return model.dataSpecs.unitName
}
// TODO puhui999:
//
// if (model?.outputParams) {
// return model.dataSpecs.unitName
// }
return ''
})
</script>
<style scoped lang="scss"></style>
/** 获得物模型属性、类型 */
const model = computed(() =>
getThingModels.value.find((item: any) => item.identifier === conditionParameter.value.identifier)
)
</script>