From 41d1219da4aa2a3ed518bad3f1866bf69373f751 Mon Sep 17 00:00:00 2001 From: hwj Date: Wed, 21 Jan 2026 18:19:18 +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=A8=A1=E5=9E=8B-=E6=B7=BB=E5=8A=A0=E7=82=B9?= =?UTF-8?q?=E4=BD=8D=E8=A7=84=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/api/iot/devicemodelattribute/index.ts | 5 + src/views/iot/devicemodel/index.vue | 491 +++++++++++++++++++++- src/views/mes/zjitem/index.vue | 15 +- 3 files changed, 497 insertions(+), 14 deletions(-) diff --git a/src/api/iot/devicemodelattribute/index.ts b/src/api/iot/devicemodelattribute/index.ts index 6c45c8b1..c09b6507 100644 --- a/src/api/iot/devicemodelattribute/index.ts +++ b/src/api/iot/devicemodelattribute/index.ts @@ -21,6 +21,11 @@ export const DeviceModelAttributeApi = { return await request.get({ url: `/iot/device-model-attribute/page`, params }) }, + // 查询采集设备模型-点位管理列表(当前设备模型下的点位) + getDeviceModelAttributeList: async (id: number) => { + return await request.get({ url: `/iot/device-model-attribute/list`, params: { id } }) + }, + // 查询采集设备模型-点位管理详情 getDeviceModelAttribute: async (id: number) => { return await request.get({ url: `/iot/device-model-attribute/get?id=` + id }) diff --git a/src/views/iot/devicemodel/index.vue b/src/views/iot/devicemodel/index.vue index d0b892a3..1b8cf46c 100644 --- a/src/views/iot/devicemodel/index.vue +++ b/src/views/iot/devicemodel/index.vue @@ -102,12 +102,221 @@ link type="primary" @click="openForm('update', scope.row.id)" - - - - - - + + @@ -117,7 +326,9 @@ link type="primary" @click="openForm('update', scope.row.id)" import { getStrDictOptions, DICT_TYPE } from '@/utils/dict' import { dateFormatter } from '@/utils/formatTime' import download from '@/utils/download' +import request from '@/config/axios' import { DeviceModelApi, DeviceModelVO } from '@/api/iot/devicemodel' +import { DeviceModelAttributeApi, DeviceModelAttributeVO } from '@/api/iot/devicemodelattribute' import DeviceModelForm from './DeviceModelForm.vue' import ModelAttributeList from './components/ModelAttributeList.vue' @@ -229,12 +440,274 @@ const attributeModelId = ref(undefined) const attributeModelName = ref('') const modelAttributeTabLabel = computed(() => { - if (!attributeModelId.value) { - return '采集点' + return '采集点' +}) + +const modelRuleTabLabel = computed(() => { + return '点位规则' +}) + +interface DeviceModelRuleVO { + id: number + identifier: string + fieldName: string + fieldRule: string + defaultValue: string + modelId: number + createTime?: string | number | Date + ruleAttributeId?: number + ruleOperator?: string + ruleValue?: string | number +} + +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, + modelId: undefined as number | undefined, +}) +const ruleQueryFormRef = ref() + +const ruleAttributeOptions = ref([]) + +const getRuleList = async () => { + if (!attributeModelId.value) return + ruleLoading.value = true + try { + const params = { + ...ruleQueryParams, + modelId: attributeModelId.value, + } + const data = await request.get({ url: '/iot/device-model-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 DeviceModelRuleVO[] + ruleTotal.value = totalData + } finally { + ruleLoading.value = false } - return attributeModelName.value ? `采集点:${attributeModelName.value}` : '采集点' +} + +const handleRuleQuery = () => { + if (!attributeModelId.value) return + ruleQueryParams.pageNo = 1 + getRuleList() +} + +const resetRuleQuery = () => { + if (!attributeModelId.value) return + ruleQueryFormRef.value?.resetFields?.() + handleRuleQuery() +} + +const loadRuleAttributeOptions = async (modelId: number) => { + try { + const res = await DeviceModelAttributeApi.getDeviceModelAttributeList(modelId) + const data = Array.isArray(res) ? res : (res as any)?.list ?? [] + ruleAttributeOptions.value = data as DeviceModelAttributeVO[] + } catch { + ruleAttributeOptions.value = [] + } +} + +watch( + () => attributeModelId.value, + async (val) => { + ruleQueryParams.modelId = 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: '', + modelId: 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 = [ + { value: 'TRUE', label: '为真' }, + { value: 'FALSE', label: '为假' }, + { value: 'EQ', label: '等于' }, + { value: 'LE', label: '小于等于' }, + { value: 'GE', label: '大于等于' }, +] + +const isRunningIdentifier = computed(() => { + return (ruleForm.identifier || '').toString().toUpperCase() === 'RUNNING' +}) + +const openRuleForm = (row: DeviceModelRuleVO & { pointRulesVOList?: any[] }) => { + ruleForm.id = row.id + ruleForm.identifier = row.identifier + ruleForm.fieldName = row.fieldName + ruleForm.defaultValue = row.defaultValue + ruleForm.modelId = row.modelId + + 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, + modelId: ruleForm.modelId, + pointRulesVOList, + } + + await request.put({ url: '/iot/device-model-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 handleShowAttribute = (row: any) => { attributeModelId.value = row?.id attributeModelName.value = row?.name ?? '' diff --git a/src/views/mes/zjitem/index.vue b/src/views/mes/zjitem/index.vue index 30e35277..8da9e339 100644 --- a/src/views/mes/zjitem/index.vue +++ b/src/views/mes/zjitem/index.vue @@ -8,15 +8,18 @@ - - - @@ -30,7 +33,8 @@ 新增 - 导出 @@ -67,7 +71,8 @@ -