From a7dc1d1632c3c7411b1df77d3c80e5d262343fbd Mon Sep 17 00:00:00 2001 From: hwj Date: Thu, 22 Jan 2026 11:21:35 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E9=87=87=E9=9B=86=E8=AE=BE?= =?UTF-8?q?=E5=A4=87-=E6=B7=BB=E5=8A=A0=E7=82=B9=E4=BD=8D=E8=A7=84?= =?UTF-8?q?=E5=88=99=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/iot/device/index.vue | 570 ++++++++++++++++++++++++++++++++- 1 file changed, 560 insertions(+), 10 deletions(-) diff --git a/src/views/iot/device/index.vue b/src/views/iot/device/index.vue index 854b40c1..544e231f 100644 --- a/src/views/iot/device/index.vue +++ b/src/views/iot/device/index.vue @@ -145,12 +145,263 @@ link :type="isRowConnected(scope.row) ? 'warning' : 'success'" - - - - - - + + @@ -158,6 +409,7 @@ link :type="isRowConnected(scope.row) ? 'warning' : 'success'" import { getStrDictOptions, DICT_TYPE } from '@/utils/dict' import { dateFormatter } from '@/utils/formatTime' import download from '@/utils/download' +import request from '@/config/axios' import { DeviceApi, DeviceConnectParams, DeviceVO } from '@/api/iot/device' import DeviceForm from './DeviceForm.vue' import DeviceAttributeList from './components/DeviceAttributeList.vue' @@ -288,14 +540,30 @@ const handleExport = async () => { } } +interface DevicePointRuleVO { + id: number + identifier: string + fieldName: string + fieldRule: string + defaultValue: string + deviceId: number + createTime?: string | number | Date + ruleAttributeId?: number + ruleOperator?: string + ruleValue?: string | number +} + const attributeDeviceId = ref(undefined) const attributeDeviceName = ref('') +const deviceTabActive = ref('deviceAttribute') + const deviceAttributeTabLabel = computed(() => { - if (!attributeDeviceId.value) { - return '设备属性' - } - return attributeDeviceName.value ? `设备属性:${attributeDeviceName.value}` : '设备属性' + return '设备属性' +}) + +const deviceRuleTabLabel = computed(() => { + return '点位规则' }) const handleShowAttribute = (row: any) => { @@ -303,6 +571,288 @@ const handleShowAttribute = (row: any) => { attributeDeviceName.value = row?.deviceName ?? '' } +const ruleLoading = ref(false) +const ruleList = ref([]) +const ruleTotal = ref(0) +const ruleQueryParams = reactive({ + pageNo: 1, + pageSize: 10, + identifier: undefined as string | undefined, + fieldName: undefined as string | undefined, + fieldRule: undefined as string | undefined, + defaultValue: undefined as string | undefined, + deviceId: undefined as number | undefined, +}) +const ruleQueryFormRef = ref() + +const ruleAttributeOptions = ref([]) + +const getRuleList = async () => { + if (!attributeDeviceId.value) return + ruleLoading.value = true + try { + const params = { + ...ruleQueryParams, + deviceId: attributeDeviceId.value, + } + const data = await request.get({ url: '/iot/device-point-rules/page', params }) + const listData = Array.isArray((data as any)?.list) ? (data as any).list : (Array.isArray(data) ? data : []) + const totalData = (data as any)?.total ?? listData.length + ruleList.value = listData as DevicePointRuleVO[] + ruleTotal.value = totalData + } finally { + ruleLoading.value = false + } +} + +const handleRuleQuery = () => { + if (!attributeDeviceId.value) return + ruleQueryParams.pageNo = 1 + getRuleList() +} + +const resetRuleQuery = () => { + if (!attributeDeviceId.value) return + ruleQueryFormRef.value?.resetFields?.() + handleRuleQuery() +} + +const loadRuleAttributeOptions = async (deviceId: number) => { + try { + const res = await DeviceApi.getDeviceAttributePage({ pageNo: 1, pageSize: 1000, deviceId }) + const data = Array.isArray((res as any)?.list) ? (res as any).list : (Array.isArray(res) ? res : []) + ruleAttributeOptions.value = data as any[] + } catch { + ruleAttributeOptions.value = [] + } +} + +watch( + () => attributeDeviceId.value, + async (val) => { + ruleQueryParams.deviceId = val ?? undefined + if (!val) { + ruleList.value = [] + ruleTotal.value = 0 + ruleAttributeOptions.value = [] + return + } + ruleQueryParams.pageNo = 1 + await Promise.all([getRuleList(), loadRuleAttributeOptions(val)]) + } +) + +const ruleDialogVisible = ref(false) +const ruleFormLoading = ref(false) +const ruleFormRef = ref() +const ruleForm = reactive>({ + id: undefined, + identifier: '', + fieldName: '', + fieldRule: '', + defaultValue: '', + deviceId: undefined, + ruleAttributeId: undefined, + ruleOperator: undefined, + ruleValue: undefined, +}) + +const extraPointRules = ref< + Array<{ + id?: number + rule?: string + operator?: string + operatorRule?: string | number + }> +>([]) + +const runningRuleOptions = [ + { value: '1', label: '运行' }, + { value: '2', label: '待机中(不运行、没故障)' }, + { value: '3', label: '故障中(故障且待机)' }, + { value: '4', label: '报警中(故障且运行)' }, +] + +const alarmRuleOptions = [{ value: 'ALARM', label: '报警' }] + +const currentRuleOptions = computed(() => { + const id = (ruleForm.identifier || '').toString().toUpperCase() + if (id === 'RUNNING') return runningRuleOptions + if (id === 'ALARM') return alarmRuleOptions + return [] +}) + +const isRuleDisabled = (value: string, selfRule?: string | number) => { + if (!value) return false + const v = String(value) + const self = selfRule != null ? String(selfRule) : undefined + if (self === v) return false + if (ruleForm.fieldRule && String(ruleForm.fieldRule) === v && self !== v) return true + if ( + extraPointRules.value.some( + (item) => item.rule != null && String(item.rule) === v && String(item.rule) !== self + ) + ) { + return true + } + return false +} + +const ruleOperatorOptions = computed(() => getStrDictOptions('czsb_rules_conditions')) + +const isRunningIdentifier = computed(() => { + return (ruleForm.identifier || '').toString().toUpperCase() === 'RUNNING' +}) + +const openRuleForm = (row: DevicePointRuleVO & { pointRulesVOList?: any[] }) => { + ruleForm.id = row.id + ruleForm.identifier = row.identifier + ruleForm.fieldName = row.fieldName + ruleForm.defaultValue = row.defaultValue + ruleForm.deviceId = row.deviceId + + extraPointRules.value = [] + + const list = Array.isArray(row.pointRulesVOList) ? row.pointRulesVOList : [] + + if (list.length) { + const first = list[0] as any + ruleForm.fieldRule = (first.rule ?? row.fieldRule) as any + ruleForm.ruleAttributeId = first.id as any + ruleForm.ruleOperator = first.operator as any + ruleForm.ruleValue = first.operatorRule as any + extraPointRules.value = list.slice(1).map((item: any) => ({ + id: item.id, + rule: item.rule, + operator: item.operator, + operatorRule: item.operatorRule, + })) + } else { + ruleForm.fieldRule = row.fieldRule + ruleForm.ruleAttributeId = row.ruleAttributeId + ruleForm.ruleOperator = row.ruleOperator + ruleForm.ruleValue = row.ruleValue as any + } + + const options = currentRuleOptions.value + if (options.length && !options.some((item) => item.value === ruleForm.fieldRule)) { + ruleForm.fieldRule = options[0].value + } + ruleDialogVisible.value = true +} + +const handleRuleSubmit = async () => { + if (!ruleForm.id) return + try { + ruleFormLoading.value = true + const pointRulesVOList: any[] = [] + + if (ruleForm.ruleAttributeId && ruleForm.ruleOperator) { + pointRulesVOList.push({ + id: ruleForm.ruleAttributeId, + rule: ruleForm.fieldRule, + operator: ruleForm.ruleOperator, + operatorRule: ruleForm.ruleValue, + }) + } + + extraPointRules.value.forEach((item) => { + if (!item.id || !item.operator) return + pointRulesVOList.push({ + id: item.id, + rule: item.rule ?? ruleForm.fieldRule, + operator: item.operator, + operatorRule: item.operatorRule, + }) + }) + + const payload = { + id: ruleForm.id, + identifier: ruleForm.identifier, + fieldName: ruleForm.fieldName, + fieldRule: ruleForm.fieldRule, + defaultValue: ruleForm.defaultValue, + deviceId: ruleForm.deviceId ?? attributeDeviceId.value, + pointRulesVOList, + } + + await request.put({ url: '/iot/device-point-rules/update', data: payload }) + message.success('保存成功') + ruleDialogVisible.value = false + await getRuleList() + } finally { + ruleFormLoading.value = false + } +} + +const handleAddPointRule = () => { + if (!isRunningIdentifier.value) return + const allOptions = currentRuleOptions.value || [] + const used = new Set() + if (ruleForm.fieldRule) used.add(String(ruleForm.fieldRule)) + extraPointRules.value.forEach((item) => { + if (item.rule != null) used.add(String(item.rule)) + }) + const next = allOptions.find((opt) => !used.has(String(opt.value))) + if (!next) { + message.warning('已没有可用的点位规则可选') + return + } + extraPointRules.value.push({ + rule: next.value as any, + id: undefined, + operator: undefined, + operatorRule: undefined, + }) +} + +const handleRemovePointRule = (index: number) => { + if (index < 0 || index >= extraPointRules.value.length) return + extraPointRules.value.splice(index, 1) +} + +const createRuleDialogVisible = ref(false) +const createRuleFormLoading = ref(false) +const createRuleFormRef = ref() +const createRuleForm = reactive({ + identifier: 'ALARM', + fieldName: '', + defaultValue: '报警', +}) + +const openCreateRuleForm = () => { + if (!attributeDeviceId.value) { + message.error('请先选择设备') + return + } + createRuleForm.identifier = 'ALARM' + createRuleForm.fieldName = '报警' + createRuleForm.defaultValue = '报警' + createRuleDialogVisible.value = true +} + +const handleCreateRuleSubmit = async () => { + if (!attributeDeviceId.value) return + if (!createRuleForm.fieldName) { + message.warning('请输入名称') + return + } + try { + createRuleFormLoading.value = true + const payload = { + identifier: createRuleForm.identifier, + fieldName: createRuleForm.fieldName, + defaultValue: createRuleForm.defaultValue, + deviceId: attributeDeviceId.value, + } + await request.post({ url: '/iot/device-point-rules/create', data: payload }) + message.success('新增成功') + createRuleDialogVisible.value = false + await getRuleList() + } finally { + createRuleFormLoading.value = false + } +} const connectLoadingMap = reactive>({}) const getRowConnectValue = (row: any) => {