review:【IoT 物联网】场景联动的部分 review
parent
2fb68bc441
commit
a72c297e86
|
|
@ -2,6 +2,7 @@
|
||||||
* IoT 场景联动接口定义
|
* IoT 场景联动接口定义
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// TODO @puhui999:枚举挪到 views/iot/utils/constants.ts 里
|
||||||
// 枚举定义
|
// 枚举定义
|
||||||
const IotRuleSceneTriggerTypeEnum = {
|
const IotRuleSceneTriggerTypeEnum = {
|
||||||
DEVICE_STATE_UPDATE: 1, // 设备上下线变更
|
DEVICE_STATE_UPDATE: 1, // 设备上下线变更
|
||||||
|
|
@ -24,6 +25,7 @@ const IotDeviceMessageTypeEnum = {
|
||||||
EVENT: 'event' // 事件
|
EVENT: 'event' // 事件
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
|
// TODO @puhui999:这个貌似可以不要?
|
||||||
const IotDeviceMessageIdentifierEnum = {
|
const IotDeviceMessageIdentifierEnum = {
|
||||||
PROPERTY_SET: 'set', // 属性设置
|
PROPERTY_SET: 'set', // 属性设置
|
||||||
SERVICE_INVOKE: '${identifier}' // 服务调用
|
SERVICE_INVOKE: '${identifier}' // 服务调用
|
||||||
|
|
@ -44,6 +46,7 @@ const IotRuleSceneTriggerConditionParameterOperatorEnum = {
|
||||||
NOT_NULL: { name: '非空', value: 'not null' } // 非空
|
NOT_NULL: { name: '非空', value: 'not null' } // 非空
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
|
// TODO @puhui999:下面 IotAlertConfigReceiveTypeEnum、DeviceStateEnum 没用到,貌似可以删除下?
|
||||||
const IotAlertConfigReceiveTypeEnum = {
|
const IotAlertConfigReceiveTypeEnum = {
|
||||||
SMS: 1, // 短信
|
SMS: 1, // 短信
|
||||||
MAIL: 2, // 邮箱
|
MAIL: 2, // 邮箱
|
||||||
|
|
@ -57,6 +60,7 @@ const DeviceStateEnum = {
|
||||||
OFFLINE: 2 // 离线
|
OFFLINE: 2 // 离线
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
|
// TODO @puhui999:这个全局已经有啦
|
||||||
// 通用状态枚举
|
// 通用状态枚举
|
||||||
const CommonStatusEnum = {
|
const CommonStatusEnum = {
|
||||||
ENABLE: 0, // 开启
|
ENABLE: 0, // 开启
|
||||||
|
|
@ -64,6 +68,7 @@ const CommonStatusEnum = {
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
// 基础接口
|
// 基础接口
|
||||||
|
// TODO @puhui999:这个貌似可以不要?
|
||||||
interface TenantBaseDO {
|
interface TenantBaseDO {
|
||||||
createTime?: Date // 创建时间
|
createTime?: Date // 创建时间
|
||||||
updateTime?: Date // 更新时间
|
updateTime?: Date // 更新时间
|
||||||
|
|
@ -169,6 +174,7 @@ interface IotRuleScene extends TenantBaseDO {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 工具类型
|
// 工具类型
|
||||||
|
// TODO @puhui999:这些在瞅瞅~
|
||||||
type TriggerType = (typeof IotRuleSceneTriggerTypeEnum)[keyof typeof IotRuleSceneTriggerTypeEnum]
|
type TriggerType = (typeof IotRuleSceneTriggerTypeEnum)[keyof typeof IotRuleSceneTriggerTypeEnum]
|
||||||
type ActionType = (typeof IotRuleSceneActionTypeEnum)[keyof typeof IotRuleSceneActionTypeEnum]
|
type ActionType = (typeof IotRuleSceneActionTypeEnum)[keyof typeof IotRuleSceneActionTypeEnum]
|
||||||
type MessageType = (typeof IotDeviceMessageTypeEnum)[keyof typeof IotDeviceMessageTypeEnum]
|
type MessageType = (typeof IotDeviceMessageTypeEnum)[keyof typeof IotDeviceMessageTypeEnum]
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
// TODO @puhui999:这些后续需要删除哈
|
||||||
# IoT场景联动规则表单设计思路文档
|
# IoT场景联动规则表单设计思路文档
|
||||||
|
|
||||||
## 概述
|
## 概述
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
// TODO @puhui999:这些后续需要删除哈
|
||||||
# IotThingModelTSLRespVO 数据结构文档
|
# IotThingModelTSLRespVO 数据结构文档
|
||||||
|
|
||||||
## 概述
|
## 概述
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
// IoT物模型TSL数据类型定义
|
// IoT物模型TSL数据类型定义
|
||||||
|
|
||||||
|
// TODO @puhui999:看看这些里面,是不是一些已经有了哈?可以复用下~
|
||||||
|
|
||||||
/** 物模型TSL响应数据结构 */
|
/** 物模型TSL响应数据结构 */
|
||||||
export interface IotThingModelTSLRespVO {
|
export interface IotThingModelTSLRespVO {
|
||||||
productId: number
|
productId: number
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@
|
||||||
class="!w-240px"
|
class="!w-240px"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<!-- TODO @puhui999:字典 -->
|
||||||
<el-form-item label="规则状态">
|
<el-form-item label="规则状态">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="queryParams.status"
|
v-model="queryParams.status"
|
||||||
|
|
@ -61,6 +62,7 @@
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<!-- 统计卡片 -->
|
<!-- 统计卡片 -->
|
||||||
|
<!-- TODO @puhui999:这种需要服用的 stats-content、stats-info 的属性,到底 unocss 好,还是现有的 style css 好~ -->
|
||||||
<el-row :gutter="16" class="stats-row">
|
<el-row :gutter="16" class="stats-row">
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<el-card class="stats-card" shadow="hover">
|
<el-card class="stats-card" shadow="hover">
|
||||||
|
|
@ -124,6 +126,7 @@
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<div class="rule-name-cell">
|
<div class="rule-name-cell">
|
||||||
<span class="rule-name">{{ row.name }}</span>
|
<span class="rule-name">{{ row.name }}</span>
|
||||||
|
<!-- TODO @puhui999:字典 -->
|
||||||
<el-tag
|
<el-tag
|
||||||
:type="row.status === 0 ? 'success' : 'danger'"
|
:type="row.status === 0 ? 'success' : 'danger'"
|
||||||
size="small"
|
size="small"
|
||||||
|
|
@ -137,7 +140,6 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column label="触发条件" min-width="250">
|
<el-table-column label="触发条件" min-width="250">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<div class="trigger-summary">
|
<div class="trigger-summary">
|
||||||
|
|
@ -153,7 +155,6 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column label="执行动作" min-width="250">
|
<el-table-column label="执行动作" min-width="250">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<div class="action-summary">
|
<div class="action-summary">
|
||||||
|
|
@ -169,7 +170,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<!-- TODO @puhui999:貌似要新增一个字段? -->
|
||||||
<el-table-column label="最近触发" prop="lastTriggeredTime" width="180">
|
<el-table-column label="最近触发" prop="lastTriggeredTime" width="180">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span v-if="row.lastTriggeredTime">
|
<span v-if="row.lastTriggeredTime">
|
||||||
|
|
@ -178,13 +179,11 @@
|
||||||
<span v-else class="text-gray-400">未触发</span>
|
<span v-else class="text-gray-400">未触发</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column label="创建时间" prop="createTime" width="180">
|
<el-table-column label="创建时间" prop="createTime" width="180">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
{{ formatDate(row.createTime) }}
|
{{ formatDate(row.createTime) }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column label="操作" width="200" fixed="right">
|
<el-table-column label="操作" width="200" fixed="right">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
|
|
@ -242,11 +241,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 表单对话框 -->
|
<!-- 表单对话框 -->
|
||||||
<RuleSceneForm
|
<RuleSceneForm v-model="formVisible" :rule-scene="currentRule" @success="handleFormSuccess" />
|
||||||
v-model="formVisible"
|
|
||||||
:rule-scene="currentRule"
|
|
||||||
@success="handleFormSuccess"
|
|
||||||
/>
|
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -435,6 +430,7 @@ const handleSelectionChange = (selection: IotRuleScene[]) => {
|
||||||
selectedRows.value = selection
|
selectedRows.value = selection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO @puhui999:batch 操作的逻辑,要不和其它 UI 界面保持一致,或者相对一致哈;
|
||||||
const handleBatchEnable = async () => {
|
const handleBatchEnable = async () => {
|
||||||
try {
|
try {
|
||||||
await ElMessageBox.confirm(`确定要启用选中的 ${selectedRows.value.length} 个规则吗?`, '提示', {
|
await ElMessageBox.confirm(`确定要启用选中的 ${selectedRows.value.length} 个规则吗?`, '提示', {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
* IoT 场景联动错误处理和用户反馈工具
|
* IoT 场景联动错误处理和用户反馈工具
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// TODO @puhui999:这个貌似用不到?
|
||||||
|
|
||||||
import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
|
import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
|
||||||
|
|
||||||
// 错误类型枚举
|
// 错误类型枚举
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* IoT 场景联动数据转换工具函数
|
* IoT 场景联动数据转换工具函数
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
IotRuleScene,
|
IotRuleScene,
|
||||||
TriggerConfig,
|
TriggerConfig,
|
||||||
|
|
@ -12,6 +11,8 @@ import {
|
||||||
} from '@/api/iot/rule/scene/scene.types'
|
} from '@/api/iot/rule/scene/scene.types'
|
||||||
import { generateUUID } from '@/utils'
|
import { generateUUID } from '@/utils'
|
||||||
|
|
||||||
|
// TODO @puhui999:这些是不是放到对应的界面,会好一丢丢哈?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建默认的表单数据
|
* 创建默认的表单数据
|
||||||
*/
|
*/
|
||||||
|
|
@ -19,7 +20,7 @@ export function createDefaultFormData(): RuleSceneFormData {
|
||||||
return {
|
return {
|
||||||
name: '',
|
name: '',
|
||||||
description: '',
|
description: '',
|
||||||
status: 0,
|
status: 0, // TODO @puhui999:枚举值
|
||||||
triggers: [],
|
triggers: [],
|
||||||
actions: []
|
actions: []
|
||||||
}
|
}
|
||||||
|
|
@ -30,7 +31,7 @@ export function createDefaultFormData(): RuleSceneFormData {
|
||||||
*/
|
*/
|
||||||
export function createDefaultTriggerData(): TriggerFormData {
|
export function createDefaultTriggerData(): TriggerFormData {
|
||||||
return {
|
return {
|
||||||
type: 2, // 默认为属性上报
|
type: 2, // 默认为属性上报 TODO @puhui999:枚举值
|
||||||
productId: undefined,
|
productId: undefined,
|
||||||
deviceId: undefined,
|
deviceId: undefined,
|
||||||
identifier: undefined,
|
identifier: undefined,
|
||||||
|
|
@ -46,7 +47,7 @@ export function createDefaultTriggerData(): TriggerFormData {
|
||||||
*/
|
*/
|
||||||
export function createDefaultActionData(): ActionFormData {
|
export function createDefaultActionData(): ActionFormData {
|
||||||
return {
|
return {
|
||||||
type: 1, // 默认为属性设置
|
type: 1, // 默认为属性设置 TODO @puhui999:枚举值
|
||||||
productId: undefined,
|
productId: undefined,
|
||||||
deviceId: undefined,
|
deviceId: undefined,
|
||||||
params: {},
|
params: {},
|
||||||
|
|
@ -58,7 +59,8 @@ export function createDefaultActionData(): ActionFormData {
|
||||||
* 将表单数据转换为API请求格式
|
* 将表单数据转换为API请求格式
|
||||||
*/
|
*/
|
||||||
export function transformFormToApi(formData: RuleSceneFormData): IotRuleScene {
|
export function transformFormToApi(formData: RuleSceneFormData): IotRuleScene {
|
||||||
// 这里需要根据实际API结构进行转换
|
// TODO @puhui999:这个关注下
|
||||||
|
// 这里需要根据实际 API 结构进行转换
|
||||||
// 暂时返回基本结构
|
// 暂时返回基本结构
|
||||||
return {
|
return {
|
||||||
id: formData.id,
|
id: formData.id,
|
||||||
|
|
@ -71,7 +73,7 @@ export function transformFormToApi(formData: RuleSceneFormData): IotRuleScene {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将API响应数据转换为表单格式
|
* 将 API 响应数据转换为表单格式
|
||||||
*/
|
*/
|
||||||
export function transformApiToForm(apiData: IotRuleScene): RuleSceneFormData {
|
export function transformApiToForm(apiData: IotRuleScene): RuleSceneFormData {
|
||||||
return {
|
return {
|
||||||
|
|
@ -94,6 +96,7 @@ export function transformApiToForm(apiData: IotRuleScene): RuleSceneFormData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO @puhui999:貌似没用到;
|
||||||
/**
|
/**
|
||||||
* 创建默认的触发器配置
|
* 创建默认的触发器配置
|
||||||
*/
|
*/
|
||||||
|
|
@ -144,6 +147,7 @@ export function createDefaultTriggerConfig(type?: number): TriggerConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO @puhui999:貌似没用到;
|
||||||
/**
|
/**
|
||||||
* 创建默认的执行器配置
|
* 创建默认的执行器配置
|
||||||
*/
|
*/
|
||||||
|
|
@ -174,6 +178,7 @@ export function createDefaultActionConfig(type?: number): ActionConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO @puhui999:全局已经有类似的
|
||||||
/**
|
/**
|
||||||
* 深度克隆对象(用于避免引用问题)
|
* 深度克隆对象(用于避免引用问题)
|
||||||
*/
|
*/
|
||||||
|
|
@ -203,6 +208,7 @@ export function deepClone<T>(obj: T): T {
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO @puhui999:貌似没用到;
|
||||||
/**
|
/**
|
||||||
* 清理空值和无效数据
|
* 清理空值和无效数据
|
||||||
*/
|
*/
|
||||||
|
|
@ -297,6 +303,7 @@ export function formatCronExpression(cron: string): string {
|
||||||
return description || cron
|
return description || cron
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO @puhui999:貌似没用到;
|
||||||
/**
|
/**
|
||||||
* 验证并修复数据结构
|
* 验证并修复数据结构
|
||||||
*/
|
*/
|
||||||
|
|
@ -351,6 +358,7 @@ export function validateAndFixData(data: IotRuleScene): IotRuleScene {
|
||||||
return fixed
|
return fixed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO @puhui999:貌似没用到;
|
||||||
/**
|
/**
|
||||||
* 比较两个场景联动规则是否相等(忽略key字段)
|
* 比较两个场景联动规则是否相等(忽略key字段)
|
||||||
*/
|
*/
|
||||||
|
|
@ -401,6 +409,5 @@ export function getRuleSceneSummary(ruleScene: IotRuleScene): {
|
||||||
return '未知执行类型'
|
return '未知执行类型'
|
||||||
}
|
}
|
||||||
}) || []
|
}) || []
|
||||||
|
|
||||||
return { triggerSummary, actionSummary }
|
return { triggerSummary, actionSummary }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,18 @@
|
||||||
/**
|
/**
|
||||||
* IoT 场景联动表单验证工具函数
|
* IoT 场景联动表单验证工具函数
|
||||||
*/
|
*/
|
||||||
|
import {
|
||||||
|
FormValidationRules,
|
||||||
|
IotRuleScene,
|
||||||
|
TriggerConfig,
|
||||||
|
ActionConfig
|
||||||
|
} from '@/api/iot/rule/scene/scene.types'
|
||||||
|
import {
|
||||||
|
IotRuleSceneTriggerTypeEnum,
|
||||||
|
IotRuleSceneActionTypeEnum
|
||||||
|
} from '@/api/iot/rule/scene/scene.types'
|
||||||
|
|
||||||
import { FormValidationRules, IotRuleScene, TriggerConfig, ActionConfig } from '@/api/iot/rule/scene/scene.types'
|
/** 基础表单验证规则 */
|
||||||
import { IotRuleSceneTriggerTypeEnum, IotRuleSceneActionTypeEnum } from '@/api/iot/rule/scene/scene.types'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 基础表单验证规则
|
|
||||||
*/
|
|
||||||
export const getBaseValidationRules = (): FormValidationRules => ({
|
export const getBaseValidationRules = (): FormValidationRules => ({
|
||||||
name: [
|
name: [
|
||||||
{ required: true, message: '场景名称不能为空', trigger: 'blur' },
|
{ required: true, message: '场景名称不能为空', trigger: 'blur' },
|
||||||
|
|
@ -30,55 +35,47 @@ export const getBaseValidationRules = (): FormValidationRules => ({
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/** 验证CRON表达式格式 */
|
||||||
* 验证CRON表达式格式
|
|
||||||
*/
|
|
||||||
export function validateCronExpression(cron: string): boolean {
|
export function validateCronExpression(cron: string): boolean {
|
||||||
if (!cron || cron.trim().length === 0) return false
|
if (!cron || cron.trim().length === 0) return false
|
||||||
|
// 基础的 CRON 表达式正则验证(支持6位和7位格式)
|
||||||
// 基础的CRON表达式正则验证(支持6位和7位格式)
|
const cronRegex =
|
||||||
const cronRegex = /^(\*|([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])|\*\/([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])) (\*|([0-9]|1[0-9]|2[0-3])|\*\/([0-9]|1[0-9]|2[0-3])) (\*|([1-9]|1[0-9]|2[0-9]|3[0-1])|\*\/([1-9]|1[0-9]|2[0-9]|3[0-1])) (\*|([1-9]|1[0-2])|\*\/([1-9]|1[0-2])) (\*|([0-6])|\*\/([0-6]))( (\*|([1-9][0-9]{3})|\*\/([1-9][0-9]{3})))?$/
|
/^(\*|([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])|\*\/([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])) (\*|([0-9]|1[0-9]|2[0-3])|\*\/([0-9]|1[0-9]|2[0-3])) (\*|([1-9]|1[0-9]|2[0-9]|3[0-1])|\*\/([1-9]|1[0-9]|2[0-9]|3[0-1])) (\*|([1-9]|1[0-2])|\*\/([1-9]|1[0-2])) (\*|([0-6])|\*\/([0-6]))( (\*|([1-9][0-9]{3})|\*\/([1-9][0-9]{3})))?$/
|
||||||
|
|
||||||
return cronRegex.test(cron.trim())
|
return cronRegex.test(cron.trim())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 验证设备名称数组 */
|
||||||
* 验证设备名称数组
|
|
||||||
*/
|
|
||||||
export function validateDeviceNames(deviceNames: string[]): boolean {
|
export function validateDeviceNames(deviceNames: string[]): boolean {
|
||||||
return Array.isArray(deviceNames) &&
|
return (
|
||||||
deviceNames.length > 0 &&
|
Array.isArray(deviceNames) &&
|
||||||
deviceNames.every(name => name && name.trim().length > 0)
|
deviceNames.length > 0 &&
|
||||||
|
deviceNames.every((name) => name && name.trim().length > 0)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 验证比较值格式 */
|
||||||
* 验证比较值格式
|
|
||||||
*/
|
|
||||||
export function validateCompareValue(operator: string, value: string): boolean {
|
export function validateCompareValue(operator: string, value: string): boolean {
|
||||||
if (!value || value.trim().length === 0) return false
|
if (!value || value.trim().length === 0) return false
|
||||||
|
|
||||||
const trimmedValue = value.trim()
|
const trimmedValue = value.trim()
|
||||||
|
|
||||||
switch (operator) {
|
switch (operator) {
|
||||||
case 'between':
|
case 'between':
|
||||||
case 'not between':
|
case 'not between':
|
||||||
const betweenValues = trimmedValue.split(',')
|
const betweenValues = trimmedValue.split(',')
|
||||||
return betweenValues.length === 2 &&
|
return (
|
||||||
betweenValues.every(v => v.trim().length > 0) &&
|
betweenValues.length === 2 &&
|
||||||
!isNaN(Number(betweenValues[0].trim())) &&
|
betweenValues.every((v) => v.trim().length > 0) &&
|
||||||
!isNaN(Number(betweenValues[1].trim()))
|
!isNaN(Number(betweenValues[0].trim())) &&
|
||||||
|
!isNaN(Number(betweenValues[1].trim()))
|
||||||
|
)
|
||||||
case 'in':
|
case 'in':
|
||||||
case 'not in':
|
case 'not in':
|
||||||
const inValues = trimmedValue.split(',')
|
const inValues = trimmedValue.split(',')
|
||||||
return inValues.length > 0 && inValues.every(v => v.trim().length > 0)
|
return inValues.length > 0 && inValues.every((v) => v.trim().length > 0)
|
||||||
|
|
||||||
case '>':
|
case '>':
|
||||||
case '>=':
|
case '>=':
|
||||||
case '<':
|
case '<':
|
||||||
case '<=':
|
case '<=':
|
||||||
return !isNaN(Number(trimmedValue))
|
return !isNaN(Number(trimmedValue))
|
||||||
|
|
||||||
case '=':
|
case '=':
|
||||||
case '!=':
|
case '!=':
|
||||||
case 'like':
|
case 'like':
|
||||||
|
|
@ -88,14 +85,14 @@ export function validateCompareValue(operator: string, value: string): boolean {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 验证触发器配置 */
|
||||||
* 验证触发器配置
|
export function validateTriggerConfig(trigger: TriggerConfig): {
|
||||||
*/
|
valid: boolean
|
||||||
export function validateTriggerConfig(trigger: TriggerConfig): { valid: boolean; message?: string } {
|
message?: string
|
||||||
|
} {
|
||||||
if (!trigger.type) {
|
if (!trigger.type) {
|
||||||
return { valid: false, message: '触发类型不能为空' }
|
return { valid: false, message: '触发类型不能为空' }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 定时触发验证
|
// 定时触发验证
|
||||||
if (trigger.type === IotRuleSceneTriggerTypeEnum.TIMER) {
|
if (trigger.type === IotRuleSceneTriggerTypeEnum.TIMER) {
|
||||||
if (!trigger.cronExpression) {
|
if (!trigger.cronExpression) {
|
||||||
|
|
@ -106,32 +103,26 @@ export function validateTriggerConfig(trigger: TriggerConfig): { valid: boolean;
|
||||||
}
|
}
|
||||||
return { valid: true }
|
return { valid: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设备触发验证
|
// 设备触发验证
|
||||||
if (!trigger.productKey) {
|
if (!trigger.productKey) {
|
||||||
return { valid: false, message: '产品标识不能为空' }
|
return { valid: false, message: '产品标识不能为空' }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!trigger.deviceNames || !validateDeviceNames(trigger.deviceNames)) {
|
if (!trigger.deviceNames || !validateDeviceNames(trigger.deviceNames)) {
|
||||||
return { valid: false, message: '设备名称不能为空' }
|
return { valid: false, message: '设备名称不能为空' }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设备状态变更无需额外条件验证
|
// 设备状态变更无需额外条件验证
|
||||||
if (trigger.type === IotRuleSceneTriggerTypeEnum.DEVICE_STATE_UPDATE) {
|
if (trigger.type === IotRuleSceneTriggerTypeEnum.DEVICE_STATE_UPDATE) {
|
||||||
return { valid: true }
|
return { valid: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 其他设备触发类型需要验证条件
|
// 其他设备触发类型需要验证条件
|
||||||
if (!trigger.conditions || trigger.conditions.length === 0) {
|
if (!trigger.conditions || trigger.conditions.length === 0) {
|
||||||
return { valid: false, message: '触发条件不能为空' }
|
return { valid: false, message: '触发条件不能为空' }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证每个条件的参数
|
// 验证每个条件的参数
|
||||||
for (const condition of trigger.conditions) {
|
for (const condition of trigger.conditions) {
|
||||||
if (!condition.parameters || condition.parameters.length === 0) {
|
if (!condition.parameters || condition.parameters.length === 0) {
|
||||||
return { valid: false, message: '触发条件参数不能为空' }
|
return { valid: false, message: '触发条件参数不能为空' }
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const param of condition.parameters) {
|
for (const param of condition.parameters) {
|
||||||
if (!param.operator) {
|
if (!param.operator) {
|
||||||
return { valid: false, message: '操作符不能为空' }
|
return { valid: false, message: '操作符不能为空' }
|
||||||
|
|
@ -141,34 +132,32 @@ export function validateTriggerConfig(trigger: TriggerConfig): { valid: boolean;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { valid: true }
|
return { valid: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 验证执行器配置 */
|
||||||
* 验证执行器配置
|
|
||||||
*/
|
|
||||||
export function validateActionConfig(action: ActionConfig): { valid: boolean; message?: string } {
|
export function validateActionConfig(action: ActionConfig): { valid: boolean; message?: string } {
|
||||||
if (!action.type) {
|
if (!action.type) {
|
||||||
return { valid: false, message: '执行类型不能为空' }
|
return { valid: false, message: '执行类型不能为空' }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 告警触发/恢复验证
|
// 告警触发/恢复验证
|
||||||
if (action.type === IotRuleSceneActionTypeEnum.ALERT_TRIGGER ||
|
if (
|
||||||
action.type === IotRuleSceneActionTypeEnum.ALERT_RECOVER) {
|
action.type === IotRuleSceneActionTypeEnum.ALERT_TRIGGER ||
|
||||||
|
action.type === IotRuleSceneActionTypeEnum.ALERT_RECOVER
|
||||||
|
) {
|
||||||
if (!action.alertConfigId) {
|
if (!action.alertConfigId) {
|
||||||
return { valid: false, message: '告警配置ID不能为空' }
|
return { valid: false, message: '告警配置ID不能为空' }
|
||||||
}
|
}
|
||||||
return { valid: true }
|
return { valid: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设备控制验证
|
// 设备控制验证
|
||||||
if (action.type === IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET ||
|
if (
|
||||||
action.type === IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE) {
|
action.type === IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET ||
|
||||||
|
action.type === IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE
|
||||||
|
) {
|
||||||
if (!action.deviceControl) {
|
if (!action.deviceControl) {
|
||||||
return { valid: false, message: '设备控制配置不能为空' }
|
return { valid: false, message: '设备控制配置不能为空' }
|
||||||
}
|
}
|
||||||
|
|
||||||
const { deviceControl } = action
|
const { deviceControl } = action
|
||||||
if (!deviceControl.productKey) {
|
if (!deviceControl.productKey) {
|
||||||
return { valid: false, message: '产品标识不能为空' }
|
return { valid: false, message: '产品标识不能为空' }
|
||||||
|
|
@ -185,34 +174,28 @@ export function validateActionConfig(action: ActionConfig): { valid: boolean; me
|
||||||
if (!deviceControl.params || Object.keys(deviceControl.params).length === 0) {
|
if (!deviceControl.params || Object.keys(deviceControl.params).length === 0) {
|
||||||
return { valid: false, message: '参数不能为空' }
|
return { valid: false, message: '参数不能为空' }
|
||||||
}
|
}
|
||||||
|
|
||||||
return { valid: true }
|
return { valid: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
return { valid: false, message: '未知的执行类型' }
|
return { valid: false, message: '未知的执行类型' }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// TODO @puhui999:貌似没用到?
|
||||||
* 验证完整的场景联动规则
|
/** 验证完整的场景联动规则 */
|
||||||
*/
|
|
||||||
export function validateRuleScene(ruleScene: IotRuleScene): { valid: boolean; message?: string } {
|
export function validateRuleScene(ruleScene: IotRuleScene): { valid: boolean; message?: string } {
|
||||||
// 基础字段验证
|
// 基础字段验证
|
||||||
if (!ruleScene.name || ruleScene.name.trim().length === 0) {
|
if (!ruleScene.name || ruleScene.name.trim().length === 0) {
|
||||||
return { valid: false, message: '场景名称不能为空' }
|
return { valid: false, message: '场景名称不能为空' }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ruleScene.status !== 0 && ruleScene.status !== 1) {
|
if (ruleScene.status !== 0 && ruleScene.status !== 1) {
|
||||||
return { valid: false, message: '场景状态必须为0或1' }
|
return { valid: false, message: '场景状态必须为0或1' }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ruleScene.triggers || ruleScene.triggers.length === 0) {
|
if (!ruleScene.triggers || ruleScene.triggers.length === 0) {
|
||||||
return { valid: false, message: '至少需要一个触发器' }
|
return { valid: false, message: '至少需要一个触发器' }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ruleScene.actions || ruleScene.actions.length === 0) {
|
if (!ruleScene.actions || ruleScene.actions.length === 0) {
|
||||||
return { valid: false, message: '至少需要一个执行器' }
|
return { valid: false, message: '至少需要一个执行器' }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证每个触发器
|
// 验证每个触发器
|
||||||
for (let i = 0; i < ruleScene.triggers.length; i++) {
|
for (let i = 0; i < ruleScene.triggers.length; i++) {
|
||||||
const triggerResult = validateTriggerConfig(ruleScene.triggers[i])
|
const triggerResult = validateTriggerConfig(ruleScene.triggers[i])
|
||||||
|
|
@ -220,7 +203,6 @@ export function validateRuleScene(ruleScene: IotRuleScene): { valid: boolean; me
|
||||||
return { valid: false, message: `触发器${i + 1}: ${triggerResult.message}` }
|
return { valid: false, message: `触发器${i + 1}: ${triggerResult.message}` }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证每个执行器
|
// 验证每个执行器
|
||||||
for (let i = 0; i < ruleScene.actions.length; i++) {
|
for (let i = 0; i < ruleScene.actions.length; i++) {
|
||||||
const actionResult = validateActionConfig(ruleScene.actions[i])
|
const actionResult = validateActionConfig(ruleScene.actions[i])
|
||||||
|
|
@ -228,14 +210,16 @@ export function validateRuleScene(ruleScene: IotRuleScene): { valid: boolean; me
|
||||||
return { valid: false, message: `执行器${i + 1}: ${actionResult.message}` }
|
return { valid: false, message: `执行器${i + 1}: ${actionResult.message}` }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { valid: true }
|
return { valid: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO @puhui999:下面 getOperatorOptions、getTriggerTypeOptions、getActionTypeOptions 三个貌似没用到?如果用到的话,要不放到 yudao-ui-admin-vue3/src/views/iot/utils/constants.ts 里
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取操作符选项
|
* 获取操作符选项
|
||||||
*/
|
*/
|
||||||
export function getOperatorOptions() {
|
export function getOperatorOptions() {
|
||||||
|
// TODO @puhui999:这个能不能从枚举计算出来,减少后续添加枚举的维护
|
||||||
return [
|
return [
|
||||||
{ value: '=', label: '等于' },
|
{ value: '=', label: '等于' },
|
||||||
{ value: '!=', label: '不等于' },
|
{ value: '!=', label: '不等于' },
|
||||||
|
|
@ -256,6 +240,7 @@ export function getOperatorOptions() {
|
||||||
* 获取触发类型选项
|
* 获取触发类型选项
|
||||||
*/
|
*/
|
||||||
export function getTriggerTypeOptions() {
|
export function getTriggerTypeOptions() {
|
||||||
|
// TODO @puhui999:这个能不能从枚举计算出来,减少后续添加枚举的维护
|
||||||
return [
|
return [
|
||||||
{ value: IotRuleSceneTriggerTypeEnum.DEVICE_STATE_UPDATE, label: '设备上下线变更' },
|
{ value: IotRuleSceneTriggerTypeEnum.DEVICE_STATE_UPDATE, label: '设备上下线变更' },
|
||||||
{ value: IotRuleSceneTriggerTypeEnum.DEVICE_PROPERTY_POST, label: '物模型属性上报' },
|
{ value: IotRuleSceneTriggerTypeEnum.DEVICE_PROPERTY_POST, label: '物模型属性上报' },
|
||||||
|
|
@ -269,6 +254,7 @@ export function getTriggerTypeOptions() {
|
||||||
* 获取执行类型选项
|
* 获取执行类型选项
|
||||||
*/
|
*/
|
||||||
export function getActionTypeOptions() {
|
export function getActionTypeOptions() {
|
||||||
|
// TODO @puhui999:这个能不能从枚举计算出来,减少后续添加枚举的维护
|
||||||
return [
|
return [
|
||||||
{ value: IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET, label: '设备属性设置' },
|
{ value: IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET, label: '设备属性设置' },
|
||||||
{ value: IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE, label: '设备服务调用' },
|
{ value: IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE, label: '设备服务调用' },
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue