feat:修改

This commit is contained in:
2026-05-19 19:26:41 +08:00
parent 4434480f32
commit 2030e68ff9
12 changed files with 1554 additions and 921 deletions

View File

@@ -242,33 +242,135 @@ app.get('/wms/acid-rolling/dashboard/overview', async (req, res) => {
return
}
try {
// 1. 获取当前班次OEE数据
const [shiftRows] = await acidPool.execute(
'SELECT * FROM klptcm1_shift_current ORDER BY create_time DESC LIMIT 1'
)
const currentShift = shiftRows[0] || {}
// 2. 获取今日产出统计
const [coilRows] = await acidPool.execute(
'SELECT COUNT(*) as count, SUM(weight) as totalWeight FROM klptcm1_pdo_excoil WHERE DATE(create_time) = CURDATE()'
`SELECT
COUNT(*) as count,
SUM(weight) as totalWeight,
SUM(CASE WHEN quality_status = 'A' THEN 1 ELSE 0 END) as qualifiedCount
FROM klptcm1_pdo_excoil
WHERE DATE(create_time) = CURDATE()`
)
// 3. 计算OEE指标
const totalCoils = coilRows[0]?.count || 0
const qualifiedCoils = coilRows[0]?.qualifiedCount || 0
const qualityRate = totalCoils > 0 ? (qualifiedCoils / totalCoils * 100) : 0
const availabilityRate = currentShift.availability || 92.1
const performanceRate = currentShift.performance || 89.8
const oeeValue = (availabilityRate * performanceRate * qualityRate / 10000).toFixed(1)
// 4. 获取OEE趋势数据最近7天
const [trendingRows] = await acidPool.execute(
`SELECT
DATE(create_time) as date,
AVG(oee) as oee,
AVG(availability) as availability,
AVG(performance) as performance,
COUNT(*) as coilCount
FROM klptcm1_shift_current
WHERE create_time >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
GROUP BY DATE(create_time)
ORDER BY date ASC`
)
// 5. 获取7大损失分布按停机类型统计
const [lossRows] = await acidPool.execute(
'SELECT loss_name as name, SUM(loss_time) as value FROM klptcm1_pro_stoppage WHERE DATE(create_time) = CURDATE() GROUP BY loss_name'
`SELECT
stop_type as name,
SUM(duration) as value
FROM klptcm1_pro_stoppage
WHERE create_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY stop_type
ORDER BY value DESC`
)
const latest = shiftRows[0] || {}
// 6. 获取班组产量排名
const [teamRows] = await acidPool.execute(
`SELECT
crew as name,
COUNT(*) as coilCount,
SUM(weight) as output
FROM klptcm1_pdo_excoil
WHERE create_time >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
GROUP BY crew
ORDER BY output DESC`
)
// 7. 获取实时告警(停机异常、质量不合格)
const [alarmRows] = await acidPool.execute(
`SELECT
'停机告警' as type,
stop_type as message,
DATE_FORMAT(create_time, '%H:%i:%s') as time,
CASE
WHEN duration > 3600 THEN 'danger'
WHEN duration > 1800 THEN 'warning'
ELSE 'info'
END as level
FROM klptcm1_pro_stoppage
WHERE create_time >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
ORDER BY create_time DESC
LIMIT 5`
)
// 8. 获取质量告警
const [qualityAlarmRows] = await acidPool.execute(
`SELECT
'质量告警' as type,
CONCAT('钢卷 ', coil_id, ' 质量异常') as message,
DATE_FORMAT(in_date, '%H:%i:%s') as time,
'warning' as level
FROM klptcm1_pdo_excoil
WHERE quality_status IN ('B', 'C', 'D')
AND DATE(in_date) = CURDATE()
ORDER BY in_date DESC
LIMIT 3`
)
const overview = {
oee: latest.oee || 86.5,
availability: latest.availability || 92.1,
performance: latest.performance || 89.8,
quality: latest.quality || 97.5,
totalOutput: coilRows[0]?.count || mockOverview.totalOutput,
totalWeight: coilRows[0]?.totalWeight || mockOverview.totalWeight,
// 核心指标
oee: parseFloat(oeeValue),
availability: availabilityRate,
performance: performanceRate,
quality: qualityRate.toFixed(1),
totalOutput: totalCoils,
totalWeight: coilRows[0]?.totalWeight || 0,
targetOutput: 15000,
efficiency: 92.0,
trendingData: mockOverview.trendingData,
lossData: lossRows.length > 0 ? lossRows : mockOverview.lossData,
teamRanking: mockOverview.teamRanking,
alarms: mockOverview.alarms
// OEE趋势数据
trendingData: trendingRows.length > 0 ? trendingRows.map(row => ({
date: row.date ? row.date.toString().substring(5) : '',
oee: parseFloat((row.oee || 0).toFixed(1)),
availability: parseFloat((row.availability || 0).toFixed(1)),
performance: parseFloat((row.performance || 0).toFixed(1)),
coilCount: row.coilCount || 0
})) : mockOverview.trendingData,
// 7大损失分布
lossData: lossRows.length > 0 ? lossRows.map(row => ({
name: row.name || '未知损失',
value: row.value || 0
})) : mockOverview.lossData,
// 班组产量排名
teamRanking: teamRows.length > 0 ? teamRows.map(row => ({
name: row.name || '未知班组',
output: Math.round(row.output || 0),
coilCount: row.coilCount || 0,
rate: 96.5
})) : mockOverview.teamRanking,
// 告警信息
alarms: [...alarmRows, ...qualityAlarmRows].map(row => ({
type: row.type,
message: row.message,
time: row.time,
level: row.level
}))
}
sendResponse(res, overview)
@@ -378,6 +480,331 @@ app.get('/wms/acid-rolling/report/stop', async (req, res) => {
}
})
// ==================== 订单数据接口 ====================
app.get('/api/dashboard/order', async (req, res) => {
if (!masterPool) {
sendResponse(res, {
todayOrderCount: 45,
pendingOrderCount: 12,
completedOrderCount: 156,
orderTotalAmount: 568,
orderTrend: [
{ date: '05-11', count: 38, amount: 420 },
{ date: '05-12', count: 42, amount: 480 },
{ date: '05-13', count: 35, amount: 390 },
{ date: '05-14', count: 48, amount: 520 },
{ date: '05-15', count: 45, amount: 568 }
],
statusDistribution: [
{ name: '生产中', value: 45 },
{ name: '待生产', value: 12 },
{ name: '已完成', value: 156 }
],
recentOrders: [
{ orderNo: 'ORD20260515001', customer: '周口钢铁', amount: 125000, status: '生产中', time: '10:30' },
{ orderNo: 'ORD20260515002', customer: '南阳重工', amount: 89000, status: '已完成', time: '09:45' },
{ orderNo: 'ORD20260515003', customer: '洛阳机械', amount: 156000, status: '待生产', time: '11:20' },
{ orderNo: 'ORD20260515004', customer: '开封汽配', amount: 67000, status: '生产中', time: '08:15' },
{ orderNo: 'ORD20260515005', customer: '商丘金属', amount: 45000, status: '已完成', time: '07:30' }
]
})
return
}
try {
const [todayOrders] = await masterPool.execute(
'SELECT COUNT(*) as count FROM klp_order WHERE DATE(create_time) = CURDATE()'
)
const [pendingOrders] = await masterPool.execute(
'SELECT COUNT(*) as count FROM klp_order WHERE status = "pending"'
)
const [completedOrders] = await masterPool.execute(
'SELECT COUNT(*) as count FROM klp_order WHERE status = "completed"'
)
const [totalAmount] = await masterPool.execute(
'SELECT SUM(amount) as total FROM klp_order WHERE DATE(create_time) = CURDATE()'
)
const [trendData] = await masterPool.execute(
'SELECT DATE(create_time) as date, COUNT(*) as count, SUM(amount) as amount FROM klp_order GROUP BY DATE(create_time) ORDER BY date DESC LIMIT 7'
)
const [statusData] = await masterPool.execute(
'SELECT status, COUNT(*) as count FROM klp_order GROUP BY status'
)
const [recentOrders] = await masterPool.execute(
'SELECT order_no as orderNo, customer, amount, status, TIME(create_time) as time FROM klp_order ORDER BY create_time DESC LIMIT 5'
)
sendResponse(res, {
todayOrderCount: todayOrders[0]?.count || 0,
pendingOrderCount: pendingOrders[0]?.count || 0,
completedOrderCount: completedOrders[0]?.count || 0,
orderTotalAmount: (totalAmount[0]?.total || 0) / 10000,
orderTrend: trendData.map(row => ({
date: row.date?.substring(5) || '',
count: row.count || 0,
amount: Math.round((row.amount || 0) / 10000)
})),
statusDistribution: statusData.map(row => ({
name: row.status === 'pending' ? '待生产' : row.status === 'processing' ? '生产中' : '已完成',
value: row.count || 0
})),
recentOrders: recentOrders.map(row => ({
orderNo: row.orderNo || '',
customer: row.customer || '',
amount: row.amount || 0,
status: row.status === 'pending' ? '待生产' : row.status === 'processing' ? '生产中' : '已完成',
time: row.time?.substring(0, 5) || ''
}))
})
} catch (err) {
console.error('订单数据查询失败:', err)
sendResponse(res, {
todayOrderCount: 45,
pendingOrderCount: 12,
completedOrderCount: 156,
orderTotalAmount: 568,
orderTrend: [
{ date: '05-11', count: 38, amount: 420 },
{ date: '05-12', count: 42, amount: 480 },
{ date: '05-13', count: 35, amount: 390 },
{ date: '05-14', count: 48, amount: 520 },
{ date: '05-15', count: 45, amount: 568 }
],
statusDistribution: [
{ name: '生产中', value: 45 },
{ name: '待生产', value: 12 },
{ name: '已完成', value: 156 }
],
recentOrders: [
{ orderNo: 'ORD20260515001', customer: '周口钢铁', amount: 125000, status: '生产中', time: '10:30' },
{ orderNo: 'ORD20260515002', customer: '南阳重工', amount: 89000, status: '已完成', time: '09:45' },
{ orderNo: 'ORD20260515003', customer: '洛阳机械', amount: 156000, status: '待生产', time: '11:20' },
{ orderNo: 'ORD20260515004', customer: '开封汽配', amount: 67000, status: '生产中', time: '08:15' },
{ orderNo: 'ORD20260515005', customer: '商丘金属', amount: 45000, status: '已完成', time: '07:30' }
]
})
}
})
// ==================== 成本数据接口 ====================
app.get('/api/dashboard/cost', async (req, res) => {
if (!acidPool) {
sendResponse(res, {
totalCost: 156.8,
materialCost: 89.5,
laborCost: 32.6,
energyCost: 24.7,
costTrend: [
{ month: '1月', total: 142, material: 82, labor: 30, energy: 22 },
{ month: '2月', total: 138, material: 80, labor: 31, energy: 21 },
{ month: '3月', total: 152, material: 88, labor: 33, energy: 25 },
{ month: '4月', total: 149, material: 86, labor: 32, energy: 24 },
{ month: '5月', total: 156.8, material: 89.5, labor: 32.6, energy: 24.7 }
],
costComposition: [
{ name: '材料成本', value: 57.1 },
{ name: '人工成本', value: 20.8 },
{ name: '能源成本', value: 15.7 },
{ name: '其他成本', value: 6.4 }
],
analysisList: [
{ label: '材料成本', value: '89.5万', change: 3.2, color: '#ff6b6b' },
{ label: '人工成本', value: '32.6万', change: -1.5, color: '#00d4ff' },
{ label: '能源成本', value: '24.7万', change: 5.8, color: '#00ff88' },
{ label: '总成本', value: '156.8万', change: 2.1, color: '#7c63ff' }
]
})
return
}
try {
const [costData] = await acidPool.execute(
'SELECT total_cost, material_cost, labor_cost, energy_cost, stat_month FROM klptcm1_cost_month ORDER BY stat_month DESC LIMIT 5'
)
const latest = costData[0] || {}
const costTrend = costData.map(row => ({
month: row.stat_month?.substring(5) + '月' || '',
total: row.total_cost || 0,
material: row.material_cost || 0,
labor: row.labor_cost || 0,
energy: row.energy_cost || 0
}))
const total = latest.total_cost || 0
sendResponse(res, {
totalCost: total,
materialCost: latest.material_cost || 0,
laborCost: latest.labor_cost || 0,
energyCost: latest.energy_cost || 0,
costTrend: costTrend,
costComposition: [
{ name: '材料成本', value: total > 0 ? Math.round((latest.material_cost || 0) / total * 1000) / 10 : 57.1 },
{ name: '人工成本', value: total > 0 ? Math.round((latest.labor_cost || 0) / total * 1000) / 10 : 20.8 },
{ name: '能源成本', value: total > 0 ? Math.round((latest.energy_cost || 0) / total * 1000) / 10 : 15.7 },
{ name: '其他成本', value: total > 0 ? 100 - Math.round(((latest.material_cost || 0) + (latest.labor_cost || 0) + (latest.energy_cost || 0)) / total * 1000) / 10 : 6.4 }
],
analysisList: [
{ label: '材料成本', value: (latest.material_cost || 89.5) + '万', change: 3.2, color: '#ff6b6b' },
{ label: '人工成本', value: (latest.labor_cost || 32.6) + '万', change: -1.5, color: '#00d4ff' },
{ label: '能源成本', value: (latest.energy_cost || 24.7) + '万', change: 5.8, color: '#00ff88' },
{ label: '总成本', value: total + '万', change: 2.1, color: '#7c63ff' }
]
})
} catch (err) {
console.error('成本数据查询失败:', err)
sendResponse(res, {
totalCost: 156.8,
materialCost: 89.5,
laborCost: 32.6,
energyCost: 24.7,
costTrend: [
{ month: '1月', total: 142, material: 82, labor: 30, energy: 22 },
{ month: '2月', total: 138, material: 80, labor: 31, energy: 21 },
{ month: '3月', total: 152, material: 88, labor: 33, energy: 25 },
{ month: '4月', total: 149, material: 86, labor: 32, energy: 24 },
{ month: '5月', total: 156.8, material: 89.5, labor: 32.6, energy: 24.7 }
],
costComposition: [
{ name: '材料成本', value: 57.1 },
{ name: '人工成本', value: 20.8 },
{ name: '能源成本', value: 15.7 },
{ name: '其他成本', value: 6.4 }
],
analysisList: [
{ label: '材料成本', value: '89.5万', change: 3.2, color: '#ff6b6b' },
{ label: '人工成本', value: '32.6万', change: -1.5, color: '#00d4ff' },
{ label: '能源成本', value: '24.7万', change: 5.8, color: '#00ff88' },
{ label: '总成本', value: '156.8万', change: 2.1, color: '#7c63ff' }
]
})
}
})
// ==================== 能源数据接口 ====================
app.get('/api/dashboard/energy', async (req, res) => {
if (!acidPool) {
sendResponse(res, {
totalPower: 12560,
waterUsage: 856,
gasUsage: 325,
steamUsage: 156,
powerTrend: [
{ hour: '08:00', power: 1200, water: 85, gas: 32, steam: 15 },
{ hour: '09:00', power: 1350, water: 92, gas: 35, steam: 18 },
{ hour: '10:00', power: 1280, water: 88, gas: 33, steam: 16 },
{ hour: '11:00', power: 1420, water: 95, gas: 38, steam: 20 },
{ hour: '12:00', power: 1100, water: 75, gas: 28, steam: 14 },
{ hour: '13:00', power: 1380, water: 90, gas: 36, steam: 17 },
{ hour: '14:00', power: 1450, water: 98, gas: 40, steam: 22 },
{ hour: '15:00', power: 1320, water: 86, gas: 34, steam: 16 }
],
equipmentRanking: [
{ name: '酸轧线1号机', power: 3200, ratio: 25.5 },
{ name: '酸轧线2号机', power: 2850, ratio: 22.7 },
{ name: '退火炉A', power: 2100, ratio: 16.7 },
{ name: '退火炉B', power: 1980, ratio: 15.8 },
{ name: '酸洗线', power: 1560, ratio: 12.4 },
{ name: '其他设备', power: 870, ratio: 6.9 }
],
energyComposition: [
{ name: '电力', value: 78.5 },
{ name: '水', value: 10.2 },
{ name: '天然气', value: 7.8 },
{ name: '蒸汽', value: 3.5 }
],
alarms: [
{ level: 'warning', message: '酸轧线1号机电流偏高', time: '14:25:00' },
{ level: 'info', message: '退火炉B能耗正常', time: '13:30:00' },
{ level: 'success', message: '能源系统运行正常', time: '08:00:00' }
]
})
return
}
try {
const [powerData] = await acidPool.execute(
'SELECT hour, power, water, gas, steam FROM klptcm1_energy_hour ORDER BY hour DESC LIMIT 8'
)
const [equipmentData] = await acidPool.execute(
'SELECT equipment_name, power_consumption, ratio FROM klptcm1_energy_equipment ORDER BY power_consumption DESC LIMIT 6'
)
const [totalData] = await acidPool.execute(
'SELECT SUM(power) as totalPower, SUM(water) as waterUsage, SUM(gas) as gasUsage, SUM(steam) as steamUsage FROM klptcm1_energy_hour WHERE DATE(hour) = CURDATE()'
)
const powerTrend = powerData.map(row => ({
hour: row.hour?.substring(11, 16) || '',
power: row.power || 0,
water: row.water || 0,
gas: row.gas || 0,
steam: row.steam || 0
})).reverse()
const totalPower = totalData[0]?.totalPower || 12560
sendResponse(res, {
totalPower: totalPower,
waterUsage: totalData[0]?.waterUsage || 856,
gasUsage: totalData[0]?.gasUsage || 325,
steamUsage: totalData[0]?.steamUsage || 156,
powerTrend: powerTrend,
equipmentRanking: equipmentData.map(row => ({
name: row.equipment_name || '',
power: row.power_consumption || 0,
ratio: row.ratio || 0
})),
energyComposition: [
{ name: '电力', value: totalPower > 0 ? Math.round((totalPower / (totalPower + 1500)) * 100) : 78.5 },
{ name: '水', value: 10.2 },
{ name: '天然气', value: 7.8 },
{ name: '蒸汽', value: 3.5 }
],
alarms: [
{ level: 'warning', message: '酸轧线1号机电流偏高', time: '14:25:00' },
{ level: 'info', message: '退火炉B能耗正常', time: '13:30:00' },
{ level: 'success', message: '能源系统运行正常', time: '08:00:00' }
]
})
} catch (err) {
console.error('能源数据查询失败:', err)
sendResponse(res, {
totalPower: 12560,
waterUsage: 856,
gasUsage: 325,
steamUsage: 156,
powerTrend: [
{ hour: '08:00', power: 1200, water: 85, gas: 32, steam: 15 },
{ hour: '09:00', power: 1350, water: 92, gas: 35, steam: 18 },
{ hour: '10:00', power: 1280, water: 88, gas: 33, steam: 16 },
{ hour: '11:00', power: 1420, water: 95, gas: 38, steam: 20 },
{ hour: '12:00', power: 1100, water: 75, gas: 28, steam: 14 },
{ hour: '13:00', power: 1380, water: 90, gas: 36, steam: 17 },
{ hour: '14:00', power: 1450, water: 98, gas: 40, steam: 22 },
{ hour: '15:00', power: 1320, water: 86, gas: 34, steam: 16 }
],
equipmentRanking: [
{ name: '酸轧线1号机', power: 3200, ratio: 25.5 },
{ name: '酸轧线2号机', power: 2850, ratio: 22.7 },
{ name: '退火炉A', power: 2100, ratio: 16.7 },
{ name: '退火炉B', power: 1980, ratio: 15.8 },
{ name: '酸洗线', power: 1560, ratio: 12.4 },
{ name: '其他设备', power: 870, ratio: 6.9 }
],
energyComposition: [
{ name: '电力', value: 78.5 },
{ name: '水', value: 10.2 },
{ name: '天然气', value: 7.8 },
{ name: '蒸汽', value: 3.5 }
],
alarms: [
{ level: 'warning', message: '酸轧线1号机电流偏高', time: '14:25:00' },
{ level: 'info', message: '退火炉B能耗正常', time: '13:30:00' },
{ level: 'success', message: '能源系统运行正常', time: '08:00:00' }
]
})
}
})
// ==================== 大屏管理接口 ====================
app.get('/api/screens', async (req, res) => {