|
|
|
|
@ -17,23 +17,59 @@
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { ref, onMounted, onUnmounted } from 'vue'
|
|
|
|
|
import dayjs from 'dayjs'
|
|
|
|
|
import { useChart, colors } from '../utils'
|
|
|
|
|
import { DeviceOperationRecordApi, type DeviceOperationRecordVO } from '@/api/iot/deviceOperationRecord'
|
|
|
|
|
|
|
|
|
|
const { init, instance } = useChart('chart-today')
|
|
|
|
|
|
|
|
|
|
const lineMetrics = [
|
|
|
|
|
{ name: '产线A', run: 88, avail: 82 },
|
|
|
|
|
{ name: '产线B', run: 86, avail: 80 },
|
|
|
|
|
{ name: '产线C', run: 90, avail: 84 },
|
|
|
|
|
{ name: '产线D', run: 85, avail: 79 },
|
|
|
|
|
{ name: '产线E', run: 87, avail: 81 },
|
|
|
|
|
{ name: '产线F', run: 92, avail: 86 }
|
|
|
|
|
]
|
|
|
|
|
type LineMetric = { name: string; run: number; avail: number }
|
|
|
|
|
const lineMetrics = ref<LineMetric[]>([])
|
|
|
|
|
|
|
|
|
|
const currentLineName = ref(lineMetrics[0].name)
|
|
|
|
|
const currentLineName = ref('')
|
|
|
|
|
let timer: ReturnType<typeof setInterval> | null = null
|
|
|
|
|
let currentLineIndex = 0
|
|
|
|
|
|
|
|
|
|
const toRateNumber = (value: unknown) => {
|
|
|
|
|
if (typeof value === 'number') {
|
|
|
|
|
if (!Number.isFinite(value)) return 0
|
|
|
|
|
if (value >= 0 && value <= 1) return Math.round(value * 10000) / 100
|
|
|
|
|
return Math.max(0, Math.min(100, Math.round(value * 100) / 100))
|
|
|
|
|
}
|
|
|
|
|
const raw = String(value ?? '').trim()
|
|
|
|
|
if (!raw) return 0
|
|
|
|
|
const numeric = Number(raw.replace(/[^\d.+-]/g, ''))
|
|
|
|
|
if (!Number.isFinite(numeric)) return 0
|
|
|
|
|
if (raw.includes('%')) return Math.max(0, Math.min(100, Math.round(numeric * 100) / 100))
|
|
|
|
|
if (numeric >= 0 && numeric <= 1) return Math.round(numeric * 10000) / 100
|
|
|
|
|
return Math.max(0, Math.min(100, Math.round(numeric * 100) / 100))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const buildLineMetrics = (rows: DeviceOperationRecordVO[]) => {
|
|
|
|
|
const groupMap = new Map<string, { name: string; power: number[]; util: number[] }>()
|
|
|
|
|
rows.forEach((row) => {
|
|
|
|
|
const name = String((row as any)?.lineName ?? (row as any)?.lineCode ?? row.deviceName ?? row.deviceCode ?? '').trim()
|
|
|
|
|
if (!name) return
|
|
|
|
|
const power = toRateNumber((row as any)?.powerOnRate)
|
|
|
|
|
const util = toRateNumber((row as any)?.utilizationRate)
|
|
|
|
|
const key = name
|
|
|
|
|
if (!groupMap.has(key)) {
|
|
|
|
|
groupMap.set(key, { name, power: [], util: [] })
|
|
|
|
|
}
|
|
|
|
|
const g = groupMap.get(key)!
|
|
|
|
|
if (Number.isFinite(power)) g.power.push(power)
|
|
|
|
|
if (Number.isFinite(util)) g.util.push(util)
|
|
|
|
|
})
|
|
|
|
|
const avg = (arr: number[]) => (arr.length ? arr.reduce((s, v) => s + v, 0) / arr.length : 0)
|
|
|
|
|
const list = Array.from(groupMap.values()).map((g) => ({
|
|
|
|
|
name: g.name,
|
|
|
|
|
run: Math.round(avg(g.power) * 100) / 100,
|
|
|
|
|
avail: Math.round(avg(g.util) * 100) / 100
|
|
|
|
|
}))
|
|
|
|
|
list.sort((a, b) => a.name.localeCompare(b.name))
|
|
|
|
|
return list
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const gaugeOption = (run: number, avail: number) => {
|
|
|
|
|
return {
|
|
|
|
|
backgroundColor: 'transparent',
|
|
|
|
|
@ -80,25 +116,56 @@ const gaugeOption = (run: number, avail: number) => {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
const chart = init()
|
|
|
|
|
if (!chart) return
|
|
|
|
|
const renderCurrent = () => {
|
|
|
|
|
const list = lineMetrics.value
|
|
|
|
|
if (!list.length) {
|
|
|
|
|
currentLineName.value = '暂无数据'
|
|
|
|
|
const c = instance()
|
|
|
|
|
if (c) c.setOption(gaugeOption(0, 0))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
const idx = Math.max(0, Math.min(list.length - 1, currentLineIndex))
|
|
|
|
|
const item = list[idx]
|
|
|
|
|
currentLineName.value = item.name
|
|
|
|
|
const c = instance()
|
|
|
|
|
if (c) c.setOption(gaugeOption(item.run, item.avail))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initial render
|
|
|
|
|
chart.setOption(gaugeOption(lineMetrics[0].run, lineMetrics[0].avail))
|
|
|
|
|
const loadTodayOps = async () => {
|
|
|
|
|
const res = await DeviceOperationRecordApi.getDeviceOperationList({})
|
|
|
|
|
const rawList = Array.isArray(res) ? res : (res as any)?.list || (res as any)?.data || []
|
|
|
|
|
const list = Array.isArray(rawList) ? (rawList as DeviceOperationRecordVO[]) : []
|
|
|
|
|
lineMetrics.value = buildLineMetrics(list)
|
|
|
|
|
currentLineIndex = 0
|
|
|
|
|
renderCurrent()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Start rotation
|
|
|
|
|
const startRotate = () => {
|
|
|
|
|
if (timer) clearInterval(timer)
|
|
|
|
|
timer = null
|
|
|
|
|
if (lineMetrics.value.length <= 1) return
|
|
|
|
|
timer = setInterval(() => {
|
|
|
|
|
currentLineIndex = (currentLineIndex + 1) % lineMetrics.length
|
|
|
|
|
const item = lineMetrics[currentLineIndex]
|
|
|
|
|
currentLineName.value = item.name
|
|
|
|
|
const list = lineMetrics.value
|
|
|
|
|
if (!list.length) return
|
|
|
|
|
currentLineIndex = (currentLineIndex + 1) % list.length
|
|
|
|
|
renderCurrent()
|
|
|
|
|
}, 8000)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update chart
|
|
|
|
|
const c = instance()
|
|
|
|
|
if (c) {
|
|
|
|
|
c.setOption(gaugeOption(item.run, item.avail))
|
|
|
|
|
onMounted(async () => {
|
|
|
|
|
const chart = init()
|
|
|
|
|
if (!chart) return
|
|
|
|
|
|
|
|
|
|
chart.setOption(gaugeOption(0, 0))
|
|
|
|
|
try {
|
|
|
|
|
await loadTodayOps()
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('Failed to load today ops:', e)
|
|
|
|
|
lineMetrics.value = []
|
|
|
|
|
currentLineIndex = 0
|
|
|
|
|
renderCurrent()
|
|
|
|
|
}
|
|
|
|
|
}, 3000)
|
|
|
|
|
startRotate()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
|