perf: 【IoT 物联网】场景联动移除自定义校验规则简化校验逻辑

pull/808/head
puhui999 2025-08-07 16:31:07 +08:00
parent 09be0a10b1
commit 9684857174
9 changed files with 149 additions and 441 deletions

View File

@ -106,7 +106,6 @@ const props = defineProps<{
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:modelValue', value: TriggerCondition): void (e: 'update:modelValue', value: TriggerCondition): void
(e: 'validate', result: { valid: boolean; message: string }): void
}>() }>()
const condition = useVModel(props, 'modelValue', emit) const condition = useVModel(props, 'modelValue', emit)
@ -155,10 +154,6 @@ const timeOperatorOptions = [
} }
] ]
//
const validationMessage = ref('')
const isValid = ref(true)
// //
const needsTimeInput = computed(() => { const needsTimeInput = computed(() => {
const timeOnlyOperators = [ const timeOnlyOperators = [
@ -181,62 +176,16 @@ const needsSecondTimeInput = computed(() => {
// //
const updateConditionField = (field: keyof TriggerCondition, value: any) => { const updateConditionField = (field: keyof TriggerCondition, value: any) => {
condition.value[field] = value condition.value[field] = value
updateValidationResult()
} }
const updateValidationResult = () => {
if (!condition.value.operator) {
isValid.value = false
validationMessage.value = '请选择时间条件'
emit('validate', { valid: false, message: validationMessage.value })
return
}
//
if (condition.value.operator === IotRuleSceneTriggerTimeOperatorEnum.TODAY.value) {
isValid.value = true
validationMessage.value = '当前时间条件配置验证通过'
emit('validate', { valid: true, message: validationMessage.value })
return
}
if (needsTimeInput.value && !condition.value.timeValue) {
isValid.value = false
validationMessage.value = '请设置时间值'
emit('validate', { valid: false, message: validationMessage.value })
return
}
if (needsSecondTimeInput.value && !condition.value.timeValue2) {
isValid.value = false
validationMessage.value = '请设置结束时间'
emit('validate', { valid: false, message: validationMessage.value })
return
}
isValid.value = true
validationMessage.value = '当前时间条件配置验证通过'
emit('validate', { valid: true, message: validationMessage.value })
}
//
watch(
() => [condition.value.operator, condition.value.timeValue, condition.value.timeValue2],
() => {
updateValidationResult()
},
{ immediate: true }
)
// //
watch( watch(
() => condition.value.operator, () => condition.value.operator,
(newOperator) => { (newOperator) => {
if (newOperator === IotRuleSceneTriggerTimeOperatorEnum.TODAY.value) { if (newOperator === IotRuleSceneTriggerTimeOperatorEnum.TODAY.value) {
condition.value.timeValue = undefined ;(condition.value as any).timeValue = undefined(condition.value as any).timeValue2 = undefined
condition.value.timeValue2 = undefined
} else if (!needsSecondTimeInput.value) { } else if (!needsSecondTimeInput.value) {
condition.value.timeValue2 = undefined ;(condition.value as any).timeValue2 = undefined
} }
} }
) )

View File

@ -55,7 +55,6 @@
type="service" type="service"
:config="{ service: selectedService }" :config="{ service: selectedService }"
placeholder="请输入JSON格式的服务参数" placeholder="请输入JSON格式的服务参数"
@validate="handleParamsValidate"
/> />
</el-form-item> </el-form-item>
</div> </div>
@ -70,7 +69,6 @@
type="property" type="property"
:config="{ properties: thingModelProperties }" :config="{ properties: thingModelProperties }"
placeholder="请输入JSON格式的控制参数" placeholder="请输入JSON格式的控制参数"
@validate="handleParamsValidate"
/> />
</el-form-item> </el-form-item>
</div> </div>
@ -86,7 +84,8 @@ import type { Action } from '@/api/iot/rule/scene'
import type { ThingModelProperty, ThingModelService } from '@/api/iot/thingmodel' import type { ThingModelProperty, ThingModelService } from '@/api/iot/thingmodel'
import { import {
IotRuleSceneActionTypeEnum, IotRuleSceneActionTypeEnum,
IoTThingModelAccessModeEnum IoTThingModelAccessModeEnum,
IoTDataSpecsDataTypeEnum
} from '@/views/iot/utils/constants' } from '@/views/iot/utils/constants'
import { ThingModelApi } from '@/api/iot/thingmodel' import { ThingModelApi } from '@/api/iot/thingmodel'
@ -126,12 +125,6 @@ const paramsValue = computed({
} }
}) })
//
const handleParamsValidate = (result: { valid: boolean; message: string }) => {
//
console.log('参数验证结果:', result)
}
const isPropertySetAction = computed(() => { const isPropertySetAction = computed(() => {
// //
return action.value.type === IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET return action.value.type === IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET
@ -301,16 +294,16 @@ const loadServiceFromTSL = async (productId: number, serviceIdentifier: string)
*/ */
const getDefaultValueForParam = (param: any) => { const getDefaultValueForParam = (param: any) => {
switch (param.dataType) { switch (param.dataType) {
case 'int': case IoTDataSpecsDataTypeEnum.INT:
return 0 return 0
case 'float': case IoTDataSpecsDataTypeEnum.FLOAT:
case 'double': case IoTDataSpecsDataTypeEnum.DOUBLE:
return 0.0 return 0.0
case 'bool': case IoTDataSpecsDataTypeEnum.BOOL:
return false return false
case 'text': case IoTDataSpecsDataTypeEnum.TEXT:
return '' return ''
case 'enum': case IoTDataSpecsDataTypeEnum.ENUM:
// 使 // 使
if (param.dataSpecs?.dataSpecsList && param.dataSpecs.dataSpecsList.length > 0) { if (param.dataSpecs?.dataSpecsList && param.dataSpecs.dataSpecsList.length > 0) {
return param.dataSpecs.dataSpecsList[0].value return param.dataSpecs.dataSpecsList[0].value

View File

@ -29,7 +29,6 @@
:model-value="trigger" :model-value="trigger"
@update:model-value="updateCondition" @update:model-value="updateCondition"
:trigger-type="trigger.type" :trigger-type="trigger.type"
@validate="handleValidate"
@trigger-type-change="handleTriggerTypeChange" @trigger-type-change="handleTriggerTypeChange"
/> />
</div> </div>
@ -123,7 +122,6 @@
@update:model-value="(value) => updateSubGroup(subGroupIndex, value)" @update:model-value="(value) => updateSubGroup(subGroupIndex, value)"
:trigger-type="trigger.type" :trigger-type="trigger.type"
:max-conditions="maxConditionsPerGroup" :max-conditions="maxConditionsPerGroup"
@validate="(result) => handleSubGroupValidate(subGroupIndex, result)"
/> />
</div> </div>
@ -182,7 +180,6 @@ const props = defineProps<{
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:modelValue', value: Trigger): void (e: 'update:modelValue', value: Trigger): void
(e: 'validate', value: { valid: boolean; message: string }): void
(e: 'trigger-type-change', type: number): void (e: 'trigger-type-change', type: number): void
}>() }>()
@ -192,18 +189,11 @@ const trigger = useVModel(props, 'modelValue', emit)
const maxSubGroups = 3 // 3 const maxSubGroups = 3 // 3
const maxConditionsPerGroup = 3 // 3 const maxConditionsPerGroup = 3 // 3
//
const subGroupValidations = ref<{ [key: number]: { valid: boolean; message: string } }>({})
// //
const updateCondition = (condition: Trigger) => { const updateCondition = (condition: Trigger) => {
trigger.value = condition trigger.value = condition
} }
const handleValidate = (result: { valid: boolean; message: string }) => {
emit('validate', result)
}
const handleTriggerTypeChange = (type: number) => { const handleTriggerTypeChange = (type: number) => {
trigger.value.type = type trigger.value.type = type
emit('trigger-type-change', type) emit('trigger-type-change', type)
@ -231,21 +221,6 @@ const addSubGroup = () => {
const removeSubGroup = (index: number) => { const removeSubGroup = (index: number) => {
if (trigger.value.conditionGroups) { if (trigger.value.conditionGroups) {
trigger.value.conditionGroups.splice(index, 1) trigger.value.conditionGroups.splice(index, 1)
delete subGroupValidations.value[index]
//
const newValidations: { [key: number]: { valid: boolean; message: string } } = {}
Object.keys(subGroupValidations.value).forEach((key) => {
const numKey = parseInt(key)
if (numKey > index) {
newValidations[numKey - 1] = subGroupValidations.value[numKey]
} else if (numKey < index) {
newValidations[numKey] = subGroupValidations.value[numKey]
}
})
subGroupValidations.value = newValidations
updateValidationResult()
} }
} }
@ -258,35 +233,4 @@ const updateSubGroup = (index: number, subGroup: any) => {
const removeConditionGroup = () => { const removeConditionGroup = () => {
trigger.value.conditionGroups = undefined trigger.value.conditionGroups = undefined
} }
const handleSubGroupValidate = (index: number, result: { valid: boolean; message: string }) => {
subGroupValidations.value[index] = result
updateValidationResult()
}
const updateValidationResult = () => {
if (!trigger.value.conditionGroups || trigger.value.conditionGroups.length === 0) {
emit('validate', { valid: true, message: '条件组容器为空,验证通过' })
return
}
const validations = Object.values(subGroupValidations.value)
const allValid = validations.every((v: any) => v.valid)
if (allValid) {
emit('validate', { valid: true, message: '条件组容器配置验证通过' })
} else {
const errorMessages = validations.filter((v: any) => !v.valid).map((v: any) => v.message)
emit('validate', { valid: false, message: `子条件组配置错误: ${errorMessages.join('; ')}` })
}
}
//
watch(
() => trigger.value.conditionGroups,
() => {
updateValidationResult()
},
{ deep: true, immediate: true }
)
</script> </script>

View File

@ -100,7 +100,6 @@
type="service" type="service"
:config="serviceConfig" :config="serviceConfig"
placeholder="请输入JSON格式的服务参数" placeholder="请输入JSON格式的服务参数"
@validate="handleValueValidate"
/> />
<!-- 事件上报参数配置 --> <!-- 事件上报参数配置 -->
<JsonParamsInput <JsonParamsInput
@ -109,7 +108,6 @@
type="event" type="event"
:config="eventConfig" :config="eventConfig"
placeholder="请输入JSON格式的事件参数" placeholder="请输入JSON格式的事件参数"
@validate="handleValueValidate"
/> />
<!-- 普通值输入 --> <!-- 普通值输入 -->
<ValueInput <ValueInput
@ -119,7 +117,6 @@
:property-type="propertyType" :property-type="propertyType"
:operator="condition.operator" :operator="condition.operator"
:property-config="propertyConfig" :property-config="propertyConfig"
@validate="handleValueValidate"
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -202,15 +199,11 @@ const props = defineProps<{
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:modelValue', value: Trigger): void (e: 'update:modelValue', value: Trigger): void
(e: 'validate', result: { valid: boolean; message: string }): void
(e: 'trigger-type-change', value: number): void (e: 'trigger-type-change', value: number): void
}>() }>()
// //
const condition = useVModel(props, 'modelValue', emit) const condition = useVModel(props, 'modelValue', emit)
// TODO @puhui999 validationMessage
const isValid = ref(true)
const validationMessage = ref('')
const propertyType = ref('') const propertyType = ref('')
const propertyConfig = ref<any>(null) const propertyConfig = ref<any>(null)
@ -279,7 +272,6 @@ const triggerTypeOptions = getTriggerTypeOptions()
// //
const updateConditionField = (field: keyof Trigger, value: any) => { const updateConditionField = (field: keyof Trigger, value: any) => {
;(condition.value as any)[field] = value ;(condition.value as any)[field] = value
updateValidationResult()
} }
const handleTriggerTypeChange = (type: number) => { const handleTriggerTypeChange = (type: number) => {
@ -290,13 +282,11 @@ const handleProductChange = () => {
// //
condition.value.deviceId = undefined condition.value.deviceId = undefined
condition.value.identifier = '' condition.value.identifier = ''
updateValidationResult()
} }
const handleDeviceChange = () => { const handleDeviceChange = () => {
// //
condition.value.identifier = '' condition.value.identifier = ''
updateValidationResult()
} }
const handlePropertyChange = (propertyInfo: any) => { const handlePropertyChange = (propertyInfo: any) => {
@ -312,88 +302,9 @@ const handlePropertyChange = (propertyInfo: any) => {
condition.value.operator = '=' condition.value.operator = '='
} }
} }
updateValidationResult()
} }
const handleOperatorChange = () => { const handleOperatorChange = () => {
updateValidationResult() //
} }
//
const handleValueValidate = (result: { valid: boolean; message: string }) => {
isValid.value = result.valid
validationMessage.value = result.message
emit('validate', result)
updateValidationResult()
}
//
// TODO @puhui999 validator
const updateValidationResult = () => {
if (isDevicePropertyTrigger.value) {
//
if (!condition.value.productId) {
isValid.value = false
validationMessage.value = '请选择产品'
emit('validate', { valid: false, message: validationMessage.value })
return
}
if (!condition.value.deviceId) {
isValid.value = false
validationMessage.value = '请选择设备'
emit('validate', { valid: false, message: validationMessage.value })
return
}
if (!condition.value.identifier) {
isValid.value = false
validationMessage.value = '请选择监控项'
emit('validate', { valid: false, message: validationMessage.value })
return
}
//
if (
props.triggerType !== IotRuleSceneTriggerTypeEnum.DEVICE_SERVICE_INVOKE &&
props.triggerType !== IotRuleSceneTriggerTypeEnum.DEVICE_EVENT_POST &&
!condition.value.operator
) {
isValid.value = false
validationMessage.value = '请选择操作符'
emit('validate', { valid: false, message: validationMessage.value })
return
}
if (!condition.value.value) {
isValid.value = false
validationMessage.value = '请输入比较值'
emit('validate', { valid: false, message: validationMessage.value })
return
}
}
isValid.value = true
validationMessage.value = '主条件配置验证通过'
emit('validate', { valid: true, message: validationMessage.value })
}
//
watch(
() => [
condition.value.productId,
condition.value.deviceId,
condition.value.identifier,
//
props.triggerType !== IotRuleSceneTriggerTypeEnum.DEVICE_SERVICE_INVOKE &&
props.triggerType !== IotRuleSceneTriggerTypeEnum.DEVICE_EVENT_POST
? condition.value.operator
: null,
condition.value.value
],
() => {
updateValidationResult()
},
{ immediate: true }
)
</script> </script>

View File

@ -56,7 +56,6 @@
:model-value="condition" :model-value="condition"
@update:model-value="(value) => updateCondition(conditionIndex, value)" @update:model-value="(value) => updateCondition(conditionIndex, value)"
:trigger-type="triggerType" :trigger-type="triggerType"
@validate="(result) => handleConditionValidate(conditionIndex, result)"
/> />
</div> </div>
</div> </div>
@ -100,7 +99,6 @@ const props = defineProps<{
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:modelValue', value: TriggerCondition[]): void (e: 'update:modelValue', value: TriggerCondition[]): void
(e: 'validate', result: { valid: boolean; message: string }): void
}>() }>()
const subGroup = useVModel(props, 'modelValue', emit) const subGroup = useVModel(props, 'modelValue', emit)
@ -108,9 +106,6 @@ const subGroup = useVModel(props, 'modelValue', emit)
// //
const maxConditions = computed(() => props.maxConditions || 3) const maxConditions = computed(() => props.maxConditions || 3)
//
const conditionValidations = ref<{ [key: number]: { valid: boolean; message: string } }>({})
// //
const addCondition = () => { const addCondition = () => {
// subGroup.value // subGroup.value
@ -143,21 +138,6 @@ const addCondition = () => {
const removeCondition = (index: number) => { const removeCondition = (index: number) => {
if (subGroup.value) { if (subGroup.value) {
subGroup.value.splice(index, 1) subGroup.value.splice(index, 1)
delete conditionValidations.value[index]
//
const newValidations: { [key: number]: { valid: boolean; message: string } } = {}
Object.keys(conditionValidations.value).forEach((key) => {
const numKey = parseInt(key)
if (numKey > index) {
newValidations[numKey - 1] = conditionValidations.value[numKey]
} else if (numKey < index) {
newValidations[numKey] = conditionValidations.value[numKey]
}
})
conditionValidations.value = newValidations
updateValidationResult()
} }
} }
@ -166,35 +146,4 @@ const updateCondition = (index: number, condition: TriggerCondition) => {
subGroup.value[index] = condition subGroup.value[index] = condition
} }
} }
const handleConditionValidate = (index: number, result: { valid: boolean; message: string }) => {
conditionValidations.value[index] = result
updateValidationResult()
}
const updateValidationResult = () => {
if (!subGroup.value || subGroup.value.length === 0) {
emit('validate', { valid: false, message: '子条件组至少需要一个条件' })
return
}
const validations = Object.values(conditionValidations.value)
const allValid = validations.every((v: any) => v.valid)
if (allValid) {
emit('validate', { valid: true, message: '子条件组配置验证通过' })
} else {
const errorMessages = validations.filter((v: any) => !v.valid).map((v: any) => v.message)
emit('validate', { valid: false, message: `条件配置错误: ${errorMessages.join('; ')}` })
}
}
//
watch(
() => subGroup.value,
() => {
updateValidationResult()
},
{ deep: true, immediate: true }
)
</script> </script>

View File

@ -136,6 +136,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { useVModel } from '@vueuse/core' import { useVModel } from '@vueuse/core'
import { InfoFilled } from '@element-plus/icons-vue' import { InfoFilled } from '@element-plus/icons-vue'
import { IoTDataSpecsDataTypeEnum } from '@/views/iot/utils/constants'
/** JSON参数输入组件 - 通用版本 */ /** JSON参数输入组件 - 通用版本 */
defineOptions({ name: 'JsonParamsInput' }) defineOptions({ name: 'JsonParamsInput' })
@ -169,7 +170,6 @@ interface Props {
interface Emits { interface Emits {
(e: 'update:modelValue', value: string): void (e: 'update:modelValue', value: string): void
(e: 'validate', result: { valid: boolean; message: string }): void
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
@ -317,7 +317,6 @@ const handleParamsChange = () => {
// //
if (typeof parsed !== 'object' || parsed === null) { if (typeof parsed !== 'object' || parsed === null) {
jsonError.value = '参数必须是一个有效的 JSON 对象' jsonError.value = '参数必须是一个有效的 JSON 对象'
emit('validate', { valid: false, message: jsonError.value })
return return
} }
@ -325,7 +324,6 @@ const handleParamsChange = () => {
for (const param of paramsList.value) { for (const param of paramsList.value) {
if (param.required && (!parsed[param.identifier] || parsed[param.identifier] === '')) { if (param.required && (!parsed[param.identifier] || parsed[param.identifier] === '')) {
jsonError.value = `参数 ${param.name} 为必填项` jsonError.value = `参数 ${param.name} 为必填项`
emit('validate', { valid: false, message: jsonError.value })
return return
} }
} }
@ -334,10 +332,9 @@ const handleParamsChange = () => {
} }
// //
emit('validate', { valid: true, message: 'JSON格式正确' }) jsonError.value = ''
} catch (error) { } catch (error) {
jsonError.value = `JSON格式错误: ${error instanceof Error ? error.message : '未知错误'}` jsonError.value = `JSON格式错误: ${error instanceof Error ? error.message : '未知错误'}`
emit('validate', { valid: false, message: jsonError.value })
} }
} }
@ -352,7 +349,6 @@ const clearParams = () => {
paramsJson.value = '' paramsJson.value = ''
localValue.value = '' localValue.value = ''
jsonError.value = '' jsonError.value = ''
emit('validate', { valid: true, message: '' })
} }
// //
@ -373,35 +369,35 @@ const getParamTypeName = (dataType: string) => {
const getParamTypeTag = (dataType: string) => { const getParamTypeTag = (dataType: string) => {
const tagMap = { const tagMap = {
int: 'primary', [IoTDataSpecsDataTypeEnum.INT]: 'primary',
float: 'success', [IoTDataSpecsDataTypeEnum.FLOAT]: 'success',
double: 'success', [IoTDataSpecsDataTypeEnum.DOUBLE]: 'success',
text: 'info', [IoTDataSpecsDataTypeEnum.TEXT]: 'info',
bool: 'warning', [IoTDataSpecsDataTypeEnum.BOOL]: 'warning',
enum: 'danger', [IoTDataSpecsDataTypeEnum.ENUM]: 'danger',
date: 'primary', [IoTDataSpecsDataTypeEnum.DATE]: 'primary',
struct: 'info', [IoTDataSpecsDataTypeEnum.STRUCT]: 'info',
array: 'warning' [IoTDataSpecsDataTypeEnum.ARRAY]: 'warning'
} }
return tagMap[dataType] || 'info' return tagMap[dataType] || 'info'
} }
const getExampleValue = (param: any) => { const getExampleValue = (param: any) => {
switch (param.dataType) { switch (param.dataType) {
case 'int': case IoTDataSpecsDataTypeEnum.INT:
return '25' return '25'
case 'float': case IoTDataSpecsDataTypeEnum.FLOAT:
case 'double': case IoTDataSpecsDataTypeEnum.DOUBLE:
return '25.5' return '25.5'
case 'bool': case IoTDataSpecsDataTypeEnum.BOOL:
return 'false' return 'false'
case 'text': case IoTDataSpecsDataTypeEnum.TEXT:
return '"auto"' return '"auto"'
case 'enum': case IoTDataSpecsDataTypeEnum.ENUM:
return '"option1"' return '"option1"'
case 'struct': case IoTDataSpecsDataTypeEnum.STRUCT:
return '{}' return '{}'
case 'array': case IoTDataSpecsDataTypeEnum.ARRAY:
return '[]' return '[]'
default: default:
return '""' return '""'

View File

@ -132,19 +132,12 @@
</el-tooltip> </el-tooltip>
</template> </template>
</el-input> </el-input>
<!-- 验证提示 -->
<div v-if="validationMessage" class="mt-4px">
<el-text :type="isValid ? 'success' : 'danger'" size="small">
<Icon :icon="isValid ? 'ep:check' : 'ep:warning-filled'" />
{{ validationMessage }}
</el-text>
</div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useVModel } from '@vueuse/core' import { useVModel } from '@vueuse/core'
import { IoTDataSpecsDataTypeEnum } from '@/views/iot/utils/constants'
/** 值输入组件 */ /** 值输入组件 */
defineOptions({ name: 'ValueInput' }) defineOptions({ name: 'ValueInput' })
@ -158,7 +151,6 @@ interface Props {
interface Emits { interface Emits {
(e: 'update:modelValue', value: string): void (e: 'update:modelValue', value: string): void
(e: 'validate', result: { valid: boolean; message: string }): void
} }
const props = defineProps<Props>() const props = defineProps<Props>()
@ -173,8 +165,6 @@ const rangeStart = ref('')
const rangeEnd = ref('') const rangeEnd = ref('')
const dateValue = ref('') const dateValue = ref('')
const numberValue = ref<number>() const numberValue = ref<number>()
const validationMessage = ref('')
const isValid = ref(true)
// //
const enumOptions = computed(() => { const enumOptions = computed(() => {
@ -199,14 +189,18 @@ const listPreview = computed(() => {
// //
const isNumericType = () => { const isNumericType = () => {
return ['int', 'float', 'double'].includes(props.propertyType || '') return [
IoTDataSpecsDataTypeEnum.INT,
IoTDataSpecsDataTypeEnum.FLOAT,
IoTDataSpecsDataTypeEnum.DOUBLE
].includes(props.propertyType || '')
} }
const getInputType = () => { const getInputType = () => {
switch (props.propertyType) { switch (props.propertyType) {
case 'int': case IoTDataSpecsDataTypeEnum.INT:
case 'float': case IoTDataSpecsDataTypeEnum.FLOAT:
case 'double': case IoTDataSpecsDataTypeEnum.DOUBLE:
return 'number' return 'number'
default: default:
return 'text' return 'text'
@ -215,22 +209,22 @@ const getInputType = () => {
const getPlaceholder = () => { const getPlaceholder = () => {
const typeMap = { const typeMap = {
string: '请输入字符串', [IoTDataSpecsDataTypeEnum.TEXT]: '请输入字符串',
int: '请输入整数', [IoTDataSpecsDataTypeEnum.INT]: '请输入整数',
float: '请输入浮点数', [IoTDataSpecsDataTypeEnum.FLOAT]: '请输入浮点数',
double: '请输入双精度数', [IoTDataSpecsDataTypeEnum.DOUBLE]: '请输入双精度数',
struct: '请输入JSON格式数据', [IoTDataSpecsDataTypeEnum.STRUCT]: '请输入JSON格式数据',
array: '请输入数组格式数据' [IoTDataSpecsDataTypeEnum.ARRAY]: '请输入数组格式数据'
} }
return typeMap[props.propertyType || ''] || '请输入值' return typeMap[props.propertyType || ''] || '请输入值'
} }
const getPrecision = () => { const getPrecision = () => {
return props.propertyType === 'int' ? 0 : 2 return props.propertyType === IoTDataSpecsDataTypeEnum.INT ? 0 : 2
} }
const getStep = () => { const getStep = () => {
return props.propertyType === 'int' ? 1 : 0.1 return props.propertyType === IoTDataSpecsDataTypeEnum.INT ? 1 : 0.1
} }
const getMin = () => { const getMin = () => {
@ -243,7 +237,7 @@ const getMax = () => {
// //
const handleChange = () => { const handleChange = () => {
validateValue() //
} }
const handleRangeChange = () => { const handleRangeChange = () => {
@ -252,106 +246,16 @@ const handleRangeChange = () => {
} else { } else {
localValue.value = '' localValue.value = ''
} }
validateValue()
} }
const handleDateChange = (value: string) => { const handleDateChange = (value: string) => {
localValue.value = value || '' localValue.value = value || ''
validateValue()
} }
const handleNumberChange = (value: number | undefined) => { const handleNumberChange = (value: number | undefined) => {
localValue.value = value?.toString() || '' localValue.value = value?.toString() || ''
validateValue()
} }
//
const validateValue = () => {
if (!localValue.value) {
isValid.value = false
validationMessage.value = '请输入值'
emit('validate', { valid: false, message: validationMessage.value })
return
}
//
if (isNumericType()) {
const num = parseFloat(localValue.value)
if (isNaN(num)) {
isValid.value = false
validationMessage.value = '请输入有效的数字'
emit('validate', { valid: false, message: validationMessage.value })
return
}
//
const min = getMin()
const max = getMax()
if (min !== undefined && num < min) {
isValid.value = false
validationMessage.value = `值不能小于 ${min}`
emit('validate', { valid: false, message: validationMessage.value })
return
}
if (max !== undefined && num > max) {
isValid.value = false
validationMessage.value = `值不能大于 ${max}`
emit('validate', { valid: false, message: validationMessage.value })
return
}
}
//
if (props.operator === 'between') {
const parts = localValue.value.split(',')
if (parts.length !== 2) {
isValid.value = false
validationMessage.value = '范围格式错误'
emit('validate', { valid: false, message: validationMessage.value })
return
}
const start = parseFloat(parts[0])
const end = parseFloat(parts[1])
if (isNaN(start) || isNaN(end)) {
isValid.value = false
validationMessage.value = '范围值必须是数字'
emit('validate', { valid: false, message: validationMessage.value })
return
}
if (start >= end) {
isValid.value = false
validationMessage.value = '起始值必须小于结束值'
emit('validate', { valid: false, message: validationMessage.value })
return
}
}
//
if (props.operator === 'in') {
if (listPreview.value.length === 0) {
isValid.value = false
validationMessage.value = '请输入至少一个值'
emit('validate', { valid: false, message: validationMessage.value })
return
}
}
//
isValid.value = true
validationMessage.value = '输入值验证通过'
emit('validate', { valid: true, message: validationMessage.value })
}
//
watch(
() => localValue.value,
() => {
validateValue()
}
)
// //
watch( watch(
() => props.operator, () => props.operator,
@ -363,11 +267,4 @@ watch(
numberValue.value = undefined numberValue.value = undefined
} }
) )
//
onMounted(() => {
if (localValue.value) {
validateValue()
}
})
</script> </script>

View File

@ -35,7 +35,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { useVModel } from '@vueuse/core' import { useVModel } from '@vueuse/core'
import { IotRuleSceneTriggerConditionParameterOperatorEnum } from '@/views/iot/utils/constants' import {
IotRuleSceneTriggerConditionParameterOperatorEnum,
IoTDataSpecsDataTypeEnum
} from '@/views/iot/utils/constants'
/** 操作符选择器组件 */ /** 操作符选择器组件 */
defineOptions({ name: 'OperatorSelector' }) defineOptions({ name: 'OperatorSelector' })
@ -60,7 +63,14 @@ const allOperators = [
symbol: '=', symbol: '=',
description: '值完全相等时触发', description: '值完全相等时触发',
example: 'temperature = 25', example: 'temperature = 25',
supportedTypes: ['int', 'float', 'double', 'string', 'bool', 'enum'] supportedTypes: [
IoTDataSpecsDataTypeEnum.INT,
IoTDataSpecsDataTypeEnum.FLOAT,
IoTDataSpecsDataTypeEnum.DOUBLE,
IoTDataSpecsDataTypeEnum.TEXT,
IoTDataSpecsDataTypeEnum.BOOL,
IoTDataSpecsDataTypeEnum.ENUM
]
}, },
{ {
value: IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_EQUALS.value, value: IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_EQUALS.value,
@ -68,7 +78,14 @@ const allOperators = [
symbol: '≠', symbol: '≠',
description: '值不相等时触发', description: '值不相等时触发',
example: 'power != false', example: 'power != false',
supportedTypes: ['int', 'float', 'double', 'string', 'bool', 'enum'] supportedTypes: [
IoTDataSpecsDataTypeEnum.INT,
IoTDataSpecsDataTypeEnum.FLOAT,
IoTDataSpecsDataTypeEnum.DOUBLE,
IoTDataSpecsDataTypeEnum.TEXT,
IoTDataSpecsDataTypeEnum.BOOL,
IoTDataSpecsDataTypeEnum.ENUM
]
}, },
{ {
value: IotRuleSceneTriggerConditionParameterOperatorEnum.GREATER_THAN.value, value: IotRuleSceneTriggerConditionParameterOperatorEnum.GREATER_THAN.value,
@ -76,7 +93,12 @@ const allOperators = [
symbol: '>', symbol: '>',
description: '值大于指定值时触发', description: '值大于指定值时触发',
example: 'temperature > 30', example: 'temperature > 30',
supportedTypes: ['int', 'float', 'double', 'date'] supportedTypes: [
IoTDataSpecsDataTypeEnum.INT,
IoTDataSpecsDataTypeEnum.FLOAT,
IoTDataSpecsDataTypeEnum.DOUBLE,
IoTDataSpecsDataTypeEnum.DATE
]
}, },
{ {
value: IotRuleSceneTriggerConditionParameterOperatorEnum.GREATER_THAN_OR_EQUALS.value, value: IotRuleSceneTriggerConditionParameterOperatorEnum.GREATER_THAN_OR_EQUALS.value,
@ -84,7 +106,12 @@ const allOperators = [
symbol: '≥', symbol: '≥',
description: '值大于或等于指定值时触发', description: '值大于或等于指定值时触发',
example: 'humidity >= 80', example: 'humidity >= 80',
supportedTypes: ['int', 'float', 'double', 'date'] supportedTypes: [
IoTDataSpecsDataTypeEnum.INT,
IoTDataSpecsDataTypeEnum.FLOAT,
IoTDataSpecsDataTypeEnum.DOUBLE,
IoTDataSpecsDataTypeEnum.DATE
]
}, },
{ {
value: IotRuleSceneTriggerConditionParameterOperatorEnum.LESS_THAN.value, value: IotRuleSceneTriggerConditionParameterOperatorEnum.LESS_THAN.value,
@ -92,7 +119,12 @@ const allOperators = [
symbol: '<', symbol: '<',
description: '值小于指定值时触发', description: '值小于指定值时触发',
example: 'temperature < 10', example: 'temperature < 10',
supportedTypes: ['int', 'float', 'double', 'date'] supportedTypes: [
IoTDataSpecsDataTypeEnum.INT,
IoTDataSpecsDataTypeEnum.FLOAT,
IoTDataSpecsDataTypeEnum.DOUBLE,
IoTDataSpecsDataTypeEnum.DATE
]
}, },
{ {
value: IotRuleSceneTriggerConditionParameterOperatorEnum.LESS_THAN_OR_EQUALS.value, value: IotRuleSceneTriggerConditionParameterOperatorEnum.LESS_THAN_OR_EQUALS.value,
@ -100,7 +132,12 @@ const allOperators = [
symbol: '≤', symbol: '≤',
description: '值小于或等于指定值时触发', description: '值小于或等于指定值时触发',
example: 'battery <= 20', example: 'battery <= 20',
supportedTypes: ['int', 'float', 'double', 'date'] supportedTypes: [
IoTDataSpecsDataTypeEnum.INT,
IoTDataSpecsDataTypeEnum.FLOAT,
IoTDataSpecsDataTypeEnum.DOUBLE,
IoTDataSpecsDataTypeEnum.DATE
]
}, },
{ {
value: IotRuleSceneTriggerConditionParameterOperatorEnum.IN.value, value: IotRuleSceneTriggerConditionParameterOperatorEnum.IN.value,
@ -108,7 +145,12 @@ const allOperators = [
symbol: '∈', symbol: '∈',
description: '值在指定列表中时触发', description: '值在指定列表中时触发',
example: 'status in [1,2,3]', example: 'status in [1,2,3]',
supportedTypes: ['int', 'float', 'string', 'enum'] supportedTypes: [
IoTDataSpecsDataTypeEnum.INT,
IoTDataSpecsDataTypeEnum.FLOAT,
IoTDataSpecsDataTypeEnum.TEXT,
IoTDataSpecsDataTypeEnum.ENUM
]
}, },
{ {
value: IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_IN.value, value: IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_IN.value,
@ -116,7 +158,12 @@ const allOperators = [
symbol: '∉', symbol: '∉',
description: '值不在指定列表中时触发', description: '值不在指定列表中时触发',
example: 'status not in [1,2,3]', example: 'status not in [1,2,3]',
supportedTypes: ['int', 'float', 'string', 'enum'] supportedTypes: [
IoTDataSpecsDataTypeEnum.INT,
IoTDataSpecsDataTypeEnum.FLOAT,
IoTDataSpecsDataTypeEnum.TEXT,
IoTDataSpecsDataTypeEnum.ENUM
]
}, },
{ {
value: IotRuleSceneTriggerConditionParameterOperatorEnum.BETWEEN.value, value: IotRuleSceneTriggerConditionParameterOperatorEnum.BETWEEN.value,
@ -124,7 +171,12 @@ const allOperators = [
symbol: '⊆', symbol: '⊆',
description: '值在指定范围内时触发', description: '值在指定范围内时触发',
example: 'temperature between 20,30', example: 'temperature between 20,30',
supportedTypes: ['int', 'float', 'double', 'date'] supportedTypes: [
IoTDataSpecsDataTypeEnum.INT,
IoTDataSpecsDataTypeEnum.FLOAT,
IoTDataSpecsDataTypeEnum.DOUBLE,
IoTDataSpecsDataTypeEnum.DATE
]
}, },
{ {
value: IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_BETWEEN.value, value: IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_BETWEEN.value,
@ -132,7 +184,12 @@ const allOperators = [
symbol: '⊄', symbol: '⊄',
description: '值不在指定范围内时触发', description: '值不在指定范围内时触发',
example: 'temperature not between 20,30', example: 'temperature not between 20,30',
supportedTypes: ['int', 'float', 'double', 'date'] supportedTypes: [
IoTDataSpecsDataTypeEnum.INT,
IoTDataSpecsDataTypeEnum.FLOAT,
IoTDataSpecsDataTypeEnum.DOUBLE,
IoTDataSpecsDataTypeEnum.DATE
]
}, },
{ {
value: IotRuleSceneTriggerConditionParameterOperatorEnum.LIKE.value, value: IotRuleSceneTriggerConditionParameterOperatorEnum.LIKE.value,
@ -140,7 +197,7 @@ const allOperators = [
symbol: '≈', symbol: '≈',
description: '字符串匹配指定模式时触发', description: '字符串匹配指定模式时触发',
example: 'message like "%error%"', example: 'message like "%error%"',
supportedTypes: ['string'] supportedTypes: [IoTDataSpecsDataTypeEnum.TEXT]
}, },
{ {
value: IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_NULL.value, value: IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_NULL.value,
@ -148,7 +205,15 @@ const allOperators = [
symbol: '≠∅', symbol: '≠∅',
description: '值非空时触发', description: '值非空时触发',
example: 'data not null', example: 'data not null',
supportedTypes: ['int', 'float', 'double', 'string', 'bool', 'enum', 'date'] supportedTypes: [
IoTDataSpecsDataTypeEnum.INT,
IoTDataSpecsDataTypeEnum.FLOAT,
IoTDataSpecsDataTypeEnum.DOUBLE,
IoTDataSpecsDataTypeEnum.TEXT,
IoTDataSpecsDataTypeEnum.BOOL,
IoTDataSpecsDataTypeEnum.ENUM,
IoTDataSpecsDataTypeEnum.DATE
]
} }
] ]

View File

@ -159,7 +159,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { useVModel } from '@vueuse/core' import { useVModel } from '@vueuse/core'
import { InfoFilled } from '@element-plus/icons-vue' import { InfoFilled } from '@element-plus/icons-vue'
import { IotRuleSceneTriggerTypeEnum, IoTThingModelTypeEnum } from '@/views/iot/utils/constants' import {
IotRuleSceneTriggerTypeEnum,
IoTThingModelTypeEnum,
IoTDataSpecsDataTypeEnum
} from '@/views/iot/utils/constants'
import type { import type {
IotThingModelTSLResp, IotThingModelTSLResp,
ThingModelEvent, ThingModelEvent,
@ -246,30 +250,30 @@ const selectedProperty = computed(() => {
// //
const getPropertyTypeName = (dataType: string) => { const getPropertyTypeName = (dataType: string) => {
const typeMap = { const typeMap = {
int: '整数', [IoTDataSpecsDataTypeEnum.INT]: '整数',
float: '浮点数', [IoTDataSpecsDataTypeEnum.FLOAT]: '浮点数',
double: '双精度', [IoTDataSpecsDataTypeEnum.DOUBLE]: '双精度',
text: '字符串', [IoTDataSpecsDataTypeEnum.TEXT]: '字符串',
bool: '布尔值', [IoTDataSpecsDataTypeEnum.BOOL]: '布尔值',
enum: '枚举', [IoTDataSpecsDataTypeEnum.ENUM]: '枚举',
date: '日期', [IoTDataSpecsDataTypeEnum.DATE]: '日期',
struct: '结构体', [IoTDataSpecsDataTypeEnum.STRUCT]: '结构体',
array: '数组' [IoTDataSpecsDataTypeEnum.ARRAY]: '数组'
} }
return typeMap[dataType] || dataType return typeMap[dataType] || dataType
} }
const getPropertyTypeTag = (dataType: string) => { const getPropertyTypeTag = (dataType: string) => {
const tagMap = { const tagMap = {
int: 'primary', [IoTDataSpecsDataTypeEnum.INT]: 'primary',
float: 'success', [IoTDataSpecsDataTypeEnum.FLOAT]: 'success',
double: 'success', [IoTDataSpecsDataTypeEnum.DOUBLE]: 'success',
text: 'info', [IoTDataSpecsDataTypeEnum.TEXT]: 'info',
bool: 'warning', [IoTDataSpecsDataTypeEnum.BOOL]: 'warning',
enum: 'danger', [IoTDataSpecsDataTypeEnum.ENUM]: 'danger',
date: 'primary', [IoTDataSpecsDataTypeEnum.DATE]: 'primary',
struct: 'info', [IoTDataSpecsDataTypeEnum.STRUCT]: 'info',
array: 'warning' [IoTDataSpecsDataTypeEnum.ARRAY]: 'warning'
} }
return tagMap[dataType] || 'info' return tagMap[dataType] || 'info'
} }