Compare commits

...

2 Commits

@ -8,8 +8,14 @@ export interface DeviceOperationRecordVO {
totalStandbyTime: number totalStandbyTime: number
totalFaultTime: number totalFaultTime: number
totalWarningTime: number totalWarningTime: number
totalOfflineTime?: number
utilizationRate: string utilizationRate: string
powerOnRate?: string powerOnRate?: string
startTime?: string
endTime?: string
lineCode?: string
lineName?: string
deviceId?: number
} }
export interface DeviceOperationRecordPageParams { export interface DeviceOperationRecordPageParams {
@ -22,10 +28,23 @@ export interface DeviceOperationRecordPageParams {
ids?: string ids?: string
} }
export interface DeviceOperationListParams {
deviceCode?: string
deviceName?: string
startTime?: string
endTime?: string
lineCode?: string
lineName?: string
deviceId?: number | string
}
export const DeviceOperationRecordApi = { export const DeviceOperationRecordApi = {
getDeviceOperationRecordPage: async (params: DeviceOperationRecordPageParams) => { getDeviceOperationRecordPage: async (params: DeviceOperationRecordPageParams) => {
return await request.get({ url: `/iot/device-operation-record/deviceOperationPage`, params }) return await request.get({ url: `/iot/device-operation-record/deviceOperationPage`, params })
}, },
getDeviceOperationList: async (params?: DeviceOperationListParams) => {
return await request.get({ url: `/iot/device-operation-record/deviceOperationList`, params })
},
exportDeviceOperationReport: async (params: DeviceOperationRecordPageParams) => { exportDeviceOperationReport: async (params: DeviceOperationRecordPageParams) => {
return await request.download({ url: `/iot/device-operation-record/export-device-operation-report`, params }) return await request.download({ url: `/iot/device-operation-record/export-device-operation-report`, params })
} }

@ -45,6 +45,7 @@
:disabled-date="disableFutureDate" :disabled-date="disableFutureDate"
@change="(value) => handleDateRangeChange(group, value)" @change="(value) => handleDateRangeChange(group, value)"
/> />
<div class="mt-4px text-12px text-gray-500">最多选择 8 小时且不能选择未来日期</div>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>

@ -267,6 +267,18 @@ const parseIdsValue = (value: any): number[] => {
.filter((v) => !Number.isNaN(v)) .filter((v) => !Number.isNaN(v))
} }
const normalizeNumberish = (value: any): number | undefined => {
if (value === null || value === undefined || value === '') return undefined
if (typeof value === 'number') return Number.isFinite(value) ? value : undefined
if (typeof value === 'string') {
const trimmed = value.trim()
if (!trimmed) return undefined
const n = Number(trimmed)
return Number.isFinite(n) ? n : undefined
}
return undefined
}
const normalizeYmd = (value: any): string | undefined => { const normalizeYmd = (value: any): string | undefined => {
if (value === null || value === undefined || value === '') return undefined if (value === null || value === undefined || value === '') return undefined
if (typeof value === 'string') { if (typeof value === 'string') {
@ -475,6 +487,7 @@ const open = async (type: string, id?: number, defaultDeviceTypeId?: number) =>
formData.value = { formData.value = {
...initFormData(), ...initFormData(),
...(detail as any), ...(detail as any),
deviceType: normalizeNumberish((detail as any)?.deviceType),
deviceManagerIds: parseIdsValue((detail as any)?.deviceManager), deviceManagerIds: parseIdsValue((detail as any)?.deviceManager),
productionDate: normalizeYmd((detail as any)?.productionDate), productionDate: normalizeYmd((detail as any)?.productionDate),
factoryEntryDate: normalizeYmd((detail as any)?.factoryEntryDate), factoryEntryDate: normalizeYmd((detail as any)?.factoryEntryDate),
@ -503,6 +516,7 @@ const submitForm = async () => {
try { try {
const data = { const data = {
...(formData.value as any), ...(formData.value as any),
deviceType: normalizeNumberish(formData.value.deviceType),
productionDate: normalizeYmd(formData.value.productionDate), productionDate: normalizeYmd(formData.value.productionDate),
factoryEntryDate: normalizeYmd(formData.value.factoryEntryDate), factoryEntryDate: normalizeYmd(formData.value.factoryEntryDate),
deviceManager: formData.value.deviceManagerIds?.length ? formData.value.deviceManagerIds.join(',') : undefined, deviceManager: formData.value.deviceManagerIds?.length ? formData.value.deviceManagerIds.join(',') : undefined,

@ -17,23 +17,59 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue' import { ref, onMounted, onUnmounted } from 'vue'
import dayjs from 'dayjs'
import { useChart, colors } from '../utils' import { useChart, colors } from '../utils'
import { DeviceOperationRecordApi, type DeviceOperationRecordVO } from '@/api/iot/deviceOperationRecord'
const { init, instance } = useChart('chart-today') const { init, instance } = useChart('chart-today')
const lineMetrics = [ type LineMetric = { name: string; run: number; avail: number }
{ name: '产线A', run: 88, avail: 82 }, const lineMetrics = ref<LineMetric[]>([])
{ 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 }
]
const currentLineName = ref(lineMetrics[0].name) const currentLineName = ref('')
let timer: ReturnType<typeof setInterval> | null = null let timer: ReturnType<typeof setInterval> | null = null
let currentLineIndex = 0 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) => { const gaugeOption = (run: number, avail: number) => {
return { return {
backgroundColor: 'transparent', backgroundColor: 'transparent',
@ -80,25 +116,56 @@ const gaugeOption = (run: number, avail: number) => {
} }
} }
onMounted(() => { const renderCurrent = () => {
const chart = init() const list = lineMetrics.value
if (!chart) return 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 const loadTodayOps = async () => {
chart.setOption(gaugeOption(lineMetrics[0].run, lineMetrics[0].avail)) 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(() => { timer = setInterval(() => {
currentLineIndex = (currentLineIndex + 1) % lineMetrics.length const list = lineMetrics.value
const item = lineMetrics[currentLineIndex] if (!list.length) return
currentLineName.value = item.name currentLineIndex = (currentLineIndex + 1) % list.length
renderCurrent()
}, 8000)
}
// Update chart onMounted(async () => {
const c = instance() const chart = init()
if (c) { if (!chart) return
c.setOption(gaugeOption(item.run, item.avail))
} chart.setOption(gaugeOption(0, 0))
}, 3000) try {
await loadTodayOps()
} catch (e) {
console.error('Failed to load today ops:', e)
lineMetrics.value = []
currentLineIndex = 0
renderCurrent()
}
startRotate()
}) })
onUnmounted(() => { onUnmounted(() => {

Loading…
Cancel
Save