diff --git a/src/views/iot/device/index.vue b/src/views/iot/device/index.vue index 2aad141e..1f89ca64 100644 --- a/src/views/iot/device/index.vue +++ b/src/views/iot/device/index.vue @@ -434,11 +434,83 @@ -
+
+
+ + + + + + + + + +
+
按“四则运算规则”添加顺序从上到下依次计算,不按乘除优先。
+
+
@@ -554,6 +628,7 @@ @@ -1079,6 +1154,18 @@ const ruleForm = reactive>({ ruleValue: undefined, alarmLevel: '', }) +type CountArithmeticRule = { + ruleAttributeId?: number + ruleOperator?: string + ruleValue?: string | number + bigOperator?: string +} +const createEmptyCountArithmeticRule = (): CountArithmeticRule => ({ + ruleAttributeId: undefined, + ruleOperator: undefined, + ruleValue: undefined, +}) +const countArithmeticRules = ref([createEmptyCountArithmeticRule()]) const extraPointRules = ref< Array<{ @@ -1098,10 +1185,10 @@ const runningRuleOptions = [ const alarmRuleOptions = [{ value: '5', label: '报警' }] const countOperatorOptions = [ - { value: '+', label: '加' }, - { value: '-', label: '减' }, - { value: '*', label: '乘' }, - { value: '/', label: '除' }, + { value: '+', label: '+' }, + { value: '-', label: '-' }, + { value: '*', label: '*' }, + { value: '/', label: '/' }, ] const currentRuleOptions = computed(() => { @@ -1139,6 +1226,146 @@ const currentRuleOperatorOptions = computed(() => { const isRunningIdentifier = computed(() => { return (ruleForm.identifier || '').toString().toUpperCase() === 'RUNNING' }) +const resolveRuleAttributeIdByCode = (code: any) => { + if (code == null) return undefined + const targetCode = String(code).trim().toUpperCase() + const target = ruleAttributeOptions.value.find( + (item) => String(item?.attributeCode ?? '').trim().toUpperCase() === targetCode + ) + return target?.id +} +const toCountRuleValue = (value: any) => { + const text = String(value ?? '').trim() + if (!text) return '' + if (/^-?\d+(\.\d+)?$/.test(text)) { + return Number(text) + } + return text +} +const parseAlarmRunningFieldRule = (fieldRule: any) => { + if (!fieldRule) return [] as any[] + if (Array.isArray(fieldRule)) return fieldRule + if (typeof fieldRule === 'string') { + try { + const parsed = JSON.parse(fieldRule) + return Array.isArray(parsed) ? parsed : [] + } catch { + return [] + } + } + return [] +} +const buildAlarmRunningFieldRule = () => { + const list: Array<{ + code: string + operator: string + operatorRule: string | number + rule: string + }> = [] + if (ruleForm.ruleAttributeId && ruleForm.ruleOperator) { + const attr = ruleAttributeOptions.value.find((item) => item.id === ruleForm.ruleAttributeId) + if (attr?.attributeCode) { + list.push({ + code: attr.attributeCode, + operator: String(ruleForm.ruleOperator), + operatorRule: (ruleForm.ruleValue as any) ?? '', + rule: String(ruleForm.fieldRule ?? ''), + }) + } + } + extraPointRules.value.forEach((item) => { + if (!item.id || !item.operator) return + const attr = ruleAttributeOptions.value.find((opt) => opt.id === item.id) + if (!attr?.attributeCode) return + list.push({ + code: attr.attributeCode, + operator: String(item.operator), + operatorRule: (item.operatorRule as any) ?? '', + rule: String(item.rule ?? ruleForm.fieldRule ?? ''), + }) + }) + return JSON.stringify(list) +} +const buildCountFieldRule = (rules: CountArithmeticRule[]) => { + const validRules = rules.filter((item) => { + return item.ruleAttributeId && item.ruleOperator && String(item.ruleValue ?? '').trim() !== '' + }) + if (!validRules.length) return '' + const list = validRules.map((item, index) => { + const attr = ruleAttributeOptions.value.find((opt) => opt.id === item.ruleAttributeId) + if (!attr?.attributeCode) return undefined + const row: any = { + code: attr.attributeCode, + operator: String(item.ruleOperator), + value: toCountRuleValue(item.ruleValue), + } + if (index < validRules.length - 1 && item.bigOperator) { + row.bigOperator = String(item.bigOperator) + } + return row + }).filter(Boolean) + if (!list.length) return '' + return JSON.stringify(list) +} +const parseCountFieldRule = (fieldRule: any): CountArithmeticRule[] => { + if (!fieldRule) return [] + const parsed = (() => { + if (typeof fieldRule !== 'string') return fieldRule + try { + return JSON.parse(fieldRule) + } catch { + return undefined + } + })() as any + if (Array.isArray(parsed)) { + return parsed + .map((item: any) => { + const pointId = resolveRuleAttributeIdByCode(item?.code) ?? item?.pointId + return { + ruleAttributeId: pointId != null ? Number(pointId) : undefined, + ruleOperator: item?.operator, + ruleValue: item?.value ?? item?.operatorRule, + bigOperator: item?.bigOperator, + } as CountArithmeticRule + }) + .filter((item) => item.ruleAttributeId || item.ruleOperator || item.ruleValue != null || item.bigOperator) + } + const root = parsed?.expr ?? parsed + if (!root || typeof root !== 'object') return [] + const rows: CountArithmeticRule[] = [] + const walk = (node: any) => { + if (!node || typeof node !== 'object' || !node.op) return + if (node.left && typeof node.left === 'object' && node.left.op) { + walk(node.left) + } + const attrId = resolveRuleAttributeIdByCode(node?.left?.field) + const rightValue = + node?.right?.field != null + ? String(node.right.field) + : node?.right?.value != null + ? node.right.value + : undefined + rows.push({ + ruleAttributeId: attrId, + ruleOperator: node.op, + ruleValue: rightValue, + }) + } + walk(root) + return rows.filter((item) => item.ruleOperator || item.ruleAttributeId || item.ruleValue != null) +} +const handleAddCountArithmeticRule = () => { + countArithmeticRules.value.push(createEmptyCountArithmeticRule()) +} +const handleRemoveCountArithmeticRule = (index: number) => { + if (countArithmeticRules.value.length <= 1) return + if (index < 0 || index >= countArithmeticRules.value.length) return + countArithmeticRules.value.splice(index, 1) + const list = countArithmeticRules.value + if (list.length > 0) { + list[list.length - 1].bigOperator = undefined + } +} const openRuleForm = async (row: DevicePointRuleVO & { pointRulesVOList?: any[] }) => { const deviceId = row.deviceId || attributeDeviceId.value @@ -1155,12 +1382,27 @@ const openRuleForm = async (row: DevicePointRuleVO & { pointRulesVOList?: any[] ruleForm.defaultValue = row.defaultValue ruleForm.deviceId = row.deviceId ruleForm.alarmLevel = (row as any).alarmLevel + countArithmeticRules.value = [createEmptyCountArithmeticRule()] extraPointRules.value = [] const list = Array.isArray(row.pointRulesVOList) ? row.pointRulesVOList : [] - - if (list.length) { + const parsedAlarmRunningList = parseAlarmRunningFieldRule(row.fieldRule) + const identifier = (row.identifier || '').toString().toUpperCase() + + if (identifier !== 'COUNT' && parsedAlarmRunningList.length) { + const first = parsedAlarmRunningList[0] as any + ruleForm.fieldRule = first?.rule ?? '' + ruleForm.ruleAttributeId = resolveRuleAttributeIdByCode(first?.code) as any + ruleForm.ruleOperator = first?.operator as any + ruleForm.ruleValue = first?.operatorRule as any + extraPointRules.value = parsedAlarmRunningList.slice(1).map((item: any) => ({ + id: resolveRuleAttributeIdByCode(item?.code), + rule: item?.rule, + operator: item?.operator, + operatorRule: item?.operatorRule, + })) + } else if (list.length) { const first = list[0] as any const firstRule = first.rule ?? row.fieldRule const firstAttrId = (() => { @@ -1192,11 +1434,41 @@ const openRuleForm = async (row: DevicePointRuleVO & { pointRulesVOList?: any[] operator: item.operator, operatorRule: item.operatorRule, })) + if (identifier === 'COUNT') { + const parsedFieldRule = parseCountFieldRule(row.fieldRule) + if (parsedFieldRule.length) { + countArithmeticRules.value = parsedFieldRule + } else { + const parsedList = list.map((item: any) => ({ + ruleAttributeId: resolveRuleAttributeIdByCode(item?.code), + ruleOperator: item?.operator, + ruleValue: item?.operatorRule, + })) + const filteredParsedList = parsedList.filter((item) => { + return item.ruleAttributeId && item.ruleOperator && String(item.ruleValue ?? '').trim() !== '' + }) + countArithmeticRules.value = filteredParsedList.length + ? filteredParsedList + : [createEmptyCountArithmeticRule()] + } + } } else { ruleForm.fieldRule = row.fieldRule ruleForm.ruleAttributeId = row.ruleAttributeId ruleForm.ruleOperator = row.ruleOperator ruleForm.ruleValue = row.ruleValue as any + if (identifier === 'COUNT') { + const parsedFieldRule = parseCountFieldRule(row.fieldRule) + countArithmeticRules.value = parsedFieldRule.length + ? parsedFieldRule + : [ + { + ruleAttributeId: row.ruleAttributeId, + ruleOperator: row.ruleOperator, + ruleValue: row.ruleValue as any, + }, + ] + } } const options = currentRuleOptions.value @@ -1210,42 +1482,67 @@ const handleRuleSubmit = async () => { if (!ruleForm.id) return try { ruleFormLoading.value = true - const pointRulesVOList: any[] = [] - - if (ruleForm.ruleAttributeId && ruleForm.ruleOperator) { - const attr = ruleAttributeOptions.value.find( - (item) => item.id === ruleForm.ruleAttributeId - ) - if (attr && attr.attributeCode) { - pointRulesVOList.push({ - code: attr.attributeCode, - rule: ruleForm.fieldRule, - operator: ruleForm.ruleOperator, - operatorRule: ruleForm.ruleValue, - }) + if (isCountIdentifier.value) { + const hasIncompleteRule = countArithmeticRules.value.some((item) => { + return !item.ruleAttributeId || !item.ruleOperator || String(item.ruleValue ?? '').trim() === '' + }) + if (hasIncompleteRule) { + message.warning('请完整填写每一组四则运算规则') + return } - } - - extraPointRules.value.forEach((item) => { - if (!item.id || !item.operator) return - const attr = ruleAttributeOptions.value.find((opt) => opt.id === item.id) - if (!attr || !attr.attributeCode) return - pointRulesVOList.push({ - code: attr.attributeCode, - rule: item.rule ?? ruleForm.fieldRule, - operator: item.operator, - operatorRule: item.operatorRule, + const normalizedRules = countArithmeticRules.value.filter((item) => { + return item.ruleAttributeId && item.ruleOperator && String(item.ruleValue ?? '').trim() !== '' }) - }) - + if (!normalizedRules.length) { + message.warning('请至少填写一组四则运算规则') + return + } + for (let i = 0; i < normalizedRules.length - 1; i++) { + if (!normalizedRules[i].bigOperator) { + message.warning(`请为第${i + 1}组选择连接运算符`) + return + } + } + for (let i = 0; i < normalizedRules.length; i++) { + const item = normalizedRules[i] + if (item.ruleOperator !== '/') continue + const valueText = String(item.ruleValue ?? '').trim() + if (valueText !== '' && !Number.isNaN(Number(valueText)) && Number(valueText) === 0) { + message.warning(`第${i + 1}组为除法时,数值不能为0`) + return + } + } + const fieldRule = buildCountFieldRule(normalizedRules) + if (!fieldRule) { + message.warning('四则运算规则格式不正确') + return + } + const payload = { + id: ruleForm.id, + identifier: ruleForm.identifier, + fieldName: ruleForm.fieldName, + fieldRule, + defaultValue: ruleForm.defaultValue, + deviceId: ruleForm.deviceId ?? attributeDeviceId.value, + } + await request.put({ url: '/iot/device-point-rules/update', data: payload }) + message.success(t('common.updateSuccess')) + ruleDialogVisible.value = false + await getRuleList() + return + } + const alarmRunningFieldRule = buildAlarmRunningFieldRule() + if (!alarmRunningFieldRule || alarmRunningFieldRule === '[]') { + message.warning('请至少填写一组规则') + return + } const payload = { id: ruleForm.id, identifier: ruleForm.identifier, fieldName: ruleForm.fieldName, - fieldRule: ruleForm.fieldRule, + fieldRule: alarmRunningFieldRule, defaultValue: ruleForm.defaultValue, deviceId: ruleForm.deviceId ?? attributeDeviceId.value, - pointRulesVOList, } as any if (!isCountIdentifier.value) { payload.alarmLevel = ruleForm.alarmLevel