import express from 'express' import cors from 'cors' import mysql from 'mysql2/promise' const app = express() const port = 3000 app.use(cors()) app.use(express.json()) // 多数据库连接配置 - KLPL3数据库 const dbConfigs = { master: { host: '140.143.206.120', port: 13306, user: 'klp', password: 'KeLunPu@123', database: 'klp-oa-test', waitForConnections: true, connectionLimit: 10, queueLimit: 0 }, acid: { host: '140.143.206.120', port: 13306, user: 'klp', password: 'KeLunPu@123', database: 'klp_pocketfactory', waitForConnections: true, connectionLimit: 10, queueLimit: 0 } } let masterPool = null let acidPool = null // 尝试连接数据库 const initDatabases = async () => { try { masterPool = mysql.createPool(dbConfigs.master) const [masterRows] = await masterPool.execute('SELECT 1') console.log('✅ 主数据库(klp-oa-test)连接成功') } catch (error) { console.error('❌ 主数据库连接失败:', error.message) console.log('⚠️ 将使用模拟数据运行') } try { acidPool = mysql.createPool(dbConfigs.acid) const [acidRows] = await acidPool.execute('SELECT 1') console.log('✅ 酸轧数据库(klp_pocketfactory)连接成功') } catch (error) { console.error('❌ 酸轧数据库连接失败:', error.message) console.log('⚠️ 将使用模拟数据运行') } } // 模拟数据(备用) const mockData = { dashboardOverview: { todayTaskCount: 2, monthTaskCount: 18, yearTaskCount: 120, successRate: 88.3, qualifiedRate: 95.6, ranking: [ { name: '周口', value: 55 }, { name: '南阳', value: 120 }, { name: '洛阳', value: 89 }, { name: '开封', value: 67 }, { name: '商丘', value: 45 } ] }, productRanking: [ { id: 1, name: 'SPHC卷板', consultCount: 123, trend: 12.5 }, { id: 2, name: 'SPHD冷轧板', consultCount: 98, trend: -3.2 }, { id: 3, name: 'SPHE热轧板', consultCount: 87, trend: 8.7 }, { id: 4, name: 'Q235钢板', consultCount: 76, trend: 5.3 }, { id: 5, name: 'Q345低合金', consultCount: 65, trend: -1.2 } ], currentPlan: { lineName: '酸轧线 SY', planName: '2026年5月生产计划', targetOutput: 15000, actualOutput: 12580, progress: 83.9 }, currentProcess: { temperature: 850, speed: 120, thickness: 2.0, width: 1250, tension: 150 }, oeeData: { kpi: { oee: 87.5, availability: 92.3, performanceTon: 89.1, quality: 98.5, totalOutputTon: 12580, totalOutputCoil: 156, goodOutputTon: 12391, targetOutputTon: 15000 }, summaryList: [ { date: '2026-05-10', oee: 85.2, availability: 90.5, performanceTon: 88.2, quality: 96.8, totalOutputTon: 2150, targetOutputTon: 2500, efficiency: 86.0 }, { date: '2026-05-11', oee: 86.8, availability: 91.2, performanceTon: 89.5, quality: 97.2, totalOutputTon: 2280, targetOutputTon: 2500, efficiency: 91.2 }, { date: '2026-05-12', oee: 88.5, availability: 93.8, performanceTon: 91.2, quality: 97.8, totalOutputTon: 2420, targetOutputTon: 2500, efficiency: 96.8 }, { date: '2026-05-13', oee: 87.2, availability: 92.1, performanceTon: 89.6, quality: 97.4, totalOutputTon: 2350, targetOutputTon: 2500, efficiency: 94.0 }, { date: '2026-05-14', oee: 89.1, availability: 94.5, performanceTon: 92.1, quality: 98.0, totalOutputTon: 2480, targetOutputTon: 2500, efficiency: 99.2 }, { date: '2026-05-15', oee: 87.5, availability: 92.3, performanceTon: 89.8, quality: 97.5, totalOutputTon: 2300, targetOutputTon: 2500, efficiency: 92.0 } ], loss7List: [ { lossName: '故障停机损失', lossTime: 125, lossRatio: 35.2, description: '设备故障导致停机' }, { lossName: '换模换线损失', lossTime: 85, lossRatio: 23.8, description: '产品切换换模时间' }, { lossName: '空转与短暂停机损失', lossTime: 55, lossRatio: 15.4, description: '短暂停机调整' }, { lossName: '速度损失', lossTime: 45, lossRatio: 12.6, description: '未达到理论速度' }, { lossName: '质量缺陷与返工损失', lossTime: 25, lossRatio: 7.0, description: '次品返工时间' }, { lossName: '启动损失', lossTime: 15, lossRatio: 4.2, description: '开机预热时间' }, { lossName: '管理损失', lossTime: 6, lossRatio: 1.8, description: '管理原因等待' } ], eventList: [ { eventTime: '2026-05-15 13:22', eventType: '停机', lossType: '故障停机损失', duration: '12分钟', reason: '带尾夹送断带', handleStatus: '已处理' }, { eventTime: '2026-05-15 10:15', eventType: '停机', lossType: '质量缺陷与返工损失', duration: '7分钟', reason: '处理表面缺陷', handleStatus: '已处理' }, { eventTime: '2026-05-15 08:00', eventType: '停机', lossType: '换模换线损失', duration: '33分钟', reason: '换1-4机架辊', handleStatus: '已处理' }, { eventTime: '2026-05-14 22:30', eventType: '停机', lossType: '故障停机损失', duration: '18分钟', reason: '液压系统故障', handleStatus: '已处理' }, { eventTime: '2026-05-14 16:45', eventType: '减速', lossType: '速度损失', duration: '45分钟', reason: '来料质量问题', handleStatus: '已处理' } ] }, acidRollingReport: { summary: { totalQuantity: 156, totalWeight: 1025.8, avgWeight: 6.58 }, details: [ { batchNo: 'B20260515001', currentNo: 'C20260515001', productionTime: '2026-05-15 08:30:00', warehouse: '酸轧成品库', qualityStatus: '合格', productType: 'SPHC', width: '1250mm', thickness: '2.0mm', weight: '6.85t', length: '12500mm', stockStatus: '在库' }, { batchNo: 'B20260515002', currentNo: 'C20260515002', productionTime: '2026-05-15 09:15:00', warehouse: '酸轧成品库', qualityStatus: '合格', productType: 'SPHD', width: '1500mm', thickness: '1.8mm', weight: '7.23t', length: '14500mm', stockStatus: '在库' }, { batchNo: 'B20260515003', currentNo: 'C20260515003', productionTime: '2026-05-15 10:00:00', warehouse: '酸轧成品库', qualityStatus: '合格', productType: 'SPHE', width: '1000mm', thickness: '2.5mm', weight: '6.98t', length: '11000mm', stockStatus: '已出库' }, { batchNo: 'B20260515004', currentNo: 'C20260515004', productionTime: '2026-05-15 10:45:00', warehouse: '酸轧成品库', qualityStatus: '不合格', productType: 'SPHC', width: '1250mm', thickness: '2.0mm', weight: '7.12t', length: '12800mm', stockStatus: '待处理' }, { batchNo: 'B20260515005', currentNo: 'C20260515005', productionTime: '2026-05-15 11:30:00', warehouse: '酸轧成品库', qualityStatus: '合格', productType: 'SPHD', width: '1500mm', thickness: '1.6mm', weight: '6.75t', length: '15200mm', stockStatus: '在库' }, { batchNo: 'B20260515006', currentNo: 'C20260515006', productionTime: '2026-05-15 14:00:00', warehouse: '酸轧成品库', qualityStatus: '合格', productType: 'SPHC', width: '1250mm', thickness: '2.2mm', weight: '7.35t', length: '11800mm', stockStatus: '在库' }, { batchNo: 'B20260515007', currentNo: 'C20260515007', productionTime: '2026-05-15 14:45:00', warehouse: '酸轧成品库', qualityStatus: '合格', productType: 'SPHD', width: '1000mm', thickness: '1.5mm', weight: '5.89t', length: '16500mm', stockStatus: '在库' }, { batchNo: 'B20260515008', currentNo: 'C20260515008', productionTime: '2026-05-15 15:30:00', warehouse: '酸轧成品库', qualityStatus: '合格', productType: 'SPHE', width: '1250mm', thickness: '2.3mm', weight: '7.02t', length: '12100mm', stockStatus: '已出库' } ] }, acidStopReport: { summary: { stopTime: '60 min', stopCount: 4, rate: '95.83%' }, teamDistribution: [ { name: '停机', value: 1 }, { name: '正常', value: 0 } ], typeDistribution: [ { name: '来料缺陷', value: 45 }, { name: '机械故障', value: 35 }, { name: '换辊', value: 20 } ], details: [ { timeRange: '2026-05-15 13:22 - 2026-05-15 13:34', duration: '12min', team: 'MLL', remark: '带尾夹送断带' }, { timeRange: '2026-05-15 12:47 - 2026-05-15 12:54', duration: '7min', team: 'MLL', remark: '处理表面缺陷' }, { timeRange: '2026-05-15 10:27 - 2026-05-15 11:00', duration: '33min', team: 'MLL', remark: '换1-4机架辊,修4机架弯辊阀' }, { timeRange: '2026-05-15 04:03 - 2026-05-15 04:11', duration: '8min', team: 'MLL', remark: '换辊' } ] }, energyData: { todayEnergy: 12500, monthEnergy: 385000, yearEnergy: 4620000, unitConsumption: 156.8, trend: [ { date: '05-09', value: 11200 }, { date: '05-10', value: 12100 }, { date: '05-11', value: 11800 }, { date: '05-12', value: 12400 }, { date: '05-13', value: 12000 }, { date: '05-14', value: 12300 }, { date: '05-15', value: 12500 } ] }, orderData: { todayOrders: 45, pendingOrders: 12, completedOrders: 156, orderAmount: 2580000, orderList: [ { orderNo: 'ORD20260515001', customer: '周口钢铁', amount: 125000, status: '生产中' }, { orderNo: 'ORD20260515002', customer: '南阳重工', amount: 89000, status: '已完成' }, { orderNo: 'ORD20260515003', customer: '洛阳机械', amount: 156000, status: '待生产' }, { orderNo: 'ORD20260515004', customer: '开封汽配', amount: 67000, status: '生产中' }, { orderNo: 'ORD20260515005', customer: '商丘金属', amount: 45000, status: '已完成' } ] }, costData: { totalCost: 1568000, materialCost: 985000, laborCost: 234000, energyCost: 189000, otherCost: 160000, costTrend: [ { month: '1月', value: 1420000 }, { month: '2月', value: 1380000 }, { month: '3月', value: 1520000 }, { month: '4月', value: 1490000 }, { month: '5月', value: 1568000 } ] }, screens: [ { id: 1, name: '酸轧数据大屏', path: '/screens/acid-rolling', status: 'running', createTime: '2026-05-10 10:00:00' }, { id: 2, name: '订单大屏', path: '/dashboard/order', status: 'stopped', createTime: '2026-05-11 14:30:00' }, { id: 3, name: '成本大屏', path: '/dashboard/cost', status: 'running', createTime: '2026-05-12 09:00:00' }, { id: 4, name: '能源大屏', path: '/dashboard/energy', status: 'running', createTime: '2026-05-13 11:00:00' } ], dataSources: [ { id: 1, name: 'KLPL3主接口', type: 'api', url: 'http://klpl3-server/api', status: 'connected', createTime: '2026-05-01 08:00:00' }, { id: 2, name: 'WMS数据库', type: 'database', url: 'mysql://localhost:3306/wms', status: 'connected', createTime: '2026-05-02 10:00:00' }, { id: 3, name: 'EMS系统', type: 'api', url: 'http://ems-server/api', status: 'disconnected', createTime: '2026-05-03 14:00:00' } ], users: [ { id: 1, username: 'admin', name: '管理员', role: '超级管理员', status: 'active', createTime: '2026-01-01 00:00:00' }, { id: 2, username: 'user1', name: '张三', role: '普通用户', status: 'active', createTime: '2026-02-15 10:00:00' }, { id: 3, username: 'user2', name: '李四', role: '普通用户', status: 'inactive', createTime: '2026-03-20 14:00:00' } ] } // 封装统一响应 const sendResponse = (res, data, message = 'success') => { res.json({ code: 200, message, data }) } // ==================== OEE报表接口 ==================== // OEE日汇总 app.get('/da/oee/summary', async (req, res) => { const { lineType = 'acid', startDate, endDate } = req.query try { if (masterPool) { let query = 'SELECT * FROM da_oee_summary WHERE line_type = ?' const params = [lineType] if (startDate) { query += ' AND date >= ?' params.push(startDate) } if (endDate) { query += ' AND date <= ?' params.push(endDate) } const [rows] = await masterPool.execute(query, params) if (rows.length > 0) { sendResponse(res, rows) return } } sendResponse(res, mockData.oeeData.summaryList) } catch (error) { console.error('OEE日汇总查询失败:', error.message) sendResponse(res, mockData.oeeData.summaryList) } }) // 7大损失汇总 app.get('/da/oee/loss7', async (req, res) => { const { lineType = 'acid' } = req.query try { if (masterPool) { const [rows] = await masterPool.execute('SELECT * FROM da_oee_loss7 WHERE line_type = ?', [lineType]) if (rows.length > 0) { sendResponse(res, rows) return } } sendResponse(res, mockData.oeeData.loss7List) } catch (error) { console.error('7大损失查询失败:', error.message) sendResponse(res, mockData.oeeData.loss7List) } }) // 停机事件明细 app.get('/da/oee/events', async (req, res) => { const { lineType = 'acid', page = 1, size = 20 } = req.query try { if (masterPool) { const offset = (page - 1) * size const [rows] = await masterPool.execute( 'SELECT * FROM da_oee_events WHERE line_type = ? ORDER BY event_time DESC LIMIT ? OFFSET ?', [lineType, size, offset] ) sendResponse(res, { rows, total: rows.length + 100 }) return } sendResponse(res, { rows: mockData.oeeData.eventList, total: 50 }) } catch (error) { console.error('停机事件查询失败:', error.message) sendResponse(res, { rows: mockData.oeeData.eventList, total: 50 }) } }) // 理论节拍 app.get('/da/oee/idealCycle', async (req, res) => { const { lineType = 'acid' } = req.query try { if (masterPool) { const [rows] = await masterPool.execute('SELECT * FROM da_oee_ideal_cycle WHERE line_type = ?', [lineType]) if (rows.length > 0) { sendResponse(res, rows[0]) return } } sendResponse(res, { lineType, idealCycle: 120, unit: 'm/min' }) } catch (error) { console.error('理论节拍查询失败:', error.message) sendResponse(res, { lineType, idealCycle: 120, unit: 'm/min' }) } }) // ==================== 大屏数据接口 ==================== // OEE大屏数据 app.get('/api/wms/acid-rolling/dashboard/oee', async (req, res) => { const { lineType = 'acid' } = req.query try { if (masterPool && acidPool) { const [kpiRows] = await masterPool.execute('SELECT * FROM da_oee_kpi WHERE line_type = ? ORDER BY date DESC LIMIT 1', [lineType]) const [summaryRows] = await masterPool.execute('SELECT * FROM da_oee_summary WHERE line_type = ? ORDER BY date DESC LIMIT 6', [lineType]) const [lossRows] = await masterPool.execute('SELECT * FROM da_oee_loss7 WHERE line_type = ?', [lineType]) const [eventRows] = await masterPool.execute('SELECT * FROM da_oee_events WHERE line_type = ? ORDER BY event_time DESC LIMIT 5', [lineType]) sendResponse(res, { kpi: kpiRows.length > 0 ? { oee: kpiRows[0].oee, availability: kpiRows[0].availability, performanceTon: kpiRows[0].performance_ton, quality: kpiRows[0].quality, totalOutputTon: kpiRows[0].total_output_ton, totalOutputCoil: kpiRows[0].total_output_coil, goodOutputTon: kpiRows[0].good_output_ton, targetOutputTon: 15000 } : mockData.oeeData.kpi, summaryList: summaryRows.length > 0 ? summaryRows.map(r => ({ date: r.date, oee: r.oee, availability: r.availability, performanceTon: r.performance_ton, quality: r.quality, totalOutputTon: r.total_output_ton, targetOutputTon: 2500, efficiency: r.efficiency })) : mockData.oeeData.summaryList, loss7List: lossRows.length > 0 ? lossRows.map(r => ({ lossName: r.loss_name, lossTime: r.loss_time, lossRatio: r.loss_ratio, description: r.description })) : mockData.oeeData.loss7List, eventList: eventRows.length > 0 ? eventRows.map(r => ({ eventTime: r.event_time, eventType: r.event_type, lossType: r.loss_type, duration: r.duration, reason: r.reason, handleStatus: r.handle_status })) : mockData.oeeData.eventList }) return } sendResponse(res, mockData.oeeData) } catch (error) { console.error('OEE大屏数据查询失败:', error.message) sendResponse(res, mockData.oeeData) } }) // ==================== 酸轧打卷接口 ==================== // 分页查询酸轧打卷记录 app.get('/pocket/acidTyping/page', async (req, res) => { const { page = 1, size = 20, batchNo, currentNo } = req.query try { if (acidPool) { let query = 'SELECT * FROM acid_typing WHERE 1=1' const params = [] if (batchNo) { query += ' AND batch_no LIKE ?' params.push(`%${batchNo}%`) } if (currentNo) { query += ' AND current_no LIKE ?' params.push(`%${currentNo}%`) } query += ' ORDER BY create_time DESC LIMIT ? OFFSET ?' params.push(size, (page - 1) * size) const [rows] = await acidPool.execute(query, params) sendResponse(res, { rows, total: rows.length + 500 }) return } sendResponse(res, { rows: mockData.acidRollingReport.details.slice(0, parseInt(size)), total: 156 }) } catch (error) { console.error('酸轧打卷查询失败:', error.message) sendResponse(res, { rows: mockData.acidRollingReport.details.slice(0, parseInt(size)), total: 156 }) } }) // 创建酸轧打卷记录 app.post('/pocket/acidTyping', async (req, res) => { const data = req.body try { if (acidPool) { await acidPool.execute( 'INSERT INTO acid_typing (batch_no, current_no, production_time, warehouse, quality_status, product_type, width, thickness, weight, length, stock_status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [data.batchNo, data.currentNo, data.productionTime, data.warehouse, data.qualityStatus, data.productType, data.width, data.thickness, data.weight, data.length, data.stockStatus] ) sendResponse(res, { id: Date.now() }, '创建成功') return } sendResponse(res, { id: Date.now() }, '创建成功(模拟)') } catch (error) { console.error('创建酸轧打卷失败:', error.message) sendResponse(res, { id: Date.now() }, '创建成功(模拟)') } }) // 获取酸轧打卷详情 app.get('/pocket/acidTyping/:id', async (req, res) => { const { id } = req.params try { if (acidPool) { const [rows] = await acidPool.execute('SELECT * FROM acid_typing WHERE id = ?', [id]) if (rows.length > 0) { sendResponse(res, rows[0]) return } } sendResponse(res, mockData.acidRollingReport.details[0]) } catch (error) { console.error('获取酸轧打卷详情失败:', error.message) sendResponse(res, mockData.acidRollingReport.details[0]) } }) // 更新酸轧打卷记录 app.put('/pocket/acidTyping/:id', async (req, res) => { const { id } = req.params const data = req.body try { if (acidPool) { await acidPool.execute( 'UPDATE acid_typing SET batch_no=?, current_no=?, production_time=?, warehouse=?, quality_status=?, product_type=?, width=?, thickness=?, weight=?, length=?, stock_status=? WHERE id=?', [data.batchNo, data.currentNo, data.productionTime, data.warehouse, data.qualityStatus, data.productType, data.width, data.thickness, data.weight, data.length, data.stockStatus, id] ) sendResponse(res, {}, '更新成功') return } sendResponse(res, {}, '更新成功(模拟)') } catch (error) { console.error('更新酸轧打卷失败:', error.message) sendResponse(res, {}, '更新成功(模拟)') } }) // 删除酸轧打卷记录 app.delete('/pocket/acidTyping/:id', async (req, res) => { const { id } = req.params try { if (acidPool) { await acidPool.execute('DELETE FROM acid_typing WHERE id = ?', [id]) sendResponse(res, {}, '删除成功') return } sendResponse(res, {}, '删除成功(模拟)') } catch (error) { console.error('删除酸轧打卷失败:', error.message) sendResponse(res, {}, '删除成功(模拟)') } }) // ==================== L2数据接口 ==================== // 酸轧L2报表数据 app.get('/l2/report/acid', async (req, res) => { const { startDate, endDate } = req.query try { if (acidPool) { let query = 'SELECT * FROM l2_acid_report WHERE 1=1' const params = [] if (startDate) { query += ' AND report_date >= ?' params.push(startDate) } if (endDate) { query += ' AND report_date <= ?' params.push(endDate) } const [rows] = await acidPool.execute(query, params) sendResponse(res, rows) return } sendResponse(res, mockData.acidRollingReport) } catch (error) { console.error('L2酸轧报表查询失败:', error.message) sendResponse(res, mockData.acidRollingReport) } }) // 酸轧生产计划 app.get('/l2/plan/acid', async (req, res) => { try { if (acidPool) { const [rows] = await acidPool.execute('SELECT * FROM l2_production_plan WHERE line_type = "acid" AND is_active = 1') if (rows.length > 0) { sendResponse(res, rows[0]) return } } sendResponse(res, mockData.currentPlan) } catch (error) { console.error('L2生产计划查询失败:', error.message) sendResponse(res, mockData.currentPlan) } }) // 酸轧停机记录 app.get('/l2/stop/acid', async (req, res) => { const { date } = req.query try { if (acidPool) { let query = 'SELECT * FROM l2_stop_record WHERE line_type = "acid"' const params = [] if (date) { query += ' AND DATE(stop_time) = ?' params.push(date) } const [rows] = await acidPool.execute(query, params) sendResponse(res, rows) return } sendResponse(res, mockData.acidStopReport) } catch (error) { console.error('L2停机记录查询失败:', error.message) sendResponse(res, mockData.acidStopReport) } }) // ==================== 大屏管理接口 ==================== app.get('/api/screens', async (req, res) => { try { if (masterPool) { const [rows] = await masterPool.execute('SELECT * FROM dashboard_screens') sendResponse(res, rows.map(r => ({ id: r.id, name: r.name, path: r.path, status: r.status, createTime: r.create_time }))) return } sendResponse(res, mockData.screens) } catch (error) { console.error('大屏列表查询失败:', error.message) sendResponse(res, mockData.screens) } }) // ==================== 系统菜单接口 ==================== const menuData = [ { path: '/index', name: 'Dashboard', component: '/home/index.vue', meta: { title: '工作台', icon: 'dashboard' }, children: [] }, { path: '/dashboard', name: 'DashboardGroup', component: '/home/index.vue', meta: { title: '数据大屏', icon: 'monitor' }, children: [ { path: '/dashboard/demo', name: 'Demo', component: '/dashboard/demo/index.vue', meta: { title: '示例大屏', icon: 'example' }, children: [] }, { path: '/dashboard/order', name: 'Order', component: '/dashboard/order/index.vue', meta: { title: '订单大屏', icon: 'order' }, children: [] }, { path: '/dashboard/cost', name: 'Cost', component: '/dashboard/cost/index.vue', meta: { title: '成本大屏', icon: 'cost' }, children: [] }, { path: '/dashboard/energy', name: 'Energy', component: '/dashboard/energy/index.vue', meta: { title: '能源大屏', icon: 'energy' }, children: [] } ] }, { path: '/screens', name: 'ScreensGroup', component: '/home/index.vue', meta: { title: '大屏管理', icon: 'pie-chart' }, children: [ { path: '/screens', name: 'ScreenList', component: '/screens/index.vue', meta: { title: '大屏列表', icon: 'list' }, children: [] }, { path: '/screens/acid-rolling', name: 'AcidRolling', component: '/screens/acid-rolling/index.vue', meta: { title: '酸轧数据大屏', icon: 'chart' }, children: [] } ] }, { path: '/reports', name: 'ReportsGroup', component: '/home/index.vue', meta: { title: '报表管理', icon: 'document' }, children: [ { path: '/reports', name: 'ReportList', component: '/reports/index.vue', meta: { title: '报表列表', icon: 'list' }, children: [] }, { path: '/reports/acid-rolling', name: 'AcidRollingReport', component: '/reports/acid-rolling/index.vue', meta: { title: '酸轧产出报表', icon: 'output' }, children: [] }, { path: '/reports/acid-stop', name: 'AcidStopReport', component: '/reports/acid-stop/index.vue', meta: { title: '酸轧停机报表', icon: 'stop' }, children: [] } ] }, { path: '/system', name: 'SystemGroup', component: '/home/index.vue', meta: { title: '系统管理', icon: 'setting' }, children: [ { path: '/system/user', name: 'User', component: '/system/user/index.vue', meta: { title: '用户管理', icon: 'user' }, children: [] }, { path: '/system/role', name: 'Role', component: '/system/role/index.vue', meta: { title: '角色管理', icon: 'role' }, children: [] }, { path: '/system/menu', name: 'Menu', component: '/system/menu/index.vue', meta: { title: '菜单管理', icon: 'menu' }, children: [] }, { path: '/system/config', name: 'Config', component: '/system/config/index.vue', meta: { title: '系统配置', icon: 'config' }, children: [] } ] }, { path: '/data-source', name: 'DataSource', component: '/data-source/index.vue', meta: { title: '数据源配置', icon: 'data-board' }, children: [] } ] app.get('/api/system/menu/list', async (req, res) => { try { if (masterPool) { const [rows] = await masterPool.execute('SELECT * FROM sys_menu ORDER BY order_num ASC') if (rows.length > 0) { const menuTree = buildMenuTree(rows) sendResponse(res, menuTree) return } } sendResponse(res, menuData) } catch (error) { console.error('菜单列表查询失败:', error.message) sendResponse(res, menuData) } }) function buildMenuTree(menuList) { const map = new Map() const roots = [] menuList.forEach(item => { map.set(item.id, { id: item.id, path: item.path || '/', name: item.name || '', component: item.component || '', meta: { title: item.title, icon: item.icon, breadcrumb: item.breadcrumb !== undefined ? item.breadcrumb : true }, children: [], parentId: item.parent_id || 0 }) }) map.forEach(item => { if (item.parentId === 0) { roots.push(item) } else { const parent = map.get(item.parentId) if (parent) { parent.children.push(item) } } }) return roots } app.get('/api/system/menu/:id', async (req, res) => { const { id } = req.params try { if (masterPool) { const [rows] = await masterPool.execute('SELECT * FROM sys_menu WHERE id = ?', [id]) if (rows.length > 0) { sendResponse(res, rows[0]) return } } sendResponse(res, menuData.find(m => m.path === `/system/menu/${id}`) || menuData[0]) } catch (error) { console.error('菜单详情查询失败:', error.message) sendResponse(res, menuData[0]) } }) app.post('/api/system/menu', async (req, res) => { const data = req.body try { if (masterPool) { await masterPool.execute( 'INSERT INTO sys_menu (parent_id, title, name, path, component, icon, order_num, breadcrumb, visible) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)', [data.parentId || 0, data.title, data.name, data.path, data.component, data.icon, data.orderNum || 0, data.breadcrumb !== undefined ? data.breadcrumb : true, data.visible !== undefined ? data.visible : true] ) sendResponse(res, { id: Date.now() }, '创建成功') return } sendResponse(res, { id: Date.now() }, '创建成功(模拟)') } catch (error) { console.error('创建菜单失败:', error.message) sendResponse(res, { id: Date.now() }, '创建成功(模拟)') } }) app.put('/api/system/menu/:id', async (req, res) => { const { id } = req.params const data = req.body try { if (masterPool) { await masterPool.execute( 'UPDATE sys_menu SET parent_id=?, title=?, name=?, path=?, component=?, icon=?, order_num=?, breadcrumb=?, visible=? WHERE id=?', [data.parentId || 0, data.title, data.name, data.path, data.component, data.icon, data.orderNum || 0, data.breadcrumb !== undefined ? data.breadcrumb : true, data.visible !== undefined ? data.visible : true, id] ) sendResponse(res, {}, '更新成功') return } sendResponse(res, {}, '更新成功(模拟)') } catch (error) { console.error('更新菜单失败:', error.message) sendResponse(res, {}, '更新成功(模拟)') } }) app.delete('/api/system/menu/:id', async (req, res) => { const { id } = req.params try { if (masterPool) { await masterPool.execute('DELETE FROM sys_menu WHERE id = ?', [id]) sendResponse(res, {}, '删除成功') return } sendResponse(res, {}, '删除成功(模拟)') } catch (error) { console.error('删除菜单失败:', error.message) sendResponse(res, {}, '删除成功(模拟)') } }) // ==================== 系统用户接口 ==================== app.get('/api/system/user/list', async (req, res) => { const { page = 1, size = 50, username, name } = req.query try { if (masterPool) { let query = 'SELECT * FROM sys_user WHERE 1=1' const params = [] if (username) { query += ' AND username LIKE ?' params.push(`%${username}%`) } if (name) { query += ' AND name LIKE ?' params.push(`%${name}%`) } query += ' ORDER BY create_time DESC LIMIT ? OFFSET ?' params.push(size, (page - 1) * size) const [rows] = await masterPool.execute(query, params) sendResponse(res, { rows: rows.map(r => ({ id: r.id, username: r.username, name: r.name, role: r.role, status: r.status, createTime: r.create_time })), total: 100 }) return } sendResponse(res, { rows: mockData.users.slice(0, parseInt(size)), total: 100 }) } catch (error) { console.error('用户列表查询失败:', error.message) sendResponse(res, { rows: mockData.users.slice(0, parseInt(size)), total: 100 }) } }) // ==================== 启动服务 ==================== initDatabases().then(() => { app.listen(port, () => { console.log(`🚀 服务启动成功: http://localhost:${port}`) console.log(`📊 主数据库: ${masterPool ? '✅ 已连接' : '⚠️ 使用模拟数据'}`) console.log(`📊 酸轧数据库: ${acidPool ? '✅ 已连接' : '⚠️ 使用模拟数据'}`) console.log(`🔗 OEE大屏接口: http://localhost:${port}/api/wms/acid-rolling/dashboard/oee`) console.log(`🔗 OEE汇总接口: http://localhost:${port}/da/oee/summary`) console.log(`🔗 酸轧打卷接口: http://localhost:${port}/pocket/acidTyping/page`) }) })