【功能新增】IoT: 规则场景监听器相关组件

This commit is contained in:
puhui999
2025-03-20 18:02:58 +08:00
parent 477b2439c5
commit 14ffb6483f
7 changed files with 843 additions and 78 deletions

View File

@ -2,36 +2,36 @@
* 场景规则触发器配置
*/
export interface IotRuleSceneTriggerConfig {
/**
/**
* 触发类型
* - 1: 设备触发
* - 2: 定时触发
*/
type: number;
type: number
/** 产品标识 */
productKey?: string;
productKey?: string
/** 设备名称数组 */
deviceNames?: string[];
deviceNames?: string[]
/** 触发条件数组。条件之间是"或"的关系 */
conditions?: IotRuleSceneTriggerCondition[];
conditions?: IotRuleSceneTriggerCondition[]
/** CRON 表达式。当 type = 2 时必填 */
cronExpression?: string;
cronExpression?: string
}
/**
* 触发条件
*/
export interface IotRuleSceneTriggerCondition {
/**
/**
* 消息类型
* - property: 属性上报
* - event: 事件上报
*/
type: string;
type: string
/** 消息标识符 */
identifier: string;
identifier: string
/** 参数数组。参数之间是"或"的关系 */
parameters: IotRuleSceneTriggerConditionParameter[];
parameters: IotRuleSceneTriggerConditionParameter[]
}
/**
@ -39,39 +39,32 @@ export interface IotRuleSceneTriggerCondition {
*/
export interface IotRuleSceneTriggerConditionParameter {
/** 标识符(属性、事件、服务) */
identifier: string;
identifier: string
/**
* 操作符
* - eq: 等于
* - gt: 大于
* - gte: 大于等于
* - lt: 小于
* - lte: 小于等于
* - between: 范围
* - in: 在列表中
*/
operator: string;
/**
operator: string
/**
* 比较值
* 如果有多个值,则使用 "," 分隔,类似 "1,2,3"
*/
value: string;
value: string
}
/**
* 执行器配置
*/
export interface IotRuleSceneActionConfig {
/**
/**
* 执行类型
* - 1: 设备控制
* - 2: 数据桥接
*/
type: number;
type: number
/** 设备控制配置。当 type = 1 时必填 */
deviceControl?: IotRuleSceneActionDeviceControl;
deviceControl?: IotRuleSceneActionDeviceControl
/** 数据桥接编号。当 type = 2 时必填 */
dataBridgeId?: number;
dataBridgeId?: number
}
/**
@ -79,23 +72,23 @@ export interface IotRuleSceneActionConfig {
*/
export interface IotRuleSceneActionDeviceControl {
/** 产品标识 */
productKey: string;
productKey: string
/** 设备名称数组 */
deviceNames: string[];
/**
deviceNames: string[]
/**
* 消息类型
* - property: 属性
* - service: 服务
*/
type: string;
/**
type: string
/**
* 消息标识符
* - property_set: 属性设置
* - service_invoke: 服务调用
*/
identifier: string;
identifier: string
/** 具体数据 */
data: Record<string, any>;
data: Record<string, any>
}
/**
@ -103,17 +96,17 @@ export interface IotRuleSceneActionDeviceControl {
*/
export interface IotRuleSceneSaveReqVO {
/** 场景规则编号 */
id?: number;
id?: number
/** 场景规则名称 */
name: string;
name: string
/** 场景规则状态0=禁用 1=启用) */
status: number;
status: number
/** 触发器配置 */
triggerConfig: IotRuleSceneTriggerConfig;
triggerConfig: IotRuleSceneTriggerConfig
/** 执行动作配置数组 */
actionConfigs: IotRuleSceneActionConfig[];
actionConfigs: IotRuleSceneActionConfig[]
/** 备注 */
remark?: string;
remark?: string
}
/**
@ -121,19 +114,19 @@ export interface IotRuleSceneSaveReqVO {
*/
export interface IotRuleSceneRespVO {
/** 场景规则编号 */
id: number;
id: number
/** 场景规则名称 */
name: string;
name: string
/** 场景规则状态0=禁用 1=启用) */
status: number;
status: number
/** 触发器配置 */
triggerConfig: IotRuleSceneTriggerConfig;
triggerConfig: IotRuleSceneTriggerConfig
/** 执行动作配置数组 */
actionConfigs: IotRuleSceneActionConfig[];
actionConfigs: IotRuleSceneActionConfig[]
/** 备注 */
remark?: string;
remark?: string
/** 创建时间 */
createTime: Date;
createTime: Date
}
/**
@ -141,9 +134,9 @@ export interface IotRuleSceneRespVO {
*/
export interface IotRuleScenePageItemRespVO extends IotRuleSceneRespVO {
/** 触发次数 */
triggerCount: number;
triggerCount: number
/** 最后触发时间 */
lastTriggerTime?: Date;
lastTriggerTime?: Date
}
/**
@ -151,15 +144,15 @@ export interface IotRuleScenePageItemRespVO extends IotRuleSceneRespVO {
*/
export interface IotRuleScenePageReqVO {
/** 场景规则名称 */
name?: string;
name?: string
/** 场景规则状态0=禁用 1=启用) */
status?: number;
status?: number
/** 创建时间 */
createTime?: [Date, Date];
createTime?: [Date, Date]
/** 页码 */
pageNo?: number;
pageNo?: number
/** 每页条数 */
pageSize?: number;
pageSize?: number
}
/**
@ -226,4 +219,4 @@ export enum IotRuleSceneTriggerConditionParameterOperatorEnum {
BETWEEN = 'between',
/** 在列表中 */
IN = 'in'
}
}

View File

@ -245,5 +245,7 @@ export enum DICT_TYPE {
IOT_PLUGIN_STATUS = 'iot_plugin_status', // IOT 插件状态
IOT_PLUGIN_TYPE = 'iot_plugin_type', // IOT 插件类型
IOT_DATA_BRIDGE_DIRECTION_ENUM = 'iot_data_bridge_direction_enum', // 桥梁方向
IOT_DATA_BRIDGE_TYPE_ENUM = 'iot_data_bridge_type_enum' // 桥梁类型
IOT_DATA_BRIDGE_TYPE_ENUM = 'iot_data_bridge_type_enum', // 桥梁类型
IOT_DEVICE_MESSAGE_TYPE_ENUM = 'iot_device_message_type_enum', // IoT 设备消息类型枚举
IOT_RULE_SCENE_TRIGGER_TYPE_ENUM = 'iot_rule_scene_trigger_type_enum' // IoT 场景流转的触发类型枚举
}

View File

@ -1,5 +1,5 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<Dialog :title="dialogTitle" v-model="dialogVisible" width="70%">
<el-form
ref="formRef"
:model="formData"
@ -7,29 +7,41 @@
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="场景名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入场景名称" />
</el-form-item>
<el-form-item label="场景描述" prop="description">
<el-input v-model="formData.description" type="textarea" placeholder="请输入场景描述" />
</el-form-item>
<el-form-item label="场景状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="触发器数组" prop="triggers">
<el-input v-model="formData.triggers" placeholder="请输入触发器数组" />
</el-form-item>
<el-form-item label="执行器数组" prop="actions">
<el-input v-model="formData.actions" placeholder="请输入执行器数组" />
</el-form-item>
<el-row>
<el-col :span="12">
<el-form-item label="场景名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入场景名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="场景状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="场景描述" prop="description">
<el-input v-model="formData.description" type="textarea" placeholder="请输入场景描述" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-divider content-position="left">触发器配置</el-divider>
<device-listener v-model="formData.triggers" />
</el-col>
<el-col :span="24">
<el-divider content-position="left">执行动作配置</el-divider>
<el-form-item label="执行器数组" prop="actionConfigs">
<el-input v-model="formData.actions" placeholder="请输入执行器数组" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
@ -40,6 +52,7 @@
<script setup lang="ts">
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { RuleSceneApi, RuleSceneVO } from '@/api/iot/rule/scene'
import DeviceListener from './components/DeviceListener.vue'
/** IoT 规则场景(场景联动) 表单 */
defineOptions({ name: 'RuleSceneForm' })

View File

@ -0,0 +1,241 @@
<template>
<div class="condition-selector">
<el-select
v-model="selectedOperator"
:placeholder="placeholder || '请选择操作符'"
class="condition-select"
@change="handleOperatorChange"
>
<el-option
v-for="item in operatorOptions"
:key="item.value"
:label="item.label"
:value="item.value"
>
<div class="operator-option">
<span>{{ item.label }}</span>
<span class="operator-symbol">{{ item.symbol }}</span>
</div>
</el-option>
</el-select>
<!-- 普通输入值 -->
<template v-if="!isRangeOperator && !isListOperator">
<el-input
v-model="inputValue"
:placeholder="valuePlaceholder || '请输入值'"
class="value-input"
@change="handleValueChange"
/>
</template>
<!-- 范围值输入 -->
<template v-if="isRangeOperator">
<div class="range-input">
<el-input
v-model="rangeValue.min"
placeholder="最小值"
class="range-input-item"
@change="handleRangeValueChange"
/>
<span class="range-separator"></span>
<el-input
v-model="rangeValue.max"
placeholder="最大值"
class="range-input-item"
@change="handleRangeValueChange"
/>
</div>
</template>
<!-- 列表值输入 -->
<template v-if="isListOperator">
<el-select
v-model="listValue"
:placeholder="valuePlaceholder || '请选择值'"
multiple
filterable
allow-create
class="list-select"
@change="handleListValueChange"
>
<el-option
v-for="item in listOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
</div>
</template>
<script setup lang="ts">
import { ref, computed, watch, defineEmits, defineProps } from 'vue'
import { IotRuleSceneTriggerConditionParameterOperatorEnum } from '@/api/iot/rule/scene/scene.types'
interface ConditionValue {
operator: IotRuleSceneTriggerConditionParameterOperatorEnum
value: string
}
// 定义组件属性
const props = defineProps({
modelValue: {
type: Object as () => ConditionValue,
required: true
},
placeholder: {
type: String,
default: ''
},
valuePlaceholder: {
type: String,
default: ''
},
listOptions: {
type: Array as () => { label: string, value: string | number }[],
default: () => []
}
})
// 定义事件
const emit = defineEmits(['update:modelValue'])
// 操作符选项
const operatorOptions = [
{ label: '等于', value: IotRuleSceneTriggerConditionParameterOperatorEnum.EQ, symbol: '=' },
{ label: '不等于', value: IotRuleSceneTriggerConditionParameterOperatorEnum.NE, symbol: '≠' },
{ label: '大于', value: IotRuleSceneTriggerConditionParameterOperatorEnum.GT, symbol: '>' },
{ label: '小于', value: IotRuleSceneTriggerConditionParameterOperatorEnum.LT, symbol: '<' },
{ label: '大于等于', value: IotRuleSceneTriggerConditionParameterOperatorEnum.GTE, symbol: '≥' },
{ label: '小于等于', value: IotRuleSceneTriggerConditionParameterOperatorEnum.LTE, symbol: '≤' },
{ label: '在...之间', value: IotRuleSceneTriggerConditionParameterOperatorEnum.BETWEEN, symbol: '↔' },
{ label: '不在...之间', value: IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_BETWEEN, symbol: '⟷' },
{ label: '在列表中', value: IotRuleSceneTriggerConditionParameterOperatorEnum.IN, symbol: '∈' },
{ label: '不在列表中', value: IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_IN, symbol: '∉' }
]
// 当前选中的操作符
const selectedOperator = ref(props.modelValue.operator || '')
// 输入值
const inputValue = ref(props.modelValue.value || '')
// 范围值
const rangeValue = ref({
min: '',
max: ''
})
// 列表值
const listValue = ref([])
// 计算属性:是否为范围操作符
const isRangeOperator = computed(() => {
return ['between', 'notBetween'].includes(selectedOperator.value)
})
// 计算属性:是否为列表操作符
const isListOperator = computed(() => {
return ['in', 'notIn'].includes(selectedOperator.value)
})
// 处理操作符变更
const handleOperatorChange = () => {
updateModelValue()
}
// 处理值变更
const handleValueChange = () => {
updateModelValue()
}
// 处理范围值变更
const handleRangeValueChange = () => {
updateModelValue()
}
// 处理列表值变更
const handleListValueChange = () => {
updateModelValue()
}
// 更新组件值
const updateModelValue = () => {
const value = isRangeOperator.value
? `${rangeValue.value.min},${rangeValue.value.max}`
: isListOperator.value
? listValue.value.join(',')
: inputValue.value
emit('update:modelValue', {
operator: selectedOperator.value,
value
})
}
// 监听 modelValue 变化
watch(() => props.modelValue, (newVal) => {
if (newVal) {
selectedOperator.value = newVal.operator || ''
if (isRangeOperator.value && newVal.value) {
const [min, max] = newVal.value.split(',')
rangeValue.value = { min, max }
} else if (isListOperator.value && newVal.value) {
listValue.value = newVal.value.split(',')
} else {
inputValue.value = newVal.value || ''
}
}
}, { deep: true })
</script>
<style scoped>
.condition-selector {
display: flex;
gap: 8px;
align-items: flex-start;
width: 100%;
}
.condition-select {
width: 120px;
}
.value-input {
flex: 1;
}
.operator-option {
display: flex;
justify-content: space-between;
align-items: center;
}
.operator-symbol {
font-size: 14px;
color: var(--el-text-color-secondary);
}
.range-input {
display: flex;
gap: 8px;
align-items: center;
flex: 1;
}
.range-input-item {
flex: 1;
}
.range-separator {
padding: 0 4px;
color: var(--el-text-color-regular);
}
.list-select {
flex: 1;
}
</style>

View File

@ -0,0 +1,87 @@
<template>
<div class="device-listener m-10px">
<div class="device-listener-header h-50px flex items-center px-10px">
<div class="flex items-center mr-60px">
<span class="mr-10px">触发条件</span>
<el-select v-model="triggerType" 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 class="flex items-center mr-60px">
<span class="mr-10px">产品</span>
<el-button type="primary">选择产品</el-button>
</div>
<div class="flex items-center mr-60px">
<span class="mr-10px">设备</span>
<el-button type="primary">选择设备</el-button>
</div>
</div>
<div class="device-listener-condition flex p-10px">
<div class="flex flex-col items-center justify-center mr-10px h-a">
<el-select v-model="messageType" class="!w-160px" clearable placeholder="">
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.IOT_DEVICE_MESSAGE_TYPE_ENUM)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</div>
<div class="flex items-center flex-wrap">
<DeviceListenerCondition
v-for="i in 2"
:key="i"
v-model="conditionParameter"
class="mb-10px last:mb-0"
/>
</div>
<div class="flex flex-col items-center justify-center mr-10px h-a">
<!-- 添加规则 -->
<el-button type="primary" circle :icon="Plus" size="small" />
</div>
</div>
<!-- 新增条件按钮 -->
<div class="mt-4">
<el-button type="primary"> 新增监听器 </el-button>
</div>
</div>
</template>
<script setup lang="ts">
import { Plus } from '@element-plus/icons-vue'
import { DICT_TYPE, getIntDictOptions, getStrDictOptions } from '@/utils/dict'
import { ref } from 'vue'
import DeviceListenerCondition from './DeviceListenerCondition.vue'
/** 场景联动之监听器组件 */
defineOptions({ name: 'DeviceListener' })
defineProps<{
modelValue: any[]
}>()
const emit = defineEmits(['update:modelValue'])
// 添加响应式变量
const triggerType = ref()
const messageType = ref('property')
const conditionParameter = ref({})
</script>
<style lang="scss" scoped>
.device-listener {
.device-listener-header {
background-color: #eff3f7;
}
.device-listener-condition {
background-color: #dbe5f6;
}
}
</style>

View File

@ -0,0 +1,50 @@
<template>
<div class="device-listener-condition">
<el-select
v-model="conditionParameter.identifier"
class="!w-240px mr-10px"
clearable
placeholder="请选择物模型"
>
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.IOT_DEVICE_MESSAGE_TYPE_ENUM)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
<el-select
v-model="conditionParameter.operator"
class="!w-240px mr-10px"
clearable
placeholder="请选择条件"
>
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.IOT_DEVICE_MESSAGE_TYPE_ENUM)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
<el-input v-model="conditionParameter.value" class="!w-240px mr-10px" placeholder="请输入值">
<template #append> 单位 </template>
</el-input>
</div>
</template>
<script setup lang="ts">
import { IotRuleSceneTriggerConditionParameter } from '@/api/iot/rule/scene/scene.types'
import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
import { useVModel } from '@vueuse/core'
defineOptions({ name: 'DeviceListenerCondition' })
const props = defineProps<{ modelValue: any }>()
const emits = defineEmits(['update:modelValue'])
const conditionParameter = useVModel(
props,
'modelValue',
emits
) as Ref<IotRuleSceneTriggerConditionParameter>
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,379 @@
<template>
<div class="trigger-conditions">
<div class="conditions-header mb-3">
<el-button type="primary" @click="addCondition" :disabled="!productKey">
<Icon icon="ep:plus" class="mr-5px" /> 添加条件
</el-button>
<div class="conditions-tips" v-if="modelValue && modelValue.length > 0">
多个条件之间为""关系
</div>
</div>
<el-empty v-if="!modelValue || modelValue.length === 0" description="暂无触发条件" />
<div class="conditions-list" v-else>
<div v-for="(condition, index) in modelValue" :key="index" class="condition-item mb-3">
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>条件 {{ index + 1 }}</span>
<el-button type="danger" link @click="removeCondition(index)"> 删除 </el-button>
</div>
</template>
<div class="condition-content">
<el-form label-width="100px" :model="condition">
<el-form-item label="消息类型">
<el-select
v-model="condition.type"
placeholder="请选择消息类型"
@change="handleMessageTypeChange(index)"
>
<el-option label="属性上报" :value="IotDeviceMessageTypeEnum.PROPERTY" />
<el-option label="事件上报" :value="IotDeviceMessageTypeEnum.EVENT" />
</el-select>
</el-form-item>
<el-form-item label="消息标识符">
<el-select
v-model="condition.identifier"
placeholder="请选择消息标识符"
filterable
:loading="thingModelLoading"
@change="handleIdentifierChange(index)"
>
<el-option
v-for="item in getThingModelOptions(condition.type)"
:key="item.identifier"
:label="item.name"
:value="item.identifier"
>
<div class="thing-model-option">
<span>{{ item.name }}</span>
<span class="thing-model-identifier">{{ item.identifier }}</span>
</div>
<div class="thing-model-desc" v-if="item.description">{{
item.description
}}</div>
</el-option>
</el-select>
</el-form-item>
<div class="parameters-area mt-3 mb-2">
<div class="parameters-header">
<div>参数列表多个参数之间为"或"关系</div>
<el-button type="primary" link @click="addParameter(index)"> 添加参数 </el-button>
</div>
<el-empty
v-if="!condition.parameters || condition.parameters.length === 0"
description="暂无参数"
/>
<div class="parameters-list mt-2" v-else>
<div
v-for="(param, pIndex) in condition.parameters"
:key="pIndex"
class="parameter-item mb-2"
>
<el-card shadow="hover">
<div class="parameter-item-header">
<span>参数 {{ pIndex + 1 }}</span>
<el-button type="danger" link @click="removeParameter(index, pIndex)">
删除
</el-button>
</div>
<el-form label-width="90px" :model="param" class="mt-2">
<el-form-item label="标识符">
<el-select
v-model="param.identifier"
placeholder="请选择参数标识符"
filterable
>
<el-option
v-for="item in getParameterOptions(condition)"
:key="item.identifier"
:label="item.name"
:value="item.identifier"
>
<div class="thing-model-option">
<span>{{ item.name }}</span>
<span class="thing-model-identifier">{{ item.identifier }}</span>
</div>
<div class="thing-model-desc" v-if="item.description">{{
item.description
}}</div>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="条件">
<condition-selector
v-model="param.condition"
:placeholder="'请选择条件'"
:value-placeholder="'请输入比较值'"
/>
</el-form-item>
</el-form>
</el-card>
</div>
</div>
</div>
</el-form>
</div>
</el-card>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { defineEmits, defineProps, onMounted, ref, watch } from 'vue'
import {
IotDeviceMessageIdentifierEnum,
IotDeviceMessageTypeEnum,
IotRuleSceneTriggerCondition,
IotRuleSceneTriggerConditionParameter
} from '@/api/iot/rule/scene/scene.types'
import { ThingModelApi, ThingModelData } from '@/api/iot/thingmodel'
import ConditionSelector from './ConditionSelector.vue'
const props = defineProps({
modelValue: {
type: Array as () => IotRuleSceneTriggerCondition[],
required: true
},
productKey: {
type: String,
required: true
}
})
const emit = defineEmits(['update:modelValue'])
// 物模型数据
const thingModelList = ref<ThingModelData[]>([])
const thingModelLoading = ref(false)
// 加载物模型数据
const loadThingModelData = async () => {
if (!props.productKey) return
try {
thingModelLoading.value = true
const result = await ThingModelApi.getThingModelListByProductId({
productKey: props.productKey
})
thingModelList.value = result || []
} catch (error) {
console.error('获取物模型数据失败', error)
} finally {
thingModelLoading.value = false
}
}
// 获取物模型选项
const getThingModelOptions = (type: string) => {
if (!thingModelList.value) return []
return thingModelList.value.filter((item) => {
if (type === IotDeviceMessageTypeEnum.PROPERTY) {
return item.property
} else if (type === IotDeviceMessageTypeEnum.EVENT) {
return item.event
}
return false
})
}
// 获取参数选项
const getParameterOptions = (condition: IotRuleSceneTriggerCondition) => {
if (!condition || !condition.identifier) return []
const model = thingModelList.value?.find((item) => item.identifier === condition.identifier)
if (!model) return []
if (condition.type === IotDeviceMessageTypeEnum.PROPERTY) {
return [model] // 属性本身就是参数
} else if (condition.type === IotDeviceMessageTypeEnum.EVENT) {
// TODO: 获取事件的输出参数列表
return []
}
return []
}
// 添加条件
const addCondition = () => {
const newCondition: IotRuleSceneTriggerCondition = {
type: IotDeviceMessageTypeEnum.PROPERTY,
identifier: IotDeviceMessageIdentifierEnum.PROPERTY_REPORT,
parameters: []
}
const newValue = [...(props.modelValue || []), newCondition]
emit('update:modelValue', newValue)
}
// 移除条件
const removeCondition = (index: number) => {
const newValue = [...props.modelValue]
newValue.splice(index, 1)
emit('update:modelValue', newValue)
}
// 消息类型变更
const handleMessageTypeChange = (index: number) => {
const newValue = [...props.modelValue]
// 更新标识符
if (newValue[index].type === IotDeviceMessageTypeEnum.PROPERTY) {
newValue[index].identifier = IotDeviceMessageIdentifierEnum.PROPERTY_REPORT
} else if (newValue[index].type === IotDeviceMessageTypeEnum.EVENT) {
newValue[index].identifier = IotDeviceMessageIdentifierEnum.EVENT_REPORT
}
// 清空参数
newValue[index].parameters = []
emit('update:modelValue', newValue)
}
// 标识符变更
const handleIdentifierChange = (index: number) => {
const newValue = [...props.modelValue]
// 清空参数
newValue[index].parameters = []
emit('update:modelValue', newValue)
}
// 添加参数
const addParameter = (conditionIndex: number) => {
const newValue = [...props.modelValue]
if (!newValue[conditionIndex].parameters) {
newValue[conditionIndex].parameters = []
}
const newParameter: IotRuleSceneTriggerConditionParameter = {
identifier: '',
condition: {
operator: 'eq',
value: ''
}
}
newValue[conditionIndex].parameters.push(newParameter)
emit('update:modelValue', newValue)
}
// 移除参数
const removeParameter = (conditionIndex: number, paramIndex: number) => {
const newValue = [...props.modelValue]
newValue[conditionIndex].parameters.splice(paramIndex, 1)
emit('update:modelValue', newValue)
}
// 监听 productKey 变化
watch(
() => props.productKey,
(newVal) => {
if (!newVal) {
// 清空条件
if (props.modelValue?.length > 0) {
emit('update:modelValue', [])
}
// 清空物模型数据
thingModelList.value = []
} else {
// 加载物模型数据
loadThingModelData()
}
}
)
// 初始化
onMounted(() => {
if (props.productKey) {
loadThingModelData()
}
})
</script>
<style scoped>
.trigger-conditions {
width: 100%;
}
.conditions-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.conditions-tips {
font-size: 12px;
color: #999;
}
.condition-item {
border-radius: 4px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.parameters-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
font-weight: bold;
}
.parameter-item-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.value-tips {
margin-top: 5px;
font-size: 12px;
color: #999;
}
.thing-model-option {
display: flex;
justify-content: space-between;
align-items: center;
}
.thing-model-identifier {
font-size: 12px;
color: #999;
}
.thing-model-desc {
margin-top: 4px;
font-size: 12px;
color: #666;
}
.mb-3 {
margin-bottom: 12px;
}
.mb-2 {
margin-bottom: 8px;
}
.mt-3 {
margin-top: 12px;
}
.mt-2 {
margin-top: 8px;
}
.mr-5px {
margin-right: 5px;
}
</style>