You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
haoliang-net/frontend/_screen_part2.vue

192 lines
6.8 KiB
Vue

<script setup lang="ts">
import { ref, reactive, onMounted, onUnmounted, nextTick } from 'vue'
import * as echarts from 'echarts'
import { ElMessage } from 'element-plus'
import request from '@/utils/request'
const filterWorkshop = ref('')
const filterBrand = ref('')
const workshopOptions = ref<string[]>([])
const brandOptions = ref<string[]>([])
const currentTime = ref('')
let timeTimer: ReturnType<typeof setInterval> | null = null
const statCards = reactive([
{ key: 'online', label: '在线机床', value: '--', sub: '', color: '#00e5ff' },
{ key: 'production', label: '今日总产量', value: '--', sub: '', color: '#76ff03' },
{ key: 'collector', label: '采集服务', value: '--', sub: '', color: '#69f0ae' },
{ key: 'alerts', label: '活跃告警', value: '--', sub: '', color: '#ffd740' },
{ key: 'avg', label: '平均产量/台', value: '--', sub: '', color: '#e0e0e0' },
])
const barChartRef = ref<HTMLElement>()
const lineChartRef = ref<HTMLElement>()
let barChart: echarts.ECharts | null = null
let lineChart: echarts.ECharts | null = null
const machineRank = ref<any[]>([])
const workerRank = ref<any[]>([])
const machineStatus = ref<any[]>([])
let refreshTimer: ReturnType<typeof setInterval> | null = null
const REFRESH_INTERVAL = 10000
function updateTime() {
const now = new Date()
currentTime.value = now.toLocaleString('zh-CN', {
year: 'numeric', month: '2-digit', day: '2-digit',
hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false,
})
}
function filterParams(): Record<string, string> {
const p: Record<string, string> = {}
if (filterWorkshop.value) p.workshop = filterWorkshop.value
if (filterBrand.value) p.brand = filterBrand.value
return p
}
async function loadFilters() {
try {
const r: any = await request.get('/screen/filters')
const items = r.data?.items || []
workshopOptions.value = items.filter((i: any) => i.filterType === 'workshop').map((i: any) => i.filterValue)
brandOptions.value = items.filter((i: any) => i.filterType === 'brand').map((i: any) => i.filterValue)
const defW = items.find((i: any) => i.filterType === 'workshop' && i.isDefault === 1)
if (defW) filterWorkshop.value = defW.filterValue
const defB = items.find((i: any) => i.filterType === 'brand' && i.isDefault === 1)
if (defB) filterBrand.value = defB.filterValue
} catch { /* 静默 */ }
}
async function loadSummary() {
try {
const r: any = await request.get('/screen/summary', { params: filterParams() })
const d = r.data || {}
statCards[0].value = d.onlineCount != null ? String(d.onlineCount) : '--'
statCards[0].sub = d.totalMachines ? '/ ' + d.totalMachines : ''
statCards[1].value = d.todayProduction != null ? d.todayProduction.toLocaleString() : '--'
statCards[3].value = d.activeAlerts != null ? String(d.activeAlerts) : '--'
statCards[4].value = d.avgQuantityPerMachine != null ? String(d.avgQuantityPerMachine) : '--'
} catch { /* 静默 */ }
}
async function loadCollectorStatus() {
try {
const r: any = await request.get('/screen/collector-status')
const d = r.data || {}
if (d.status === 'running') {
statCards[2].value = '运行中'
statCards[2].sub = d.uptime || ''
statCards[2].color = '#69f0ae'
} else {
statCards[2].value = '已停止'
statCards[2].sub = ''
statCards[2].color = '#ff5252'
}
} catch { /* 静默 */ }
}
async function loadWorkshopProduction() {
try {
const r: any = await request.get('/screen/workshop-production', { params: filterParams() })
const items = r.data?.items || []
if (!barChart) return
barChart.setOption({
tooltip: { trigger: 'axis' },
grid: { left: 60, right: 20, top: 20, bottom: 30 },
xAxis: { type: 'category', data: items.map((i: any) => i.name), axisLabel: { color: '#aaa' }, axisLine: { lineStyle: { color: '#333' } } },
yAxis: { type: 'value', axisLabel: { color: '#aaa' }, splitLine: { lineStyle: { color: '#222' } } },
series: [{ type: 'bar', data: items.map((i: any) => i.quantity), itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: '#00e5ff' }, { offset: 1, color: '#006064' }]) }, barWidth: 30 }],
})
} catch { /* 静默 */ }
}
async function loadProductionTrend() {
try {
const r: any = await request.get('/screen/production-trend', { params: filterParams() })
const items = r.data?.items || []
if (!lineChart) return
lineChart.setOption({
tooltip: { trigger: 'axis' },
grid: { left: 60, right: 20, top: 20, bottom: 30 },
xAxis: { type: 'category', data: items.map((i: any) => i.date), axisLabel: { color: '#aaa' }, axisLine: { lineStyle: { color: '#333' } } },
yAxis: { type: 'value', axisLabel: { color: '#aaa' }, splitLine: { lineStyle: { color: '#222' } } },
series: [{
type: 'line', data: items.map((i: any) => i.quantity), smooth: true,
lineStyle: { color: '#76ff03', width: 2 }, itemStyle: { color: '#76ff03' },
areaStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: 'rgba(118,255,3,0.3)' }, { offset: 1, color: 'rgba(118,255,3,0)' }]) },
}],
})
} catch { /* 静默 */ }
}
async function loadMachineRank() {
try {
const r: any = await request.get('/screen/machine-rank', { params: filterParams() })
machineRank.value = r.data?.items || []
} catch { /* 静默 */ }
}
async function loadWorkerRank() {
try {
const r: any = await request.get('/screen/worker-rank', { params: filterParams() })
workerRank.value = r.data?.items || []
} catch { /* 静默 */ }
}
async function loadMachineStatus() {
try {
const r: any = await request.get('/screen/machine-status', { params: filterParams() })
machineStatus.value = r.data?.items || []
} catch { /* 静默 */ }
}
function refreshAll() {
loadSummary()
loadCollectorStatus()
loadWorkshopProduction()
loadProductionTrend()
loadMachineRank()
loadWorkerRank()
loadMachineStatus()
}
function initCharts() {
if (barChartRef.value) {
barChart = echarts.init(barChartRef.value)
barChart.setOption({ backgroundColor: 'transparent' })
}
if (lineChartRef.value) {
lineChart = echarts.init(lineChartRef.value)
lineChart.setOption({ backgroundColor: 'transparent' })
}
}
function handleResize() {
barChart?.resize()
lineChart?.resize()
}
onMounted(async () => {
updateTime()
timeTimer = setInterval(updateTime, 1000)
ElMessage({ message: '按F11进入全屏模式', type: 'info', duration: 5000 })
await loadFilters()
await nextTick()
initCharts()
refreshAll()
refreshTimer = setInterval(refreshAll, REFRESH_INTERVAL)
window.addEventListener('resize', handleResize)
})
onUnmounted(() => {
if (timeTimer) clearInterval(timeTimer)
if (refreshTimer) clearInterval(refreshTimer)
barChart?.dispose()
lineChart?.dispose()
window.removeEventListener('resize', handleResize)
})
</script>