fix(iot): 完善场景联动配置必填校验

- Vue3 抽取场景联动校验工具,统一触发器、附加条件和执行器校验
- Vben5 antd/ele 同步场景联动提交前兜底校验
- 补充 CRON 表达式、JSON 参数和动态字段必填校验
- 保留 deviceId=0 表示全部设备的业务语义
master
YunaiV 2026-05-31 00:36:40 +08:00
parent 8405a07dd0
commit 2e68890c7d
8 changed files with 515 additions and 394 deletions

View File

@ -39,8 +39,7 @@ import ActionSection from './sections/ActionSection.vue'
import { IotSceneRule } from '@/api/iot/rule/scene'
import { RuleSceneApi } from '@/api/iot/rule/scene'
import { IotRuleSceneTriggerTypeEnum } from '@/views/iot/utils/constants'
import { validateTriggerItem } from './utils/triggerConditionRules'
import { validateActionItem } from './utils/actionRules'
import { validateActionItem, validateTriggerItem } from '@/views/iot/utils/sceneRule'
import type { Trigger, Action } from '@/api/iot/rule/scene'
import { ElMessage } from 'element-plus'
import { CommonStatusEnum } from '@/utils/constants'

View File

@ -1,6 +1,12 @@
<!-- 告警配置组件 -->
<template>
<el-form ref="innerFormRef" :model="formModel" :rules="formRules" label-width="110px" class="w-full">
<el-form
ref="innerFormRef"
:model="formModel"
:rules="formRules"
label-width="110px"
class="w-full"
>
<el-form-item label="告警配置" prop="alertConfigId" required>
<el-select
v-model="localValue"
@ -33,7 +39,7 @@
import type { FormInstance } from 'element-plus'
import { useVModel } from '@vueuse/core'
import { AlertConfigApi } from '@/api/iot/alert/config'
import { buildAlertConfigRules } from '../utils/actionRules'
import { buildAlertConfigRules } from '@/views/iot/utils/sceneRule'
/** 告警配置组件 */
defineOptions({ name: 'AlertConfig' })

View File

@ -166,7 +166,7 @@ import {
getConditionTypeOptions,
IoTDeviceStatusEnum
} from '@/views/iot/utils/constants'
import { buildSubConditionRules } from '../utils/triggerConditionRules'
import { buildSubConditionRules } from '@/views/iot/utils/sceneRule'
/** 单个条件配置组件 */
defineOptions({ name: 'ConditionConfig' })

View File

@ -92,7 +92,7 @@ import {
IoTDataSpecsDataTypeEnum
} from '@/views/iot/utils/constants'
import { ThingModelApi } from '@/api/iot/thingmodel'
import { buildDeviceControlRules } from '../utils/actionRules'
import { buildDeviceControlRules } from '@/views/iot/utils/sceneRule'
/** 设备控制配置组件 */
defineOptions({ name: 'DeviceControlConfig' })

View File

@ -203,7 +203,7 @@ import {
IotRuleSceneTriggerConditionParameterOperatorEnum,
IoTDeviceStatusEnum
} from '@/views/iot/utils/constants'
import { buildMainConditionRules } from '../utils/triggerConditionRules'
import { buildMainConditionRules } from '@/views/iot/utils/sceneRule'
import { useVModel } from '@vueuse/core'
/** 主条件内部配置组件 */

View File

@ -1,110 +0,0 @@
import type { FormItemRule } from 'element-plus'
import type { Action } from '@/api/iot/rule/scene'
import { IotRuleSceneActionTypeEnum } from '@/views/iot/utils/constants'
const requiredRule = (message: string): FormItemRule => ({
required: true,
message,
trigger: ['change', 'blur']
})
/** 判断执行器参数是否为空 */
export const isActionParamsEmpty = (params?: string): boolean => {
if (!params || !String(params).trim()) {
return true
}
try {
const parsed = JSON.parse(String(params))
if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
return Object.keys(parsed).length === 0
}
} catch {
return false
}
return false
}
/** 设备控制执行器表单项校验规则 */
export function buildDeviceControlRules(actionType: number): Record<string, FormItemRule[]> {
const rules: Record<string, FormItemRule[]> = {
productId: [requiredRule('请选择产品')],
deviceId: [requiredRule('请选择设备')],
params: [createParamsRule()]
}
if (actionType === IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE) {
rules.identifier = [requiredRule('请选择服务')]
}
return rules
}
/** 告警配置表单项校验规则 */
export function buildAlertConfigRules(): Record<string, FormItemRule[]> {
return {
alertConfigId: [requiredRule('请选择告警配置')]
}
}
function createParamsRule(): FormItemRule {
return {
validator: (_rule, value, callback) => {
if (isActionParamsEmpty(value)) {
callback(new Error('请填写参数配置'))
return
}
try {
JSON.parse(String(value))
} catch {
callback(new Error('参数格式须为合法 JSON'))
return
}
callback()
},
trigger: ['change', 'blur']
}
}
/**
* UI
* @returns null
*/
export function validateActionItem(action: Action, index: number): string | null {
const prefix = `执行器 ${index + 1}`
if (!action.type) {
return `${prefix}: 执行器类型不能为空`
}
if (
action.type === IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET ||
action.type === IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE
) {
if (!action.productId) {
return `${prefix}: 产品不能为空`
}
if (!action.deviceId) {
return `${prefix}: 设备不能为空`
}
if (action.type === IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE && !action.identifier) {
return `${prefix}: 服务不能为空`
}
if (isActionParamsEmpty(action.params)) {
return `${prefix}: 参数配置不能为空`
}
try {
JSON.parse(String(action.params))
} catch {
return `${prefix}: 参数格式须为合法 JSON`
}
return null
}
if (action.type === IotRuleSceneActionTypeEnum.ALERT_RECOVER) {
if (!action.alertConfigId) {
return `${prefix}: 告警配置不能为空`
}
}
return null
}

View File

@ -1,277 +0,0 @@
import type { FormItemRule } from 'element-plus'
import type { Trigger, TriggerCondition } from '@/api/iot/rule/scene'
import {
IotRuleSceneTriggerTypeEnum,
IotRuleSceneTriggerConditionTypeEnum,
IotRuleSceneTriggerTimeOperatorEnum,
isDeviceTrigger
} from '@/views/iot/utils/constants'
const requiredRule = (message: string): FormItemRule => ({
required: true,
message,
trigger: ['change', 'blur']
})
const isEmpty = (val: unknown) => val === undefined || val === null || val === ''
/** 主条件表单项校验规则(与 MainConditionInnerConfig 展示字段对齐) */
export function buildMainConditionRules(triggerType: number): Record<string, FormItemRule[]> {
const base: Record<string, FormItemRule[]> = {
productId: [requiredRule('请选择产品')],
deviceId: [requiredRule('请选择设备')]
}
if (triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_STATE_UPDATE) {
return {
...base,
operator: [requiredRule('请选择操作符')],
value: [requiredRule('请选择设备状态')]
}
}
if (
triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_PROPERTY_POST ||
triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_EVENT_POST ||
triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_SERVICE_INVOKE
) {
const rules: Record<string, FormItemRule[]> = {
...base,
identifier: [requiredRule('请选择监控项')]
}
const isEventOrService =
triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_EVENT_POST ||
triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_SERVICE_INVOKE
if (!isEventOrService) {
rules.operator = [requiredRule('请选择操作符')]
rules.value = [requiredRule('请填写比较值')]
}
return rules
}
return {}
}
/**
* UI RuleSceneForm
* @returns null
*/
export function validateTriggerItem(trigger: Trigger, index: number): string | null {
if (!trigger.type) {
return `触发器 ${index + 1}: 触发器类型不能为空`
}
if (isDeviceTrigger(trigger.type)) {
if (!trigger.productId) {
return `触发器 ${index + 1}: 产品不能为空`
}
if (!trigger.deviceId) {
return `触发器 ${index + 1}: 设备不能为空`
}
if (trigger.type === IotRuleSceneTriggerTypeEnum.DEVICE_STATE_UPDATE) {
if (!trigger.operator) {
return `触发器 ${index + 1}: 操作符不能为空`
}
if (isEmpty(trigger.value)) {
return `触发器 ${index + 1}: 设备状态不能为空`
}
return null
}
if (!trigger.identifier) {
return `触发器 ${index + 1}: 物模型标识符不能为空`
}
const isEventOrService =
trigger.type === IotRuleSceneTriggerTypeEnum.DEVICE_EVENT_POST ||
trigger.type === IotRuleSceneTriggerTypeEnum.DEVICE_SERVICE_INVOKE
if (!isEventOrService) {
if (!trigger.operator) {
return `触发器 ${index + 1}: 操作符不能为空`
}
if (isEmpty(trigger.value)) {
return `触发器 ${index + 1}: 参数值不能为空`
}
}
return null
}
if (trigger.type === IotRuleSceneTriggerTypeEnum.TIMER) {
if (!trigger.cronExpression) {
return `触发器 ${index + 1}: CRON表达式不能为空`
}
}
const groupError = validateTriggerConditionGroups(trigger.conditionGroups, index)
if (groupError) {
return groupError
}
return null
}
/** 子条件表单项校验规则(与 ConditionConfig 展示字段对齐) */
export function buildSubConditionRules(
conditionType?: number,
getOperator?: () => string
): Record<string, FormItemRule[]> {
const rules: Record<string, FormItemRule[]> = {
type: [requiredRule('请选择条件类型')]
}
if (!conditionType) {
return rules
}
if (
conditionType === IotRuleSceneTriggerConditionTypeEnum.DEVICE_STATUS ||
conditionType === IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY
) {
rules.productId = [requiredRule('请选择产品')]
rules.deviceId = [requiredRule('请选择设备')]
}
if (conditionType === IotRuleSceneTriggerConditionTypeEnum.DEVICE_STATUS) {
rules.operator = [requiredRule('请选择操作符')]
rules.param = [requiredRule('请选择设备状态')]
}
if (conditionType === IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY) {
rules.identifier = [requiredRule('请选择监控项')]
rules.operator = [requiredRule('请选择操作符')]
rules.param = [requiredRule('请填写比较值')]
}
if (conditionType === IotRuleSceneTriggerConditionTypeEnum.CURRENT_TIME) {
rules.operator = [requiredRule('请选择时间条件')]
rules.param = [createCurrentTimeParamRule(getOperator ?? (() => ''))]
}
return rules
}
/** 当前时间条件的 param 校验 */
function createCurrentTimeParamRule(getOperator: () => string): FormItemRule {
return {
validator: (_rule, value, callback) => {
const operator = getOperator()
if (operator === IotRuleSceneTriggerTimeOperatorEnum.TODAY.value) {
callback()
return
}
if (isEmpty(value)) {
callback(new Error('请填写时间值'))
return
}
if (operator === IotRuleSceneTriggerTimeOperatorEnum.BETWEEN_TIME.value) {
const parts = String(value).split(',')
if (!parts[0]?.trim() || !parts[1]?.trim()) {
callback(new Error('请填写开始和结束时间'))
return
}
}
callback()
},
trigger: ['change', 'blur']
}
}
/**
*
* @returns null
*/
export function validateTriggerCondition(
condition: TriggerCondition,
path: string
): string | null {
if (!condition.type) {
return `${path}: 条件类型不能为空`
}
if (
condition.type === IotRuleSceneTriggerConditionTypeEnum.DEVICE_STATUS ||
condition.type === IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY
) {
if (!condition.productId) {
return `${path}: 产品不能为空`
}
if (!condition.deviceId) {
return `${path}: 设备不能为空`
}
}
if (condition.type === IotRuleSceneTriggerConditionTypeEnum.DEVICE_STATUS) {
if (!condition.operator) {
return `${path}: 操作符不能为空`
}
if (isEmpty(condition.param)) {
return `${path}: 设备状态不能为空`
}
return null
}
if (condition.type === IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY) {
if (!condition.identifier) {
return `${path}: 监控项不能为空`
}
if (!condition.operator) {
return `${path}: 操作符不能为空`
}
if (isEmpty(condition.param)) {
return `${path}: 比较值不能为空`
}
return null
}
if (condition.type === IotRuleSceneTriggerConditionTypeEnum.CURRENT_TIME) {
if (!condition.operator) {
return `${path}: 时间条件不能为空`
}
if (condition.operator === IotRuleSceneTriggerTimeOperatorEnum.TODAY.value) {
return null
}
if (isEmpty(condition.param)) {
return `${path}: 时间值不能为空`
}
if (condition.operator === IotRuleSceneTriggerTimeOperatorEnum.BETWEEN_TIME.value) {
const parts = String(condition.param).split(',')
if (!parts[0]?.trim() || !parts[1]?.trim()) {
return `${path}: 开始和结束时间不能为空`
}
}
return null
}
return null
}
/** 校验触发器的附加条件组 */
export function validateTriggerConditionGroups(
groups: TriggerCondition[][] | undefined,
triggerIndex: number
): string | null {
if (!groups?.length) {
return null
}
for (let g = 0; g < groups.length; g++) {
const group = groups[g]
if (!group?.length) {
return `触发器 ${triggerIndex + 1} 子条件组 ${g + 1}: 至少需要一个条件`
}
for (let c = 0; c < group.length; c++) {
const error = validateTriggerCondition(
group[c],
`触发器 ${triggerIndex + 1} 子条件组 ${g + 1} 条件 ${c + 1}`
)
if (error) {
return error
}
}
}
return null
}

View File

@ -0,0 +1,503 @@
import type { FormItemRule } from 'element-plus'
import type { Action, Trigger, TriggerCondition } from '@/api/iot/rule/scene'
import { isEmptyVal } from '@/utils/is'
import {
IotRuleSceneActionTypeEnum,
IotRuleSceneTriggerConditionTypeEnum,
IotRuleSceneTriggerTimeOperatorEnum,
IotRuleSceneTriggerTypeEnum,
isDeviceTrigger
} from './constants'
/** 创建 Element Plus 表单必填规则,统一触发方式和错误提示。 */
const requiredRule = (message: string): FormItemRule => ({
required: true,
message,
trigger: ['change', 'blur']
})
/**
*
*
* MainConditionInnerConfig
* -
* -
* -
*
* @param triggerType
* @returns Element Plus rules
*/
export function buildMainConditionRules(triggerType: number): Record<string, FormItemRule[]> {
const base: Record<string, FormItemRule[]> = {
productId: [requiredRule('请选择产品')],
deviceId: [requiredRule('请选择设备')]
}
// 设备状态变化使用固定状态枚举作为比较值。
if (triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_STATE_UPDATE) {
return {
...base,
operator: [requiredRule('请选择操作符')],
value: [requiredRule('请选择设备状态')]
}
}
if (
triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_PROPERTY_POST ||
triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_EVENT_POST ||
triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_SERVICE_INVOKE
) {
const rules: Record<string, FormItemRule[]> = {
...base,
identifier: [requiredRule('请选择监控项')]
}
// 事件上报和服务调用只监听是否发生,不需要额外的操作符和比较值。
const isEventOrService =
triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_EVENT_POST ||
triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_SERVICE_INVOKE
if (!isEventOrService) {
rules.operator = [requiredRule('请选择操作符')]
rules.value = [requiredRule('请填写比较值')]
}
return rules
}
return {}
}
/**
*
*
* RuleSceneForm validate
* UI
*
* @param trigger
* @param index
* @returns null
*/
export function validateTriggerItem(trigger: Trigger, index: number): string | null {
if (!trigger.type) {
return `触发器 ${index + 1}: 触发器类型不能为空`
}
// 设备类触发器都有产品、设备两个基础字段。
if (isDeviceTrigger(trigger.type)) {
if (!trigger.productId) {
return `触发器 ${index + 1}: 产品不能为空`
}
if (!trigger.deviceId) {
return `触发器 ${index + 1}: 设备不能为空`
}
// 设备状态变化不依赖物模型标识符,只校验操作符和状态值。
if (trigger.type === IotRuleSceneTriggerTypeEnum.DEVICE_STATE_UPDATE) {
if (!trigger.operator) {
return `触发器 ${index + 1}: 操作符不能为空`
}
if (isEmptyVal(trigger.value)) {
return `触发器 ${index + 1}: 设备状态不能为空`
}
return null
}
if (!trigger.identifier) {
return `触发器 ${index + 1}: 物模型标识符不能为空`
}
// 属性上报需要比较条件;事件上报、服务调用只需要物模型标识符。
const isEventOrService =
trigger.type === IotRuleSceneTriggerTypeEnum.DEVICE_EVENT_POST ||
trigger.type === IotRuleSceneTriggerTypeEnum.DEVICE_SERVICE_INVOKE
if (!isEventOrService) {
if (!trigger.operator) {
return `触发器 ${index + 1}: 操作符不能为空`
}
if (isEmptyVal(trigger.value)) {
return `触发器 ${index + 1}: 参数值不能为空`
}
}
return null
}
// 定时触发器本身需要 CRON 表达式,附加条件组在下方统一校验。
if (trigger.type === IotRuleSceneTriggerTypeEnum.TIMER) {
if (!trigger.cronExpression) {
return `触发器 ${index + 1}: CRON表达式不能为空`
}
}
const groupError = validateTriggerConditionGroups(trigger.conditionGroups, index)
if (groupError) {
return groupError
}
return null
}
/**
*
*
* ConditionConfig
* -
* -
* -
*
* @param conditionType
* @param getOperator param
* @returns Element Plus rules
*/
export function buildSubConditionRules(
conditionType?: number,
getOperator?: () => string
): Record<string, FormItemRule[]> {
const rules: Record<string, FormItemRule[]> = {
type: [requiredRule('请选择条件类型')]
}
if (!conditionType) {
return rules
}
// 设备状态和设备属性都需要先确定具体产品、设备。
if (
conditionType === IotRuleSceneTriggerConditionTypeEnum.DEVICE_STATUS ||
conditionType === IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY
) {
rules.productId = [requiredRule('请选择产品')]
rules.deviceId = [requiredRule('请选择设备')]
}
// 设备状态使用设备在线状态作为比较值。
if (conditionType === IotRuleSceneTriggerConditionTypeEnum.DEVICE_STATUS) {
rules.operator = [requiredRule('请选择操作符')]
rules.param = [requiredRule('请选择设备状态')]
}
// 设备属性需要选择物模型属性,再填写对应的比较值。
if (conditionType === IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY) {
rules.identifier = [requiredRule('请选择监控项')]
rules.operator = [requiredRule('请选择操作符')]
rules.param = [requiredRule('请填写比较值')]
}
// 当前时间的 param 是否必填取决于具体操作符,例如“今天”不需要额外值。
if (conditionType === IotRuleSceneTriggerConditionTypeEnum.CURRENT_TIME) {
rules.operator = [requiredRule('请选择时间条件')]
rules.param = [createCurrentTimeParamRule(getOperator ?? (() => ''))]
}
return rules
}
/**
* param
*
*
* - param
* - param 使
* - param
*
* @param getOperator
* @returns Element Plus
*/
function createCurrentTimeParamRule(getOperator: () => string): FormItemRule {
return {
validator: (_rule, value, callback) => {
const operator = getOperator()
// “今天”没有附加输入项,直接通过。
if (operator === IotRuleSceneTriggerTimeOperatorEnum.TODAY.value) {
callback()
return
}
if (isEmptyVal(value)) {
callback(new Error('请填写时间值'))
return
}
// 时间区间需要同时存在开始和结束时间。
if (operator === IotRuleSceneTriggerTimeOperatorEnum.BETWEEN_TIME.value) {
const parts = String(value).split(',')
if (!parts[0]?.trim() || !parts[1]?.trim()) {
callback(new Error('请填写开始和结束时间'))
return
}
}
callback()
},
trigger: ['change', 'blur']
}
}
/**
*
*
* path便
*
*
* @param condition
* @param path
* @returns null
*/
export function validateTriggerCondition(
condition: TriggerCondition,
path: string
): string | null {
if (!condition.type) {
return `${path}: 条件类型不能为空`
}
// 设备状态和设备属性都必须先选择产品、设备。
if (
condition.type === IotRuleSceneTriggerConditionTypeEnum.DEVICE_STATUS ||
condition.type === IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY
) {
if (!condition.productId) {
return `${path}: 产品不能为空`
}
if (!condition.deviceId) {
return `${path}: 设备不能为空`
}
}
// 设备状态只校验操作符和状态枚举值。
if (condition.type === IotRuleSceneTriggerConditionTypeEnum.DEVICE_STATUS) {
if (!condition.operator) {
return `${path}: 操作符不能为空`
}
if (isEmptyVal(condition.param)) {
return `${path}: 设备状态不能为空`
}
return null
}
// 设备属性需要校验物模型标识符、操作符和比较值。
if (condition.type === IotRuleSceneTriggerConditionTypeEnum.DEVICE_PROPERTY) {
if (!condition.identifier) {
return `${path}: 监控项不能为空`
}
if (!condition.operator) {
return `${path}: 操作符不能为空`
}
if (isEmptyVal(condition.param)) {
return `${path}: 比较值不能为空`
}
return null
}
// 当前时间按操作符动态判断 param 是否需要填写。
if (condition.type === IotRuleSceneTriggerConditionTypeEnum.CURRENT_TIME) {
if (!condition.operator) {
return `${path}: 时间条件不能为空`
}
if (condition.operator === IotRuleSceneTriggerTimeOperatorEnum.TODAY.value) {
return null
}
if (isEmptyVal(condition.param)) {
return `${path}: 时间值不能为空`
}
if (condition.operator === IotRuleSceneTriggerTimeOperatorEnum.BETWEEN_TIME.value) {
const parts = String(condition.param).split(',')
if (!parts[0]?.trim() || !parts[1]?.trim()) {
return `${path}: 开始和结束时间不能为空`
}
}
return null
}
return null
}
/**
*
*
* OR AND
*
*
* @param groups
* @param triggerIndex
* @returns null
*/
export function validateTriggerConditionGroups(
groups: TriggerCondition[][] | undefined,
triggerIndex: number
): string | null {
if (!groups?.length) {
return null
}
for (let g = 0; g < groups.length; g++) {
const group = groups[g]
// 空条件组没有实际过滤条件,提交后语义不明确,需要拦截。
if (!group?.length) {
return `触发器 ${triggerIndex + 1} 子条件组 ${g + 1}: 至少需要一个条件`
}
for (let c = 0; c < group.length; c++) {
const error = validateTriggerCondition(
group[c],
`触发器 ${triggerIndex + 1} 子条件组 ${g + 1} 条件 ${c + 1}`
)
if (error) {
return error
}
}
}
return null
}
/**
*
*
* params JSON
* -
* - `{}`
* - JSON
* - JSON JSON
*
* @param params JSON
* @returns
*/
export const isActionParamsEmpty = (params?: string): boolean => {
if (!params || !String(params).trim()) {
return true
}
try {
const parsed = JSON.parse(String(params))
if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
return Object.keys(parsed).length === 0
}
} catch {
return false
}
return false
}
/**
*
*
*
*
*
* @param actionType
* @returns Element Plus rules
*/
export function buildDeviceControlRules(actionType: number): Record<string, FormItemRule[]> {
const rules: Record<string, FormItemRule[]> = {
productId: [requiredRule('请选择产品')],
deviceId: [requiredRule('请选择设备')],
params: [createParamsRule()]
}
// 服务调用需要额外选择物模型服务。
if (actionType === IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE) {
rules.identifier = [requiredRule('请选择服务')]
}
return rules
}
/**
*
*
*
*
* @returns Element Plus rules
*/
export function buildAlertConfigRules(): Record<string, FormItemRule[]> {
return {
alertConfigId: [requiredRule('请选择告警配置')]
}
}
/**
*
*
*
* -
* - JSON
*
* @returns Element Plus
*/
function createParamsRule(): FormItemRule {
return {
validator: (_rule, value, callback) => {
if (isActionParamsEmpty(value)) {
callback(new Error('请填写参数配置'))
return
}
// 这里只校验 JSON 语法,具体参数结构由物模型参数配置组件负责生成。
try {
JSON.parse(String(value))
} catch {
callback(new Error('参数格式须为合法 JSON'))
return
}
callback()
},
trigger: ['change', 'blur']
}
}
/**
*
*
* RuleSceneForm validate
* UI
*
* @param action
* @param index
* @returns null
*/
export function validateActionItem(action: Action, index: number): string | null {
const prefix = `执行器 ${index + 1}`
if (!action.type) {
return `${prefix}: 执行器类型不能为空`
}
// 设备属性设置和设备服务调用都需要指定设备,并填写物模型参数。
if (
action.type === IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET ||
action.type === IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE
) {
if (!action.productId) {
return `${prefix}: 产品不能为空`
}
if (!action.deviceId) {
return `${prefix}: 设备不能为空`
}
if (action.type === IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE && !action.identifier) {
return `${prefix}: 服务不能为空`
}
if (isActionParamsEmpty(action.params)) {
return `${prefix}: 参数配置不能为空`
}
try {
JSON.parse(String(action.params))
} catch {
return `${prefix}: 参数格式须为合法 JSON`
}
return null
}
// 告警恢复执行器需要绑定具体告警配置。
if (action.type === IotRuleSceneActionTypeEnum.ALERT_RECOVER) {
if (!action.alertConfigId) {
return `${prefix}: 告警配置不能为空`
}
}
return null
}