diff --git a/src/api/iot/device/index.ts b/src/api/iot/device/index.ts index 5afaad3b..dac795b3 100644 --- a/src/api/iot/device/index.ts +++ b/src/api/iot/device/index.ts @@ -74,6 +74,7 @@ export interface DeviceContactModelVO { dataUnit?: string } + // 物联设备 API export const DeviceApi = { // 查询物联设备分页 @@ -130,6 +131,10 @@ export const DeviceApi = { return await request.get({ url: `/iot/device/historyRecord`, params }) }, + devicePointList: async () => { + return await request.get({ url: `/iot/device/devicePointList` }) + }, + // ==================== 子表(设备属性) ==================== // 获得设备属性分页 diff --git a/src/api/iot/devicemodelattribute/index.ts b/src/api/iot/devicemodelattribute/index.ts index 8694e034..6c45c8b1 100644 --- a/src/api/iot/devicemodelattribute/index.ts +++ b/src/api/iot/devicemodelattribute/index.ts @@ -48,7 +48,7 @@ export const DeviceModelAttributeApi = { operationAnalysisDetails: async (params: { deviceId: number - modelId: number + modelId?: number collectionStartTime?: string collectionEndTime?: string }) => { diff --git a/src/api/mes/organization/index.ts b/src/api/mes/organization/index.ts index 738ea9b3..fb0b8a1b 100644 --- a/src/api/mes/organization/index.ts +++ b/src/api/mes/organization/index.ts @@ -19,7 +19,9 @@ export interface OrganizationVO { export type DeviceParameterAnalysisNodeVO = { id: number | string name: string + orgClass?: string parentId?: number | string + children?: DeviceParameterAnalysisNodeVO[] equipments?: { id: number | string name: string @@ -70,7 +72,7 @@ export const OrganizationApi = { return await request.download({ url: `/mes/organization/export-excel`, params }) }, - deviceParameterAnalysis: async (params: { keyword?: string }) => { + deviceParameterAnalysis: async (params: { keyword?: string, showDevices?: number }) => { return await request.get({ url: `/mes/organization/deviceParameterAnalysis`, params }) } } diff --git a/src/views/iot/deviceParamAnalysis/index.vue b/src/views/iot/deviceParamAnalysis/index.vue index 45dc7b84..74226294 100644 --- a/src/views/iot/deviceParamAnalysis/index.vue +++ b/src/views/iot/deviceParamAnalysis/index.vue @@ -2,23 +2,11 @@ - +
+ref="treeRef" v-loading="treeLoading" :data="treeData" :props="treeProps" node-key="id" + highlight-current @node-click="handleTreeNodeClick" />
@@ -28,15 +16,9 @@ +v-model="dateRange" type="daterange" start-placeholder="开始日期" end-placeholder="结束日期" + value-format="YYYY-MM-DD" :shortcuts="dateShortcuts" + :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" class="!w-360px" /> @@ -50,22 +32,11 @@ - +
- - - + + +
@@ -93,6 +64,7 @@ type DeviceTreeNode = { modelId?: number paramKey?: string unit?: string + paramCount?: number } type ApiTreeParameter = { @@ -111,6 +83,7 @@ type ApiTreeEquipment = { type ApiTreeOrg = { id: number name: string + orgClass?: string parentId?: number | null equipments?: ApiTreeEquipment[] children?: ApiTreeOrg[] @@ -181,7 +154,7 @@ const selectedParamTitle = computed(() => { const param = selectedParam.value if (!param) return '' const unitText = param.unit ? `(${param.unit})` : '' - return `参数:${param.label}${unitText}` + return `节点:${param.label}${unitText}` }) const chartOption = computed(() => { @@ -213,17 +186,39 @@ let keywordTimer: number | undefined const handleKeywordChange = () => { if (keywordTimer) window.clearTimeout(keywordTimer) keywordTimer = window.setTimeout(() => { + keywordTimer = undefined loadTree() }, 300) } +const toFiniteId = (value: any): number | undefined => { + if (typeof value === 'number') return Number.isFinite(value) ? value : undefined + if (typeof value === 'string') { + if (/^\d+$/.test(value.trim())) return Number(value) + const n = Number(value) + return Number.isFinite(n) ? n : undefined + } + return undefined +} + const buildTreeFromApi = (orgs: ApiTreeOrg[]): DeviceTreeNode[] => { - const normalizedOrgs: ApiTreeOrg[] = (Array.isArray(orgs) ? orgs : []).map((o) => ({ - ...o, - parentId: typeof o?.parentId === 'number' ? o.parentId : Number(o?.parentId ?? 0) || 0 - })) + const normalizeOrg = (org: any): ApiTreeOrg => { + const children = Array.isArray(org?.children) ? org.children.map(normalizeOrg) : undefined + const equipments = Array.isArray(org?.equipments) ? org.equipments : undefined + const parentId = typeof org?.parentId === 'number' ? org.parentId : Number(org?.parentId ?? 0) || 0 + return { + id: Number(org?.id ?? 0) || 0, + name: String(org?.name ?? ''), + orgClass: org?.orgClass ? String(org.orgClass) : undefined, + parentId, + equipments, + children + } + } - const orgTree = handleTree(normalizedOrgs, 'id', 'parentId', 'children') as ApiTreeOrg[] + const normalizedOrgs: ApiTreeOrg[] = Array.isArray(orgs) ? orgs.map(normalizeOrg) : [] + const isNestedTree = normalizedOrgs.some((o) => typeof (o as any)?.children !== 'undefined') + const orgTree = isNestedTree ? normalizedOrgs : (handleTree(normalizedOrgs, 'id', 'parentId', 'children') as ApiTreeOrg[]) const toOrgNode = (org: ApiTreeOrg): DeviceTreeNode => { const orgChildren = Array.isArray(org?.children) ? org.children.map(toOrgNode) : [] @@ -235,8 +230,8 @@ const buildTreeFromApi = (orgs: ApiTreeOrg[]): DeviceTreeNode[] => { id: `param-${eq.id}-${p.id}`, label: p?.name ?? p?.code ?? String(p?.id ?? ''), type: 'param', - deviceId: Number(eq.id), - modelId: Number(p.id), + deviceId: toFiniteId(eq.id), + modelId: toFiniteId(p.id), paramKey: p?.code ? String(p.code) : undefined, unit: p?.unit ? String(p.unit) : undefined })) @@ -245,6 +240,8 @@ const buildTreeFromApi = (orgs: ApiTreeOrg[]): DeviceTreeNode[] => { id: `equipment-${org.id}-${eq.id}`, label: eq?.name ?? String(eq?.id ?? ''), type: 'device', + deviceId: toFiniteId(eq.id), + paramCount: params.length, children: paramNodes.length ? paramNodes : undefined } }) @@ -261,11 +258,19 @@ const buildTreeFromApi = (orgs: ApiTreeOrg[]): DeviceTreeNode[] => { return Array.isArray(orgTree) ? orgTree.map(toOrgNode) : [] } +const extractApiOrgs = (res: any): ApiTreeOrg[] => { + if (Array.isArray(res)) return res as ApiTreeOrg[] + if (Array.isArray(res?.data)) return res.data as ApiTreeOrg[] + if (Array.isArray(res?.result)) return res.result as ApiTreeOrg[] + if (Array.isArray(res?.list)) return res.list as ApiTreeOrg[] + return [] +} + const loadTree = async () => { treeLoading.value = true try { - const data = await OrganizationApi.deviceParameterAnalysis({ keyword: keyword.value || undefined }) - treeData.value = buildTreeFromApi(Array.isArray(data) ? (data as ApiTreeOrg[]) : []) + const res = await OrganizationApi.deviceParameterAnalysis({ keyword: keyword.value || undefined, showDevices: 1 }) + treeData.value = buildTreeFromApi(extractApiOrgs(res)) if (keyword.value) { treeRef.value?.setCurrentKey?.(undefined) selectedParam.value = null @@ -311,7 +316,7 @@ const toNumber = (value: any) => { const fetchChart = async () => { const param = selectedParam.value - if (!selectedDeviceId.value || !selectedModelId.value) return + if (typeof selectedDeviceId.value !== 'number' || !Number.isFinite(selectedDeviceId.value)) return ensureDateRange() const [start, end] = dateRange.value @@ -325,17 +330,21 @@ const fetchChart = async () => { chartLoading.value = true resetChartData() try { - const data = await DeviceModelAttributeApi.operationAnalysisDetails({ + const req: Record = { deviceId: selectedDeviceId.value, - modelId: selectedModelId.value, collectionStartTime: buildDateTime(start, '00:00:00'), collectionEndTime: buildDateTime(end, '23:59:59') - }) + } + if (typeof selectedModelId.value === 'number' && Number.isFinite(selectedModelId.value)) { + req.modelId = selectedModelId.value + } + const data = await DeviceModelAttributeApi.operationAnalysisDetails(req as any) const rows: Record[] = Array.isArray(data) ? data : [] if (!rows.length) { chartXAxis.value = [] chartSeries.value = [] chartState.value = 'empty' + message.warning('该节点下暂无参数') return } @@ -390,16 +399,50 @@ const fetchChart = async () => { } const handleTreeNodeClick = async (data: DeviceTreeNode) => { - if (data.type !== 'param') return + if (keywordTimer) { + window.clearTimeout(keywordTimer) + keywordTimer = undefined + } + + const hasChildren = Array.isArray(data?.children) && data.children.length > 0 + if (hasChildren) { + return + } + + const isEquipmentNode = typeof data?.id === 'string' && data.id.startsWith('equipment-') + if (isEquipmentNode && (data.paramCount ?? 0) <= 0) { + selectedParam.value = data + selectedDeviceId.value = undefined + selectedModelId.value = undefined + chartState.value = 'empty' + resetChartData() + message.warning('该设备下没有参数') + return + } + + const toNodeIds = (node: DeviceTreeNode): { deviceId?: number; modelId?: number } => { + const modelId = typeof node.modelId === 'number' && Number.isFinite(node.modelId) ? node.modelId : undefined + const deviceId = typeof node.deviceId === 'number' && Number.isFinite(node.deviceId) ? node.deviceId : undefined + if (typeof deviceId === 'number') return { deviceId, modelId } + + const parts = String(node.id ?? '').split('-').filter(Boolean) + const last = parts.length ? parts[parts.length - 1] : undefined + const parsed = toFiniteId(last) + if (typeof parsed !== 'number' || !Number.isFinite(parsed) || parsed <= 0) { + return { modelId } + } + return { deviceId: parsed, modelId } + } + + const { deviceId, modelId } = toNodeIds(data) selectedParam.value = data - selectedDeviceId.value = data.deviceId - selectedModelId.value = data.modelId + selectedDeviceId.value = deviceId + selectedModelId.value = modelId dateRange.value = buildDefaultDateRange() await fetchChart() } const handleQuery = async () => { - if (!selectedParam.value) return await fetchChart() } diff --git a/src/views/mes/energydevice/EnergyDeviceForm.vue b/src/views/mes/energydevice/EnergyDeviceForm.vue index 37d642f4..ea720e55 100644 --- a/src/views/mes/energydevice/EnergyDeviceForm.vue +++ b/src/views/mes/energydevice/EnergyDeviceForm.vue @@ -1,12 +1,6 @@ @@ -107,8 +67,9 @@