编辑页面

修改后将从当前时间重新计算过期时间
💡 新增说明:
• 批次号自动生成,格式:年月日+序号
• 新增后默认状态为"已完成"
• 产值将自动汇总到月度统计报表
`; break; case 'workshop': modalTitle.textContent = '新增车间'; content = `
`; break; case 'production': modalTitle.textContent = '新增生产单'; content = `
`; break; case 'wage': modalTitle.textContent = '新增工资记录'; content = `
`; break; case 'energyPrice': modalTitle.textContent = '编辑能源单价参数'; content = `
`; break; case 'stdWorkHours': modalTitle.textContent = '编辑标准工时参数'; content = `
`; break; case 'user': modalTitle.textContent = '新增用户'; content = `
`; break; case 'backup': modalTitle.textContent = '创建数据备份'; content = `
`; break; case 'energy': modalTitle.textContent = '新增能耗记录'; content = `
💡 提示:用量的计算公式:用量 = 期末读数 - 期初读数
金额的计算公式:金额 = 用量 × 单价
`; break; case 'deviceLog': modalTitle.textContent = '新增设备运行日志'; content = `
💡 提示:OEE计算公式:
• 工作时间 = 关机时间 - 开机时间
• 负荷时间 = 工作时间 - 计划停机时间
• 开动时间 = 负荷时间 - 设备初始化时间 - 故障时间
• OEE = 时间开动率 × 性能开动率 × 合格品率
`; break; case 'cost': modalTitle.textContent = '新增成本记录'; content = `
💡 成本计算公式:
• 产品总成本 = 直接材料 + 直接人工 + 能耗成本 + 制造费用
• 产品单位成本 = 产品总成本 ÷ 产品产量
• 成本差异额 = 实际单位成本 - 标准单位成本
• 成本差异率 = 成本差异额 ÷ 标准单位成本 × 100%
`; break; case 'person': modalTitle.textContent = '新增人员档案'; content = `
💡 人员档案说明:
• 工号自动生成,也可手动输入
• 人员信息与工资系统关联
• 离职后状态变更为"离职",不影响历史工资记录
`; break; case 'product': modalTitle.textContent = '新增产品档案'; content = `
💡 产品档案说明:
• 产品ID自动生成,也可手动输入
• 产品信息与生产、成本系统关联
• 含税单价自动根据税率计算
`; break; case 'process': modalTitle.textContent = '新增工序档案'; content = `
💡 工序档案说明:
• 工序ID自动生成,也可手动输入
• 工序与产品关联,影响生产效率计算
• 工序顺序决定生产流程
`; break; case 'equipment': modalTitle.textContent = '新增设备档案'; content = `
💡 设备档案说明:
• 设备ID自动生成
• 设备信息用于OEE计算
• 状态变更自动记录到设备日志
`; break; case 'energyType': modalTitle.textContent = '新增能源类型'; content = `
💡 能源类型说明:
• 能源ID自动生成
• 能源类型用于能耗成本计算
• 税率影响含税单价计算
`; break; case 'importProduction': modalTitle.textContent = '📥 批量导入生产数据'; content = `
💡 导入说明:
• 请先下载导入模板,按模板格式填写数据
• 支持 .xlsx/.xls/.csv 格式文件
• 单次导入最多支持 1000 条数据
`; break; case 'importEnergy': modalTitle.textContent = '📥 批量导入能耗数据'; content = `
💡 导入说明:
• 请先下载导入模板,按模板格式填写数据
• 能耗数据包含:期初读数、期末读数、用量、单价等
• 系统将自动计算金额
`; break; case 'importWage': modalTitle.textContent = '📥 批量导入工资数据'; content = `
💡 导入说明:
• 请先下载导入模板,按模板格式填写数据
• 必须包含:工号、姓名、计件数量、计件单价等
• 计件工资将自动计算
`; break; case 'importCost': modalTitle.textContent = '📥 批量导入成本数据'; content = `
💡 导入说明:
• 请先下载导入模板,按模板格式填写数据
• 必须包含:产品名称、直接材料、直接人工、能耗成本等
• 成本计算公式:总成本 = 直接材料 + 直接人工 + 能耗成本 + 制造费用
`; break; case 'importDevice': modalTitle.textContent = '📥 批量导入设备运行数据'; content = `
💡 导入说明:
• 请先下载导入模板,按模板格式填写数据
• 必须包含:设备名称、车间、工作时间、故障时间等
• 系统将自动计算OEE各项指标
`; break; case 'exportShipment': modalTitle.textContent = '📥 导出成品发货趋势及生产计划'; content = `
💡 导出说明:
• 预计导出 4 条产品记录
• 文件名将包含导出时间戳,格式:成品发货趋势及生产计划_年月.xlsx
`; break; case 'exportEnergy': modalTitle.textContent = '📥 导出能源消耗报表'; content = `
💡 导出说明:
• 预计导出 12 条数据记录
• 文件名将包含导出时间戳
`; break; case 'exportCost': modalTitle.textContent = '📥 导出成本分析报表'; content = `
💡 导出说明:
• 预计导出 8 条数据记录
• 包含直接材料、直接人工、能耗成本等成本项
`; break; case 'exportOEE': modalTitle.textContent = '📥 导出设备OEE分析报表'; content = `
💡 导出说明:
• 预计导出 15 条数据记录
• 包含时间开动率、性能开动率、合格品率等指标
`; break; case 'exportWageReport': modalTitle.textContent = '📥 导出工资报表'; content = `
💡 导出说明:
• 预计导出 149 条工资记录
• 包含计件工资、计时工资、奖金及扣款明细
• 文件名将包含导出时间戳,格式:工资报表_年月.xlsx
`; break; case 'exportLog': modalTitle.textContent = '📥 导出系统日志'; content = `
⚠️ 注意事项:
• 日志数据仅用于系统审计和问题追踪
• 预计导出 128 条日志记录
`; break; case 'exportWage': modalTitle.textContent = '📥 导出工资汇总报表'; content = `
💡 导出说明:
• 预计导出 25 条汇总记录
• 文件名格式:工资汇总报表_年月.xlsx
`; break; case 'exportWageDetail': modalTitle.textContent = '📥 导出工资明细数据'; content = `
💡 导出说明:
• 预计导出 149 条明细记录
• 文件名格式:工资明细_年月.xlsx
`; break; case 'exportWorkshopWage': modalTitle.textContent = '📥 导出 - 成都地奥集团天府药业股份有限公司工资表'; content = `
💡 导出说明:
• 预计导出 18 条车间汇总记录
`; break; case 'exportPieceWage': modalTitle.textContent = '📥 导出 - 成都地奥天府药业车间计件工资汇总'; content = `
💡 导出说明:
• 预计导出 68 条计件工资记录
`; break; case 'exportPieceWageAnalysis': modalTitle.textContent = '📥 导出 - 2026年计件工资统计表'; content = `
💡 导出说明:
• 预计导出 12 条分析数据
`; break; case 'exportCompletedHours': modalTitle.textContent = '📥 导出 - 完工工时统计'; content = `
💡 导出说明:
• 预计导出 45 条工时记录
`; break; case 'exportOutput': modalTitle.textContent = '📥 导出产值工资分析报表'; content = `
💡 导出说明:
• 预计导出 20 条产值工资分析数据
`; break; case 'exportVerify': modalTitle.textContent = '📥 导出工资核对报表'; content = `
💡 导出说明:
• 预计导出 85 条核对记录
`; break; case 'importWorkshopWage': modalTitle.textContent = '📤 导入 - 成都地奥集团天府药业股份有限公司工资表'; content = `
⚠️ 导入说明:
• 支持.xlsx, .xls, .csv格式
• 请先下载模板文件,按模板格式填写数据
• 导入前请确认数据格式正确
`; break; case 'importPieceWage': modalTitle.textContent = '📤 导入 - 成都地奥天府药业车间计件工资汇总'; content = `
⚠️ 导入说明:
• 必须包含字段:工号、姓名、产品名称、计件数量、计件单价
• 金额将自动计算
`; break; case 'importPieceWageAnalysis': modalTitle.textContent = '📤 导入 - 2026年计件工资统计表'; content = `
⚠️ 导入说明:
• 必须包含字段:产品名称、标准工时、实际工时、完成率
`; break; case 'importCompletedHours': modalTitle.textContent = '📤 导入 - 完工工时统计'; content = `
⚠️ 导入说明:
• 必须包含字段:工号、姓名、标准工时、实际工时、加班工时
`; break; case 'workshopWageDetail': modalTitle.textContent = '✏️ 编辑 - 成都地奥集团天府药业股份有限公司工资表'; content = `

📋 选择一条记录进行编辑

支持修改:工资项、扣款项、实发金额等

`; break; case 'pieceWageDetail': modalTitle.textContent = '✏️ 编辑 - 成都地奥天府药业车间计件工资汇总'; content = `

📋 选择一条计件工资记录进行编辑

支持修改:计件数量、单价、工资等

`; break; case 'pieceWageAnalysisDetail': modalTitle.textContent = '✏️ 编辑 - 2026年计件工资统计表'; content = `

📋 选择一条统计记录进行编辑

支持修改:统计数据、分析字段等

`; break; case 'completedHoursDetail': modalTitle.textContent = '✏️ 编辑 - 完工工时统计'; content = `

📋 选择一条工时记录进行编辑

支持修改:标准工时、实际工时、加班工时等

`; break; // ====== 能耗子页面(水/天然气/电/蒸汽)====== case 'importWaterEnergy': modalTitle.textContent = '📤 导入 - 水能耗统计'; content = `
💡 导入说明:
• 必须包含字段:部门、期初读数、期末读数、用量、单价、金额
`; break; case 'exportWaterEnergy': modalTitle.textContent = '📥 导出 - 水能耗统计'; content = `
📋 导出说明:
• 将导出水能耗统计数据,含蒸汽/水/部门明细
`; break; case 'waterEnergyDetail': modalTitle.textContent = '✏️ 编辑 - 水能耗统计'; content = `

📋 选择一条水能耗记录进行编辑

支持修改:读数、用量、单价等

`; break; case 'importGasEnergy': modalTitle.textContent = '📤 导入 - 天然气能耗统计'; content = `
💡 导入说明:
• 必须包含字段:部门、期初读数、期末读数、用量、单价、金额
`; break; case 'exportGasEnergy': modalTitle.textContent = '📥 导出 - 天然气能耗统计'; content = `
📋 导出说明:
• 将导出天然气能耗统计数据
`; break; case 'gasEnergyDetail': modalTitle.textContent = '✏️ 编辑 - 天然气能耗统计'; content = `

📋 选择一条天然气能耗记录进行编辑

支持修改:读数、用量、单价等

`; break; case 'importElectricEnergy': modalTitle.textContent = '📤 导入 - 用电能耗统计'; content = `
💡 导入说明:
• 必须包含字段:部门、期初读数、期末读数、用量、单价、金额
`; break; case 'exportElectricEnergy': modalTitle.textContent = '📥 导出 - 用电能耗统计'; content = `
📋 导出说明:
• 将导出用电能耗统计数据
`; break; case 'electricEnergyDetail': modalTitle.textContent = '✏️ 编辑 - 用电能耗统计'; content = `

📋 选择一条用电能耗记录进行编辑

支持修改:读数、用量、单价等

`; break; case 'importSteamEnergy': modalTitle.textContent = '📤 导入 - 蒸汽能耗统计'; content = `
💡 导入说明:
• 必须包含字段:部门、期初读数、期末读数、用量、单价、金额
`; break; case 'exportSteamEnergy': modalTitle.textContent = '📥 导出 - 蒸汽能耗统计'; content = `
📋 导出说明:
• 将导出蒸汽能耗统计数据
`; break; case 'steamEnergyDetail': modalTitle.textContent = '✏️ 编辑 - 蒸汽能耗统计'; content = `

📋 选择一条蒸汽能耗记录进行编辑

支持修改:读数、用量、单价等

`; break; // ====== 设备管理子页面(7个)====== // --- 设备运行日志统计 --- case 'importDeviceRunLog': modalTitle.textContent = '📤 导入 - 设备运行日志统计'; content = `
💡 导入说明:
• 必须包含字段:设备名称、月份、计划停机时间(min)、实际停机时间(min)、计划故障时间(min)、故障原因列、处理方式、月故障率、总故障时间、开机时间
`; break; case 'exportDeviceRunLog': modalTitle.textContent = '📥 导出 - 设备运行日志统计'; content = `
📋 导出说明:
• 将导出设备运行日志统计数据,含OEE分析报表
`; break; case 'deviceRunLogDetail': modalTitle.textContent = '✏️ 编辑 - 设备运行日志统计'; content = `

📋 选择一条设备运行日志进行编辑

支持修改:停机时间、故障原因等

`; break; // --- 制剂二车间OEE --- case 'importZhiJiOEE': modalTitle.textContent = '📤 导入 - 制剂二车间OEE'; content = `
💡 导入说明:
• 必须包含字段:设备名称、计划产量、实际产量、合格品数量、不合格品数量、理论周期时间、实际生产时间、停机时间、开机时间、OEE(%)、TEPE(%)、性能效率(%)
`; break; case 'exportZhiJiOEE': modalTitle.textContent = '📥 导出 - 制剂二车间OEE'; content = `
📋 导出说明:
• 将导出制剂二车间OEE数据
`; break; case 'zhiJiOEEDetail': modalTitle.textContent = '✏️ 编辑 - 制剂二车间OEE'; content = `

📋 选择一条OEE记录进行编辑

支持修改:产量、时间、效率等

`; break; // --- 机物料消耗检验 --- case 'importMaterial': modalTitle.textContent = '📤 导入 - 机物料消耗检验'; content = `
💡 导入说明:
• 必须包含字段:物料名称、规格型号、单位、期初库存、本期入库、本期出库、期末库存、金额(元)
`; break; case 'exportMaterial': modalTitle.textContent = '📥 导出 - 机物料消耗检验'; content = `
📋 导出说明:
• 将导出机物料消耗检验数据
`; break; case 'materialDetail': modalTitle.textContent = '✏️ 编辑 - 机物料消耗检验'; content = `

📋 选择一条物料记录进行编辑

支持修改:库存、出入库等

`; break; // --- 设备台账 --- case 'importDeviceCatalog': modalTitle.textContent = '📤 导入 - 设备台账'; content = `
💡 导入说明:
• 必须包含字段:设备编号、设备名称、规格型号、使用部门、购置日期、原值(元)、净值(元)、状态
`; break; case 'exportDeviceCatalog': modalTitle.textContent = '📥 导出 - 设备台账'; content = `
📋 导出说明:
• 将导出设备台账完整数据
`; break; case 'deviceCatalogDetail': modalTitle.textContent = '✏️ 编辑 - 设备台账'; content = `

📋 选择一条设备台账进行编辑

支持修改:设备信息、状态等

`; break; // --- 配件申购/采购 --- case 'importPartsPurchase': modalTitle.textContent = '📤 导入 - 配件申购/采购'; content = `
💡 导入说明:
• 必须包含字段:配件名称、规格型号、申请部门、申请数量、单价(元)、总价(元)、供应商、到货日期
`; break; case 'exportPartsPurchase': modalTitle.textContent = '📥 导出 - 配件申购/采购'; content = `
📋 导出说明:
• 将导出配件申购/采购数据
`; break; case 'partsPurchaseDetail': modalTitle.textContent = '✏️ 编辑 - 配件申购/采购'; content = `

📋 选择一条配件采购记录进行编辑

支持修改:数量、价格、供应商等

`; break; // --- 外委维修/保养 --- case 'importOutsourceRepair': modalTitle.textContent = '📤 导入 - 外委维修/保养'; content = `
💡 导入说明:
• 必须包含字段:设备名称、维修类型、承修单位、维修费用(元)、验收人、完成日期
`; break; case 'exportOutsourceRepair': modalTitle.textContent = '📥 导出 - 外委维修/保养'; content = `
📋 导出说明:
• 将导出外委维修/保养数据
`; break; case 'outsourceRepairDetail': modalTitle.textContent = '✏️ 编辑 - 外委维修/保养'; content = `

📋 选择一条外委维修记录进行编辑

支持修改:费用、承修单位等

`; break; // ====== 成本分析子页面(2个)====== // --- 预算 --- case 'importBudget': modalTitle.textContent = '📤 导入 - 预算'; content = `
💡 导入说明:
• 必须包含字段:项目名称、预算金额(元)、实际金额(元)、差异(元)、执行率(%)
`; break; case 'exportBudget': modalTitle.textContent = '📥 导出 - 预算'; content = `
📋 导出说明:
• 将导出预算数据
`; break; case 'budgetDetail': modalTitle.textContent = '✏️ 编辑 - 预算'; content = `

📋 选择一条预算记录进行编辑

支持修改:预算金额等

`; break; // --- 成本对比 --- case 'importCostCompare': modalTitle.textContent = '📤 导入 - 成本对比'; content = `
💡 导入说明:
• 必须包含字段:成本项目、本期金额(元)、上期金额(元)、同比变动(%)、环比变动(%)
`; break; case 'exportCostCompare': modalTitle.textContent = '📥 导出 - 成本对比'; content = `
📋 导出说明:
• 将导出成本对比数据
`; break; case 'costCompareDetail': modalTitle.textContent = '✏️ 编辑 - 成本对比'; content = `

📋 选择一条成本对比记录进行编辑

支持修改:金额等

`; break; // ====== 物耗成本分析(3个)====== case 'importMaterialCost': modalTitle.textContent = '📤 导入 - 物耗成本分析'; content = `
💡 导入说明:
• 必须包含字段:项目、2025年1-3月(万元)、2026年1-3月(万元)、2025年占产比(%)、2026年占产比(%)、增减幅度(%)
• 支持 .xlsx / .xls / .csv 格式
`; break; case 'exportMaterialCost': modalTitle.textContent = '📥 导出 - 物耗成本分析'; content = `
📋 导出说明:
• 将导出物耗成本分析数据,含各项目年度对比及增减幅度
`; break; case 'materialCost': modalTitle.textContent = '✏️ 编辑 - 物耗成本分析'; content = `

📋 选择一条物耗成本记录进行编辑

支持修改:项目金额、占比等

`; break; // ====== 管理费用分析(3个)====== case 'importAdminCost': modalTitle.textContent = '📤 导入 - 管理费用分析'; content = `
💡 导入说明:
• 必须包含字段:项目、2025年1-3月(万元)、2026年1-3月(万元)、2025年占产比(%)、2026年占产比(%)、增减幅度(%)
• 支持 .xlsx / .xls / .csv 格式
`; break; case 'exportAdminCost': modalTitle.textContent = '📥 导出 - 管理费用分析'; content = `
📋 导出说明:
• 将导出管理费用分析数据,含各费用项年度对比及增减幅度
`; break; case 'adminCost': modalTitle.textContent = '✏️ 编辑 - 管理费用分析'; content = `

📋 选择一条管理费用记录进行编辑

支持修改:项目金额、占比等

`; break; case 'schedule': modalTitle.textContent = '新增生产调度单'; content = `
💡 调度单说明:
• 调度单号自动生成,格式:SC+年月日+序号
• 新增后默认状态为"待排产"
• 排产后状态更新为"已排产"
`; break; } content += '
'; modalBody.innerHTML = content; modal.classList.add('show'); } // showCrudModal 实现在 M02 模块中(下方 script 块),此处为兼容降级 if (typeof showCrudModal === 'function') { /* 已被 M02 版本覆盖 */ } function __showCrudModalFallback(subPage, action) { const subNames = { shipment: '成品发货', production: '生产记录', yearlySales: '年度销售', yearlyShipment: '年度发货', currentSales: '当前销售', outputRate: '产出率', outputValue: '产值', capacity: '产能', completion: '完成率', batch: '批次', budget: '预算' }; const actionNames = {import: '导入', export: '导出', add: '新增', edit: '编辑'}; const msg = (actionNames[action] || action) + ' - ' + (subNames[subPage] || subPage); if (typeof showToast === 'function') { showToast(msg); } else if (typeof toast === 'function') { toast(msg); } else { alert(msg); } } function closeModal() { document.getElementById('modal').classList.remove('show'); } function saveData() { const type = currentModalType; const modal = document.getElementById('modal'); modal.classList.remove('show'); // 根据类型处理数据 switch(type) { case 'workshop': addWorkshopRow(); break; case 'production': addProductionRow(); break; case 'productionLedger': addProductionLedgerRow(); break; case 'wage': addWageRow(); break; case 'energy': addEnergyRow(); break; case 'schedule': addScheduleRow(); break; case 'exportProduction': showToast('📥 生产台账导出中,请稍候...'); setTimeout(() => showToast('✅ 导出成功!文件已保存'), 2000); break; case 'exportYearlySales': showToast('📥 品种历年销售统计导出中,请稍候...'); setTimeout(() => showToast('✅ 导出成功!文件已保存'), 2000); break; case 'exportEnergy': showToast('📥 能源消耗报表导出中...'); break; case 'exportCost': showToast('📥 成本分析报表导出中...'); break; case 'exportOEE': showToast('📥 设备OEE分析报表导出中...'); break; case 'exportWageReport': showToast('📥 工资报表导出中,请稍候...'); setTimeout(() => showToast('✅ 工资报表导出成功!'), 2000); break; case 'exportShipment': showToast('📥 成品发货趋势报表导出中,请稍候...'); setTimeout(() => showToast('✅ 导出成功!文件已保存'), 2000); break; case 'exportLog': showToast('📥 系统日志导出中...'); break; default: break; } showToast('✅ 操作成功!'); } // 新增车间数据 function addWorkshopRow() { const table = document.querySelector('#workshop-tab .data-table tbody'); const nameInput = document.querySelector('#modalBody input[placeholder="请输入车间名称"]'); const leaderInput = document.querySelector('#modalBody input[placeholder="请输入负责人"]'); const name = nameInput?.value || '新车间'; const id = 'WS' + String(Math.floor(Math.random() * 900) + 100); const row = `${id}${name}${leaderInput?.value || '待定'}生技部● 启用${new Date().toISOString().slice(0,10)}`; table.insertAdjacentHTML('beforeend', row); } // 新增生产单数据 function addProductionRow() { const table = document.querySelector('#production-tab .data-table tbody'); const productSelect = document.querySelector('#modalBody select:nth-of-type(1)'); const qtyInput = document.querySelector('#modalBody input[type="number"]'); const id = 'PS' + new Date().getFullYear() + String(Math.floor(Math.random() * 9000) + 1000); const row = `${id}${productSelect?.value || '新产品'}${qtyInput?.value || '0'}待定待定${new Date().toISOString().slice(0,10)}-待计算`; table.insertAdjacentHTML('beforeend', row); } // 新增生产台账记录 function addProductionLedgerRow() { const table = document.querySelector('#production-tab .data-table tbody'); const batchNo = document.getElementById('ledgerBatchNo')?.value || new Date().getTime().toString().slice(-6); const product = document.getElementById('ledgerProduct')?.value || ''; const qty = document.getElementById('ledgerQty')?.value || '0'; const convertedQty = document.getElementById('ledgerConvertedQty')?.value || qty; const workshop = document.getElementById('ledgerWorkshop')?.value || ''; const team = document.getElementById('ledgerTeam')?.value || ''; const startTime = document.getElementById('ledgerStartTime')?.value || ''; const endTime = document.getElementById('ledgerEndTime')?.value || ''; const outputValue = document.getElementById('ledgerOutputValue')?.value || '0'; const formatDate = (datetime) => { if (!datetime) return '-'; return datetime.replace('T', ' '); }; const row = ` ${batchNo} ${product} ${parseInt(qty).toLocaleString()} ${parseInt(convertedQty).toLocaleString()}盒 ${workshop} ${team} ${formatDate(startTime)} ${formatDate(endTime)} ${parseInt(outputValue).toLocaleString()} `; table.insertAdjacentHTML('beforeend', row); } // 新增工资记录 function addWageRow() { const table = document.querySelector('#wageEntry-tab .data-table tbody'); const nameInput = document.querySelector('#modalBody input[placeholder="请输入姓名"]'); const qtyInput = document.querySelector('#modalBody input[type="number"]'); const id = 'W' + String(Math.floor(Math.random() * 9000) + 1000); const qty = qtyInput?.value || '0'; const row = `${id}${nameInput?.value || '新员工'}待定待定${qty}待计算● 已录入`; table.insertAdjacentHTML('beforeend', row); } // 新增能耗记录 function addEnergyRow() { const table = document.querySelector('#energyEntry-tab .data-table tbody'); const row = `待定待定---待定-`; table.insertAdjacentHTML('beforeend', row); } // 新增调度单记录 function addScheduleRow() { const table = document.querySelector('#schedule-tab .data-table tbody'); const productSelect = document.querySelector('#scheduleProduct'); const qtyInput = document.querySelector('#scheduleQty'); const startInput = document.querySelector('#scheduleStartDate'); const endInput = document.querySelector('#scheduleEndDate'); const id = 'SC' + new Date().getFullYear() + String(Math.floor(Math.random() * 90000) + 10000); const qty = qtyInput?.value || '0'; const start = startInput?.value || new Date().toISOString().slice(0,10); const end = endInput?.value || new Date().toISOString().slice(0,10); const row = `${id}${productSelect?.value || '待定'}${qty}0
0%${start}${end}● 待排产`; table.insertAdjacentHTML('beforeend', row); } // 下载导入模板 function downloadTemplate(type) { const templates = { production: '生产数据导入模板:批次号,产品名称,生产数量,生产车间,生产班组,开工时间,完工时间', energy: '能耗数据导入模板:车间,能源类型,期初读数,期末读数,单价', wage: '工资数据导入模板:工号,姓名,车间,班组,计件数量,计件单价', cost: '成本数据导入模板:产品名称,直接材料,直接人工,能耗成本,制造费用,产量', device: '设备运行数据导入模板:设备名称,车间,工作时间,故障时间,合格品数' }; const content = templates[type] || '模板内容'; showToast('📥 ' + content.substring(0, 10) + '...模板下载功能开发中'); } function showToast(message) { const toast = document.getElementById('toast'); toast.textContent = message; toast.classList.add('show'); setTimeout(() => toast.classList.remove('show'), 3000); } // 加载车间数据 function loadWorkshopData() { const tbody = document.getElementById('workshopTableBody'); const data = [ {id: 'WS001', name: '前处理车间', leader: '待定', dept: '生技部', status: '启用', date: '2024-01-01'}, {id: 'WS002', name: '一车间', leader: '待定', dept: '生技部', status: '启用', date: '2024-01-01'}, {id: 'WS003', name: '二车间', leader: '待定', dept: '生技部', status: '启用', date: '2024-01-01'}, {id: 'WS004', name: '三车间', leader: '待定', dept: '生技部', status: '启用', date: '2024-01-01'}, {id: 'WS005', name: '公用工程', leader: '待定', dept: '生技部', status: '启用', date: '2024-01-01'} ]; tbody.innerHTML = data.map(item => ` ${item.id} ${item.name} ${item.leader} ${item.dept} ● ${item.status} ${item.date} `).join(''); } function editItem(type, id) { currentModalType = type; const modal = document.getElementById('modal'); const modalTitle = document.getElementById('modalTitle'); const modalBody = document.getElementById('modalBody'); let content = ''; switch(type) { case 'editWorkshop': modalTitle.textContent = '📝 编辑车间信息'; content = `
车间详情
编号:${id || 'WS001'}

基本信息

统计信息

5
设备数量
3
生产线数量
25
员工人数

备注说明

`; break; case 'editTeam': modalTitle.textContent = '📝 编辑班组信息'; content = `
班组详情
编号:${id || 'TM001'}

基本信息

班组配置

`; break; case 'editProduct': modalTitle.textContent = '📝 编辑产品信息'; content = `
产品详情
编号:${id || 'PD001'}

基本信息

生产信息

`; break; case 'editProcess': modalTitle.textContent = '📝 编辑工序信息'; content = `
工序详情
编号:${id || 'PR001'}

基本信息

工艺参数

`; break; case 'editEquipment': modalTitle.textContent = '📝 编辑设备信息'; content = `
设备详情
编号:${id || 'EQ001'}

基本信息

技术参数

`; break; case 'editEnergyType': modalTitle.textContent = '📝 编辑能源类型'; content = `
能源类型详情
编号:${id || 'EN001'}

基本信息

(吨)

统计信息

3%
占比
125
使用次数
¥3,280
本月费用
`; break; case 'editProduction': modalTitle.textContent = '📝 编辑生产单'; content = `
生产单详情
单号:${id || '260501'}

基本信息

产值信息

`; break; case 'editWage': modalTitle.textContent = '📝 编辑工资记录'; content = `
工资记录详情
工号:${id || 'P001'}

员工信息

工资构成

`; break; case 'editEnergy': modalTitle.textContent = '📝 编辑能源消耗记录'; content = `
能源消耗详情
记录:${id || 'REC001'}

基本信息

`; break; case 'editDevice': modalTitle.textContent = '📝 编辑设备运行记录'; content = `
设备运行详情
编号:${id || 'DR001'}

设备信息

OEE指标

85%
时间开动率
95%
性能开动率
99%
合格品率
80%
OEE
`; break; case 'editCost': modalTitle.textContent = '📝 编辑成本记录'; content = `
成本分析详情
月份:${id || '2026-05'}

成本构成

`; break; case 'editUser': modalTitle.textContent = '📝 编辑用户信息'; content = `
用户详情
工号:${id || 'U001'}

基本信息

`; break; case 'editStdWorkHours': modalTitle.textContent = '📝 编辑标准工时'; content = `
标准工时详情
编号:${id || 'WH001'}

基本信息

`; break; case 'editPerson': modalTitle.textContent = '📝 编辑人员信息'; content = `
人员详情
工号:${id || 'P001'}

基本信息

`; break; case 'editSchedule': modalTitle.textContent = '📝 编辑生产调度'; content = `
生产调度详情
单号:${id || 'SCH001'}

调度信息

`; break; case 'viewDeviceDetail': modalTitle.textContent = '🔧 设备运行详情'; content = `
设备运行详情
编号:${id || 'DR001'}

基本信息

运行时间分析

OEE三大指标

89.4%
时间开动率
85.0%
性能开动率
99.0%
合格品率
75.2%
OEE综合

生产统计

维护信息

备注说明

`; break; default: showAddModal(type); return; } modalBody.innerHTML = content; modal.classList.add('show'); } // 图表初始化 let chartsInitialized = false; function initCharts() { if (chartsInitialized) return; chartsInitialized = true; // 产值趋势图 new Chart(document.getElementById('productionTrendChart'), { type: 'line', data: { labels: ['1月', '2月', '3月', '4月', '5月'], datasets: [{ label: '产值(万元)', data: [12480, 12450, 13000, 12600, 13000], borderColor: '#667eea', backgroundColor: 'rgba(102, 126, 234, 0.1)', fill: true, tension: 0.4 }] }, options: { responsive: true, plugins: { legend: { display: false } } } }); // 车间产值饼图 new Chart(document.getElementById('workshopPieChart'), { type: 'doughnut', data: { labels: ['前处理', '一车间', '二车间', '三车间'], datasets: [{ data: [19.3, 25.2, 22.4, 33.1], backgroundColor: ['#667eea', '#764ba2', '#f093fb', '#4facfe'] }] }, options: { responsive: true } }); // 计件工资统计图 new Chart(document.getElementById('wageWorkshopChart'), { type: 'bar', data: { labels: ['前处理', '一车间', '二车间', '三车间', '公用工程'], datasets: [{ label: '工资总额(元)', data: [159860, 256789, 256520, 325632, 200012], backgroundColor: '#667eea' }] }, options: { responsive: true } }); // 能耗饼图 new Chart(document.getElementById('energyPieChart'), { type: 'pie', data: { labels: ['水', '电', '蒸汽', '天然气'], datasets: [{ data: [0.01, 87.8, 11.5, 0.7], backgroundColor: ['#4facfe', '#f093fb', '#667eea', '#764ba2'] }] }, options: { responsive: true } }); // 能耗趋势 new Chart(document.getElementById('energyTrendChart'), { type: 'line', data: { labels: ['1月', '2月', '3月', '4月', '5月'], datasets: [{ label: '能耗费用(元)', data: [445012, 0, 0, 0, 0], borderColor: '#667eea', backgroundColor: 'rgba(102, 126, 234, 0.1)', fill: true }] }, options: { responsive: true } }); // OEE趋势 new Chart(document.getElementById('oeeTrendChart'), { type: 'line', data: { labels: ['1月', '2月', '3月'], datasets: [{ label: 'OEE', data: [82.8, 80.7, 85.3], borderColor: '#667eea', fill: false }] }, options: { responsive: true } }); // 产值月度趋势图 if (document.getElementById('outputTrendChart')) { new Chart(document.getElementById('outputTrendChart'), { type: 'line', data: { labels: ['1月', '2月', '3月', '4月', '5月'], datasets: [{ label: '产值(万元)', data: [12480, 12450, 13000, 12600, 13000], borderColor: '#667eea', backgroundColor: 'rgba(102, 126, 234, 0.1)', fill: true, tension: 0.4 }] }, options: { responsive: true, plugins: { legend: { display: false } }, scales: { y: { beginAtZero: false, title: { display: true, text: '产值(万元)' } } } } }); } // 各车间产值占比 if (document.getElementById('outputWorkshopChart')) { new Chart(document.getElementById('outputWorkshopChart'), { type: 'doughnut', data: { labels: ['前处理车间', '一车间', '二车间', '三车间'], datasets: [{ data: [2380, 3200, 2800, 4100], backgroundColor: ['#667eea', '#764ba2', '#f093fb', '#4facfe'] }] }, options: { responsive: true, plugins: { legend: { position: 'right' }, tooltip: { callbacks: { label: (ctx) => ctx.label + ': ' + ctx.raw.toLocaleString() + '万元' } } } } }); } // 成本构成分析图 if (document.getElementById('costBreakdownChart')) { new Chart(document.getElementById('costBreakdownChart'), { type: 'pie', data: { labels: ['直接材料', '直接人工', '能耗成本', '制造费用'], datasets: [{ data: [13, 14, 13.18, 0], backgroundColor: ['#667eea', '#f093fb', '#4facfe', '#43e97b'] }] }, options: { responsive: true, plugins: { legend: { position: 'right' }, tooltip: { callbacks: { label: (ctx) => ctx.label + ': ' + ctx.raw + '万元' } } } } }); } } // 设备目录动态台账 const deviceCatalogColumns = [ { key: 'name', label: '设备名称', required: true }, { key: 'model', label: '规格型号' }, { key: 'code', label: '设备编号', required: true }, { key: 'manufacturer', label: '制造厂家' }, { key: 'location', label: '安装位置' }, { key: 'category', label: '设备分类' }, { key: 'purchaseDate', label: '购入日期' }, { key: 'status', label: '使用状态' }, { key: 'remark', label: '备注' } ]; let deviceCatalogRows = [ { id: 1, name: '炒药机', model: 'CYJ-800', code: 'QC-001', manufacturer: '浙江制药设备厂', location: '前处理', category: '前处理设备', purchaseDate: '2020-03', status: '正常', remark: '-' }, { id: 2, name: '粉碎机', model: 'FS-500', code: 'QC-002', manufacturer: '江苏机械厂', location: '前处理', category: '前处理设备', purchaseDate: '2020-05', status: '正常', remark: '-' }, { id: 3, name: '润药机', model: 'RY-300', code: 'QC-003', manufacturer: '山东设备公司', location: '前处理', category: '前处理设备', purchaseDate: '2021-01', status: '正常', remark: '-' }, { id: 4, name: '压片机1#', model: 'ZP-35', code: 'ZZ-001', manufacturer: '上海制药机械', location: '三车间', category: '制剂设备', purchaseDate: '2019-08', status: '正常', remark: '-' }, { id: 5, name: '压片机2#', model: 'ZP-35', code: 'ZZ-002', manufacturer: '上海制药机械', location: '三车间', category: '制剂设备', purchaseDate: '2019-08', status: '正常', remark: '-' }, { id: 6, name: '臭氧发生器', model: 'CF-G-5', code: 'GY-001', manufacturer: '环保设备厂', location: '公用工程', category: '公用工程设备', purchaseDate: '2018-12', status: '正常', remark: '-' } ]; const deviceCatalogReportFieldLabels = { category: '设备分类', model: '规格型号', location: '安装位置', manufacturer: '制造厂家', status: '使用状态', purchaseYear: '购入年份' }; let deviceCatalogEditingId = null; let deviceCatalogChart = null; let deviceCatalogChartType = 'bar'; function escapeHtml(value) { return String(value ?? '').replace(/[&<>"']/g, (match) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[match])); } function getUniqueDeviceValues(key) { return [...new Set(deviceCatalogRows.map(row => row[key]).filter(Boolean))].sort(); } function getFilteredDeviceCatalogRows() { const category = document.getElementById('deviceCatalogType')?.value || ''; const location = document.getElementById('deviceCatalogLocation')?.value || ''; const keyword = (document.getElementById('deviceCatalogKeyword')?.value || '').trim().toLowerCase(); return deviceCatalogRows.filter(row => { const text = [row.name, row.model, row.code, row.manufacturer, row.remark].join(' ').toLowerCase(); return (!category || row.category === category) && (!location || row.location === location) && (!keyword || text.includes(keyword)); }); } function renderDeviceCatalogModule() { const host = document.getElementById('deviceCatalog-tab'); if (!host) return; host.innerHTML = `
📑 设备目录
${deviceCatalogColumns.map(col => ``).join('')}
序号${col.label}操作
`; renderDeviceCatalogFilters(); renderDeviceCatalogChartFieldOptions(['category', 'model', 'location', 'status', 'manufacturer', 'purchaseYear'], 'category'); renderDeviceCatalogStats(); renderDeviceCatalogTable(); renderDeviceCatalogChart(); } function renderDeviceCatalogFilters() { const categorySelect = document.getElementById('deviceCatalogType'); const locationSelect = document.getElementById('deviceCatalogLocation'); if (!categorySelect || !locationSelect) return; categorySelect.innerHTML = '' + getUniqueDeviceValues('category').map(value => ``).join(''); locationSelect.innerHTML = '' + getUniqueDeviceValues('location').map(value => ``).join(''); } function renderDeviceCatalogChartFieldOptions(fields, selectedField) { const chartField = document.getElementById('deviceCatalogChartField'); if (!chartField) return; const normalizedFields = (fields && fields.length ? fields : ['category']) .filter((field, index, list) => list.indexOf(field) === index); chartField.innerHTML = normalizedFields .map(field => ``) .join(''); chartField.value = selectedField && normalizedFields.includes(selectedField) ? selectedField : normalizedFields[0]; } function renderDeviceCatalogStats() { const stats = document.getElementById('deviceCatalogStats'); if (!stats) return; const total = deviceCatalogRows.length; const categories = getUniqueDeviceValues('category').length; const locations = getUniqueDeviceValues('location').length; const normalRate = total ? Math.round(deviceCatalogRows.filter(row => row.status === '正常').length / total * 100) : 0; stats.innerHTML = `
${total}
设备总数
${categories}
设备分类数
${locations}
使用位置数
${normalRate}%
设备完好率
`; } function renderDeviceCatalogTable() { const body = document.getElementById('deviceCatalogTableBody'); if (!body) return; const rows = getFilteredDeviceCatalogRows(); if (!rows.length) { body.innerHTML = `
暂无匹配设备,可导入Excel或新增设备。
`; return; } body.innerHTML = rows.map((row, index) => deviceCatalogEditingId === row.id ? renderDeviceCatalogEditRow(row, index) : renderDeviceCatalogDisplayRow(row, index) ).join(''); } function renderDeviceCatalogDisplayRow(row, index) { return ` ${index + 1} ${deviceCatalogColumns.map(col => `${col.key === 'status' ? renderDeviceCatalogStatus(row[col.key]) : escapeHtml(row[col.key] || '-')}`).join('')}
`; } function renderDeviceCatalogEditRow(row, index) { return ` ${index + 1} ${deviceCatalogColumns.map(col => ` ${col.key === 'status' ? `` : `` } `).join('')}
`; } function renderDeviceCatalogStatus(status) { const normalized = status || '正常'; const cls = normalized === '正常' ? 'normal' : normalized === '维修中' ? 'repair' : 'stop'; return `● ${escapeHtml(normalized)}`; } function startDeviceCatalogEdit(id) { deviceCatalogEditingId = id; renderDeviceCatalogTable(); } function cancelDeviceCatalogEdit() { deviceCatalogEditingId = null; renderDeviceCatalogTable(); } function saveDeviceCatalogInlineEdit(id) { const row = deviceCatalogRows.find(item => item.id === id); const tr = [...document.querySelectorAll('#deviceCatalogTableBody tr')].find(item => item.querySelector(`[onclick="saveDeviceCatalogInlineEdit(${id})"]`)); if (!row || !tr) return; tr.querySelectorAll('[data-field]').forEach(input => { row[input.dataset.field] = input.value.trim() || '-'; }); if (!row.name || row.name === '-' || !row.code || row.code === '-') { showToast('请填写设备名称和设备编号'); return; } deviceCatalogEditingId = null; renderDeviceCatalogFilters(); renderDeviceCatalogStats(); renderDeviceCatalogTable(); renderDeviceCatalogChart(); showToast('设备信息已更新'); } function deleteDeviceCatalogRow(id) { if (!confirm('确认删除这条设备记录?')) return; deviceCatalogRows = deviceCatalogRows.filter(row => row.id !== id); deviceCatalogEditingId = null; renderDeviceCatalogFilters(); renderDeviceCatalogStats(); renderDeviceCatalogTable(); renderDeviceCatalogChart(); showToast('设备记录已删除'); } function openDeviceCatalogCreateModal() { const modal = document.getElementById('modal'); const modalTitle = document.getElementById('modalTitle'); const modalBody = document.getElementById('modalBody'); if (!modal || !modalTitle || !modalBody) return; modalTitle.textContent = '新增设备'; modalBody.innerHTML = ` ${deviceCatalogColumns.map(col => `
${col.key === 'status' ? '' : `` }
`).join('')}
`; modal.classList.add('show'); } function createDeviceCatalogRow(event) { event.preventDefault(); const form = event.target; const nextId = Math.max(0, ...deviceCatalogRows.map(row => row.id)) + 1; const row = { id: nextId }; deviceCatalogColumns.forEach(col => row[col.key] = (form.elements[col.key]?.value || '-').trim() || '-'); deviceCatalogRows.unshift(row); closeModal(); renderDeviceCatalogFilters(); renderDeviceCatalogStats(); renderDeviceCatalogTable(); renderDeviceCatalogChart(); showToast('设备已新增'); } function openDeviceCatalogReportModal() { const modal = document.getElementById('modal'); const modalTitle = document.getElementById('modalTitle'); const modalBody = document.getElementById('modalBody'); if (!modal || !modalTitle || !modalBody) return; modalTitle.textContent = '自定义统计报表'; modalBody.innerHTML = `
勾选需要统计的字段
选择图表展示类型
📊
分组柱状图
📈
月度折线图
🎯
环形饼图
筛选条件
`; modal.classList.add('show'); } function selectDeviceReportChartType(type, element) { const input = document.getElementById('deviceReportChartType'); if (input) input.value = type; document.querySelectorAll('.report-type-card').forEach(card => card.classList.remove('active')); element.classList.add('active'); } function generateDeviceCatalogReport() { const checkedFields = [...document.querySelectorAll('input[name="deviceReportField"]:checked')].map(item => item.value); const selectedField = checkedFields[0] || 'category'; const chartType = document.getElementById('deviceReportChartType')?.value || 'bar'; const category = document.getElementById('deviceReportCategory')?.value || ''; const categoryFilter = document.getElementById('deviceCatalogType'); renderDeviceCatalogChartFieldOptions(checkedFields.length ? checkedFields : ['category'], selectedField); if (categoryFilter) categoryFilter.value = category; deviceCatalogChartType = chartType; renderDeviceCatalogSelectedFields(checkedFields.length ? checkedFields : ['category']); renderDeviceCatalogTable(); renderDeviceCatalogChart(); closeModal(); showToast('统计报表已生成'); } function renderDeviceCatalogSelectedFields(fields) { const host = document.getElementById('deviceCatalogReportFields'); if (!host) return; host.classList.add('show'); host.innerHTML = ` 已选择字段 ${fields.map(field => `${escapeHtml(deviceCatalogReportFieldLabels[field] || field)}`).join('')} `; } function getDeviceCatalogReportFields() { const chips = [...document.querySelectorAll('#deviceCatalogReportFields .report-field-chip')].map(chip => chip.dataset.field).filter(Boolean); const chartField = document.getElementById('deviceCatalogChartField')?.value || 'category'; return chips.length ? chips : [chartField]; } function getDeviceCatalogChartTypeLabel() { return { bar: '分组柱状图', line: '月度折线图', doughnut: '环形饼图' }[deviceCatalogChartType] || '分组柱状图'; } function exportDeviceCatalogReport() { const canvas = document.getElementById('deviceCatalogBarChart'); if (!canvas || !deviceCatalogChart) { showToast('请先生成统计报表'); return; } const chartImage = canvas.toDataURL('image/png'); const html = ` 统计图表 统计图表 `; const blob = new Blob([html], { type: 'text/html;charset=utf-8' }); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = `设备目录统计报表_${new Date().toISOString().slice(0, 10)}.html`; link.click(); URL.revokeObjectURL(link.href); showToast('统计报表已导出'); } function showDeviceCatalogDetail(id) { const row = deviceCatalogRows.find(item => item.id === id); const modal = document.getElementById('modal'); const modalTitle = document.getElementById('modalTitle'); const modalBody = document.getElementById('modalBody'); if (!row || !modal || !modalTitle || !modalBody) return; modalTitle.textContent = '设备详情'; modalBody.innerHTML = `
${escapeHtml(row.name)}
${escapeHtml(row.code)}

基础信息

${deviceCatalogColumns.map(col => `
`).join('')}
`; modal.classList.add('show'); } function resetDeviceCatalogFilters() { const category = document.getElementById('deviceCatalogType'); const location = document.getElementById('deviceCatalogLocation'); const keyword = document.getElementById('deviceCatalogKeyword'); if (category) category.value = ''; if (location) location.value = ''; if (keyword) keyword.value = ''; renderDeviceCatalogTable(); renderDeviceCatalogChart(); } function getChartFieldValue(row, field) { if (field === 'purchaseYear') return String(row.purchaseDate || '').slice(0, 4) || '未填写'; return row[field] || '未填写'; } function renderDeviceCatalogChart() { const canvas = document.getElementById('deviceCatalogBarChart'); if (!canvas || !window.Chart) return; const field = document.getElementById('deviceCatalogChartField')?.value || 'category'; const counts = getFilteredDeviceCatalogRows().reduce((acc, row) => { const key = getChartFieldValue(row, field); acc[key] = (acc[key] || 0) + 1; return acc; }, {}); const labels = Object.keys(counts); const values = labels.map(label => counts[label]); if (deviceCatalogChart) deviceCatalogChart.destroy(); const chartType = deviceCatalogChartType || 'bar'; deviceCatalogChart = new Chart(canvas, { type: chartType, data: { labels, datasets: [{ label: '设备数量', data: values, borderColor: '#1677ff', borderWidth: chartType === 'line' ? 2 : 0, tension: 0.35, fill: chartType === 'line', backgroundColor: chartType === 'line' ? 'rgba(22, 119, 255, 0.14)' : ['#667eea', '#43e97b', '#f093fb', '#4facfe', '#764ba2', '#faad14', '#dc3545'] }] }, options: { responsive: true, plugins: { legend: { display: chartType === 'doughnut' } }, scales: chartType === 'doughnut' ? {} : { y: { beginAtZero: true, ticks: { precision: 0 } } } } }); } function mapDeviceCatalogImportRow(row, index) { const aliases = { name: ['设备名称', '名称', '设备/设施名称'], model: ['规格型号', '型号'], code: ['设备编号', '编号', '资产编号'], manufacturer: ['制造厂家', '厂家', '生产厂家'], location: ['安装位置', '位置', '使用车间', '车间'], category: ['设备分类', '分类', '类别'], purchaseDate: ['购入日期', '购买日期', '启用日期'], status: ['使用状态', '状态'], remark: ['备注', '说明'] }; const mapped = { id: Date.now() + index }; deviceCatalogColumns.forEach(col => { const foundKey = aliases[col.key].find(name => Object.prototype.hasOwnProperty.call(row, name)); mapped[col.key] = foundKey ? String(row[foundKey] ?? '').trim() : ''; }); mapped.name ||= `导入设备${index + 1}`; mapped.code ||= `IMPORT-${String(index + 1).padStart(3, '0')}`; mapped.status ||= '正常'; mapped.remark ||= '-'; return mapped; } function importDeviceCatalogExcel(event) { const file = event.target.files?.[0]; if (!file) return; const reader = new FileReader(); reader.onload = (loadEvent) => { try { const workbook = XLSX.read(loadEvent.target.result, { type: 'array' }); const sheet = workbook.Sheets[workbook.SheetNames[0]]; const rows = XLSX.utils.sheet_to_json(sheet, { defval: '' }); if (!rows.length) { showToast('Excel中没有可导入的数据'); return; } const importedRows = rows.map((row, index) => mapDeviceCatalogImportRow(row, deviceCatalogRows.length + index)); deviceCatalogRows = [...deviceCatalogRows, ...importedRows]; deviceCatalogEditingId = null; renderDeviceCatalogFilters(); renderDeviceCatalogStats(); renderDeviceCatalogTable(); renderDeviceCatalogChart(); showToast(`已追加导入 ${importedRows.length} 条设备记录,当前共 ${deviceCatalogRows.length} 条`); } catch (error) { showToast('导入失败,请检查Excel格式'); } finally { event.target.value = ''; } }; reader.readAsArrayBuffer(file); } function exportDeviceCatalogExcel() { if (!window.XLSX) { showToast('Excel组件加载失败,请检查网络'); return; } const rows = deviceCatalogRows.map((row, index) => ({ 序号: index + 1, 设备名称: row.name, 规格型号: row.model, 设备编号: row.code, 制造厂家: row.manufacturer, 安装位置: row.location, 设备分类: row.category, 购入日期: row.purchaseDate, 使用状态: row.status, 备注: row.remark })); const worksheet = XLSX.utils.json_to_sheet(rows); const workbook = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(workbook, worksheet, '设备目录'); XLSX.writeFile(workbook, `设备目录_${new Date().toISOString().slice(0, 10)}.xlsx`); showToast('已按最新数据导出Excel'); } // 页面加载时初始化 document.addEventListener('DOMContentLoaded', function() { renderDeviceCatalogModule(); try { loadWorkshopData(); } catch (error) { console.warn('车间数据初始化失败,不影响设备目录模块:', error); } try { initCharts(); } catch (error) { console.warn('图表初始化失败,不影响设备目录模块:', error); } });
取消
访问码: 3uieatwj