diff --git a/src/api/iot/rule/scene/scene.types.ts b/src/api/iot/rule/scene/scene.types.ts index bf0cffe83..ca021e725 100644 --- a/src/api/iot/rule/scene/scene.types.ts +++ b/src/api/iot/rule/scene/scene.types.ts @@ -2,6 +2,7 @@ * IoT 场景联动接口定义 */ +// TODO @puhui999:枚举挪到 views/iot/utils/constants.ts 里 // 枚举定义 const IotRuleSceneTriggerTypeEnum = { DEVICE_STATE_UPDATE: 1, // 设备上下线变更 @@ -24,6 +25,7 @@ const IotDeviceMessageTypeEnum = { EVENT: 'event' // 事件 } as const +// TODO @puhui999:这个貌似可以不要? const IotDeviceMessageIdentifierEnum = { PROPERTY_SET: 'set', // 属性设置 SERVICE_INVOKE: '${identifier}' // 服务调用 @@ -44,6 +46,7 @@ const IotRuleSceneTriggerConditionParameterOperatorEnum = { NOT_NULL: { name: '非空', value: 'not null' } // 非空 } as const +// TODO @puhui999:下面 IotAlertConfigReceiveTypeEnum、DeviceStateEnum 没用到,貌似可以删除下? const IotAlertConfigReceiveTypeEnum = { SMS: 1, // 短信 MAIL: 2, // 邮箱 @@ -57,6 +60,7 @@ const DeviceStateEnum = { OFFLINE: 2 // 离线 } as const +// TODO @puhui999:这个全局已经有啦 // 通用状态枚举 const CommonStatusEnum = { ENABLE: 0, // 开启 @@ -64,6 +68,7 @@ const CommonStatusEnum = { } as const // 基础接口 +// TODO @puhui999:这个貌似可以不要? interface TenantBaseDO { createTime?: Date // 创建时间 updateTime?: Date // 更新时间 @@ -169,6 +174,7 @@ interface IotRuleScene extends TenantBaseDO { } // 工具类型 +// TODO @puhui999:这些在瞅瞅~ type TriggerType = (typeof IotRuleSceneTriggerTypeEnum)[keyof typeof IotRuleSceneTriggerTypeEnum] type ActionType = (typeof IotRuleSceneActionTypeEnum)[keyof typeof IotRuleSceneActionTypeEnum] type MessageType = (typeof IotDeviceMessageTypeEnum)[keyof typeof IotDeviceMessageTypeEnum] diff --git a/src/views/iot/rule/scene/IoT场景联动规则表单设计思路文档.md b/src/views/iot/rule/scene/IoT场景联动规则表单设计思路文档.md index 4563f2483..17e933043 100644 --- a/src/views/iot/rule/scene/IoT场景联动规则表单设计思路文档.md +++ b/src/views/iot/rule/scene/IoT场景联动规则表单设计思路文档.md @@ -1,3 +1,4 @@ +// TODO @puhui999:这些后续需要删除哈 # IoT场景联动规则表单设计思路文档 ## 概述 diff --git a/src/views/iot/rule/scene/IotThingModelTSLRespVO数据结构文档.md b/src/views/iot/rule/scene/IotThingModelTSLRespVO数据结构文档.md index aff32ccce..7ad53a65a 100644 --- a/src/views/iot/rule/scene/IotThingModelTSLRespVO数据结构文档.md +++ b/src/views/iot/rule/scene/IotThingModelTSLRespVO数据结构文档.md @@ -1,3 +1,4 @@ +// TODO @puhui999:这些后续需要删除哈 # IotThingModelTSLRespVO 数据结构文档 ## 概述 diff --git a/src/views/iot/rule/scene/components/selectors/types.ts b/src/views/iot/rule/scene/components/selectors/types.ts index 465303521..4446d4a63 100644 --- a/src/views/iot/rule/scene/components/selectors/types.ts +++ b/src/views/iot/rule/scene/components/selectors/types.ts @@ -1,5 +1,7 @@ // IoT物模型TSL数据类型定义 +// TODO @puhui999:看看这些里面,是不是一些已经有了哈?可以复用下~ + /** 物模型TSL响应数据结构 */ export interface IotThingModelTSLRespVO { productId: number diff --git a/src/views/iot/rule/scene/index.vue b/src/views/iot/rule/scene/index.vue index 30723d539..7b8c019fc 100644 --- a/src/views/iot/rule/scene/index.vue +++ b/src/views/iot/rule/scene/index.vue @@ -36,6 +36,7 @@ class="!w-240px" /> + + @@ -124,6 +126,7 @@ - - - + - - @@ -435,6 +430,7 @@ const handleSelectionChange = (selection: IotRuleScene[]) => { selectedRows.value = selection } +// TODO @puhui999:batch 操作的逻辑,要不和其它 UI 界面保持一致,或者相对一致哈; const handleBatchEnable = async () => { try { await ElMessageBox.confirm(`确定要启用选中的 ${selectedRows.value.length} 个规则吗?`, '提示', { diff --git a/src/views/iot/rule/scene/utils/errorHandler.ts b/src/views/iot/rule/scene/utils/errorHandler.ts index c7b10f316..81efa1b59 100644 --- a/src/views/iot/rule/scene/utils/errorHandler.ts +++ b/src/views/iot/rule/scene/utils/errorHandler.ts @@ -2,6 +2,8 @@ * IoT 场景联动错误处理和用户反馈工具 */ +// TODO @puhui999:这个貌似用不到? + import { ElMessage, ElMessageBox, ElNotification } from 'element-plus' // 错误类型枚举 diff --git a/src/views/iot/rule/scene/utils/transform.ts b/src/views/iot/rule/scene/utils/transform.ts index c01d398f4..4d9d67969 100644 --- a/src/views/iot/rule/scene/utils/transform.ts +++ b/src/views/iot/rule/scene/utils/transform.ts @@ -1,7 +1,6 @@ /** * IoT 场景联动数据转换工具函数 */ - import { IotRuleScene, TriggerConfig, @@ -12,6 +11,8 @@ import { } from '@/api/iot/rule/scene/scene.types' import { generateUUID } from '@/utils' +// TODO @puhui999:这些是不是放到对应的界面,会好一丢丢哈? + /** * 创建默认的表单数据 */ @@ -19,7 +20,7 @@ export function createDefaultFormData(): RuleSceneFormData { return { name: '', description: '', - status: 0, + status: 0, // TODO @puhui999:枚举值 triggers: [], actions: [] } @@ -30,7 +31,7 @@ export function createDefaultFormData(): RuleSceneFormData { */ export function createDefaultTriggerData(): TriggerFormData { return { - type: 2, // 默认为属性上报 + type: 2, // 默认为属性上报 TODO @puhui999:枚举值 productId: undefined, deviceId: undefined, identifier: undefined, @@ -46,7 +47,7 @@ export function createDefaultTriggerData(): TriggerFormData { */ export function createDefaultActionData(): ActionFormData { return { - type: 1, // 默认为属性设置 + type: 1, // 默认为属性设置 TODO @puhui999:枚举值 productId: undefined, deviceId: undefined, params: {}, @@ -58,7 +59,8 @@ export function createDefaultActionData(): ActionFormData { * 将表单数据转换为API请求格式 */ export function transformFormToApi(formData: RuleSceneFormData): IotRuleScene { - // 这里需要根据实际API结构进行转换 + // TODO @puhui999:这个关注下 + // 这里需要根据实际 API 结构进行转换 // 暂时返回基本结构 return { id: formData.id, @@ -71,7 +73,7 @@ export function transformFormToApi(formData: RuleSceneFormData): IotRuleScene { } /** - * 将API响应数据转换为表单格式 + * 将 API 响应数据转换为表单格式 */ export function transformApiToForm(apiData: IotRuleScene): RuleSceneFormData { 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(obj: T): T { return obj } +// TODO @puhui999:貌似没用到; /** * 清理空值和无效数据 */ @@ -297,6 +303,7 @@ export function formatCronExpression(cron: string): string { return description || cron } +// TODO @puhui999:貌似没用到; /** * 验证并修复数据结构 */ @@ -351,6 +358,7 @@ export function validateAndFixData(data: IotRuleScene): IotRuleScene { return fixed } +// TODO @puhui999:貌似没用到; /** * 比较两个场景联动规则是否相等(忽略key字段) */ @@ -401,6 +409,5 @@ export function getRuleSceneSummary(ruleScene: IotRuleScene): { return '未知执行类型' } }) || [] - return { triggerSummary, actionSummary } } diff --git a/src/views/iot/rule/scene/utils/validation.ts b/src/views/iot/rule/scene/utils/validation.ts index 479be793c..f6a49ad4b 100644 --- a/src/views/iot/rule/scene/utils/validation.ts +++ b/src/views/iot/rule/scene/utils/validation.ts @@ -1,13 +1,18 @@ /** * 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 => ({ name: [ { required: true, message: '场景名称不能为空', trigger: 'blur' }, @@ -30,55 +35,47 @@ export const getBaseValidationRules = (): FormValidationRules => ({ ] }) -/** - * 验证CRON表达式格式 - */ +/** 验证CRON表达式格式 */ export function validateCronExpression(cron: string): boolean { if (!cron || cron.trim().length === 0) return false - - // 基础的CRON表达式正则验证(支持6位和7位格式) - 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})))?$/ - + // 基础的 CRON 表达式正则验证(支持6位和7位格式) + 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})))?$/ return cronRegex.test(cron.trim()) } -/** - * 验证设备名称数组 - */ +/** 验证设备名称数组 */ export function validateDeviceNames(deviceNames: string[]): boolean { - return Array.isArray(deviceNames) && - deviceNames.length > 0 && - deviceNames.every(name => name && name.trim().length > 0) + return ( + Array.isArray(deviceNames) && + deviceNames.length > 0 && + deviceNames.every((name) => name && name.trim().length > 0) + ) } -/** - * 验证比较值格式 - */ +/** 验证比较值格式 */ export function validateCompareValue(operator: string, value: string): boolean { if (!value || value.trim().length === 0) return false - const trimmedValue = value.trim() - switch (operator) { case 'between': case 'not between': const betweenValues = trimmedValue.split(',') - return betweenValues.length === 2 && - betweenValues.every(v => v.trim().length > 0) && - !isNaN(Number(betweenValues[0].trim())) && - !isNaN(Number(betweenValues[1].trim())) - + return ( + betweenValues.length === 2 && + betweenValues.every((v) => v.trim().length > 0) && + !isNaN(Number(betweenValues[0].trim())) && + !isNaN(Number(betweenValues[1].trim())) + ) case 'in': case 'not in': 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 '<=': return !isNaN(Number(trimmedValue)) - case '=': case '!=': case 'like': @@ -88,14 +85,14 @@ export function validateCompareValue(operator: string, value: string): boolean { } } -/** - * 验证触发器配置 - */ -export function validateTriggerConfig(trigger: TriggerConfig): { valid: boolean; message?: string } { +/** 验证触发器配置 */ +export function validateTriggerConfig(trigger: TriggerConfig): { + valid: boolean + message?: string +} { if (!trigger.type) { return { valid: false, message: '触发类型不能为空' } } - // 定时触发验证 if (trigger.type === IotRuleSceneTriggerTypeEnum.TIMER) { if (!trigger.cronExpression) { @@ -106,32 +103,26 @@ export function validateTriggerConfig(trigger: TriggerConfig): { valid: boolean; } return { valid: true } } - // 设备触发验证 if (!trigger.productKey) { return { valid: false, message: '产品标识不能为空' } } - if (!trigger.deviceNames || !validateDeviceNames(trigger.deviceNames)) { return { valid: false, message: '设备名称不能为空' } } - // 设备状态变更无需额外条件验证 if (trigger.type === IotRuleSceneTriggerTypeEnum.DEVICE_STATE_UPDATE) { return { valid: true } } - // 其他设备触发类型需要验证条件 if (!trigger.conditions || trigger.conditions.length === 0) { return { valid: false, message: '触发条件不能为空' } } - // 验证每个条件的参数 for (const condition of trigger.conditions) { if (!condition.parameters || condition.parameters.length === 0) { return { valid: false, message: '触发条件参数不能为空' } } - for (const param of condition.parameters) { if (!param.operator) { return { valid: false, message: '操作符不能为空' } @@ -141,34 +132,32 @@ export function validateTriggerConfig(trigger: TriggerConfig): { valid: boolean; } } } - return { valid: true } } -/** - * 验证执行器配置 - */ +/** 验证执行器配置 */ export function validateActionConfig(action: ActionConfig): { valid: boolean; message?: string } { if (!action.type) { return { valid: false, message: '执行类型不能为空' } } - // 告警触发/恢复验证 - if (action.type === IotRuleSceneActionTypeEnum.ALERT_TRIGGER || - action.type === IotRuleSceneActionTypeEnum.ALERT_RECOVER) { + if ( + action.type === IotRuleSceneActionTypeEnum.ALERT_TRIGGER || + action.type === IotRuleSceneActionTypeEnum.ALERT_RECOVER + ) { if (!action.alertConfigId) { return { valid: false, message: '告警配置ID不能为空' } } return { valid: true } } - // 设备控制验证 - if (action.type === IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET || - action.type === IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE) { + if ( + action.type === IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET || + action.type === IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE + ) { if (!action.deviceControl) { return { valid: false, message: '设备控制配置不能为空' } } - const { deviceControl } = action if (!deviceControl.productKey) { 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) { return { valid: false, message: '参数不能为空' } } - return { valid: true } } return { valid: false, message: '未知的执行类型' } } -/** - * 验证完整的场景联动规则 - */ +// TODO @puhui999:貌似没用到? +/** 验证完整的场景联动规则 */ export function validateRuleScene(ruleScene: IotRuleScene): { valid: boolean; message?: string } { // 基础字段验证 if (!ruleScene.name || ruleScene.name.trim().length === 0) { return { valid: false, message: '场景名称不能为空' } } - if (ruleScene.status !== 0 && ruleScene.status !== 1) { return { valid: false, message: '场景状态必须为0或1' } } - if (!ruleScene.triggers || ruleScene.triggers.length === 0) { return { valid: false, message: '至少需要一个触发器' } } - if (!ruleScene.actions || ruleScene.actions.length === 0) { return { valid: false, message: '至少需要一个执行器' } } - // 验证每个触发器 for (let i = 0; i < ruleScene.triggers.length; 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}` } } } - // 验证每个执行器 for (let i = 0; i < ruleScene.actions.length; 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: true } } +// TODO @puhui999:下面 getOperatorOptions、getTriggerTypeOptions、getActionTypeOptions 三个貌似没用到?如果用到的话,要不放到 yudao-ui-admin-vue3/src/views/iot/utils/constants.ts 里 + /** * 获取操作符选项 */ export function getOperatorOptions() { + // TODO @puhui999:这个能不能从枚举计算出来,减少后续添加枚举的维护 return [ { value: '=', label: '等于' }, { value: '!=', label: '不等于' }, @@ -256,6 +240,7 @@ export function getOperatorOptions() { * 获取触发类型选项 */ export function getTriggerTypeOptions() { + // TODO @puhui999:这个能不能从枚举计算出来,减少后续添加枚举的维护 return [ { value: IotRuleSceneTriggerTypeEnum.DEVICE_STATE_UPDATE, label: '设备上下线变更' }, { value: IotRuleSceneTriggerTypeEnum.DEVICE_PROPERTY_POST, label: '物模型属性上报' }, @@ -269,6 +254,7 @@ export function getTriggerTypeOptions() { * 获取执行类型选项 */ export function getActionTypeOptions() { + // TODO @puhui999:这个能不能从枚举计算出来,减少后续添加枚举的维护 return [ { value: IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET, label: '设备属性设置' }, { value: IotRuleSceneActionTypeEnum.DEVICE_SERVICE_INVOKE, label: '设备服务调用' },