// BOM物料清单管理 (() => { let bomList = []; let currentPage = 1; const pageSize = 20; let editingId = null; Router.register('/plan-mgmt/bom', async () => { const html = `
产品编码 产品名称 物料编码 物料名称 单机用量 单位 最小包装 供应商 备注 操作
加载中...
`; setTimeout(() => { document.getElementById('add-bom-btn')?.addEventListener('click', () => openModal()); document.getElementById('import-bom-btn')?.addEventListener('click', () => showImportDialog()); document.getElementById('download-template-btn')?.addEventListener('click', () => downloadTemplate()); document.getElementById('batch-delete-btn')?.addEventListener('click', () => batchDelete()); document.getElementById('search-keyword')?.addEventListener('keypress', (e) => { if (e.key === 'Enter') BOM.search(); }); loadList(); }, 100); return html; }); function showImportDialog() { const input = document.createElement('input'); input.type = 'file'; input.accept = '.xlsx,.xls,.csv'; input.onchange = async (e) => { const file = e.target.files[0]; if (!file) return; const formData = new FormData(); formData.append('file', file); try { const overlay = document.getElementById('overlay'); overlay.classList.remove('hidden'); const res = await fetch('/api/bom/import', { method: 'POST', body: formData, credentials: 'include' }); overlay.classList.add('hidden'); const data = await res.json(); if (data.ok) { alert(data.message || '导入成功'); loadList(); } else { alert(data.error || '导入失败'); } } catch (err) { document.getElementById('overlay').classList.add('hidden'); alert('导入失败: ' + err.message); } }; input.click(); } function downloadTemplate() { const headers = ['产品编码', '产品名称', '物料编码', '物料名称', '单机用量', '单位', '最小包装', '供应商', '备注']; const example = ['AP05', 'AP05基站', 'PCB-001', '主控PCB板', '1', 'pcs', '10', '深圳电子', '']; const csvContent = [headers.join(','), example.join(',')].join('\n'); const blob = new Blob(['\ufeff' + csvContent], { type: 'text/csv;charset=utf-8;' }); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = 'BOM导入模板.csv'; link.click(); } async function loadList() { try { const res = await API.get('/api/bom'); bomList = res.list || []; renderList(); } catch (e) { console.error('加载BOM列表失败:', e); document.getElementById('bom-list').innerHTML = '加载失败'; } } function renderList() { const tbody = document.getElementById('bom-list'); const keyword = (document.getElementById('search-keyword')?.value || '').toLowerCase(); let filtered = bomList; if (keyword) { filtered = bomList.filter(item => (item.product_code || '').toLowerCase().includes(keyword) || (item.product_name || '').toLowerCase().includes(keyword) || (item.material_code || '').toLowerCase().includes(keyword) || (item.material_name || '').toLowerCase().includes(keyword) ); } const totalPages = Math.ceil(filtered.length / pageSize); const start = (currentPage - 1) * pageSize; const pageData = filtered.slice(start, start + pageSize); if (pageData.length === 0) { tbody.innerHTML = '暂无数据'; } else { tbody.innerHTML = pageData.map(item => ` ${escapeHtml(item.product_code || '')} ${escapeHtml(item.product_name || '')} ${escapeHtml(item.material_code || '')} ${escapeHtml(item.material_name || '')} ${item.unit_qty || 1} ${escapeHtml(item.unit || 'pcs')} ${item.min_package || 1} ${escapeHtml(item.supplier || '-')} ${escapeHtml(item.remark || '-')} `).join(''); } renderPagination(totalPages); } function renderPagination(totalPages) { const container = document.getElementById('pagination'); if (totalPages <= 1) { container.innerHTML = ''; return; } let html = ''; html += ``; html += `第 ${currentPage} / ${totalPages} 页`; html += ``; container.innerHTML = html; } function openModal(item = null) { editingId = item?.id || null; document.getElementById('modal-title').textContent = item ? '编辑BOM' : '新增BOM'; document.getElementById('product-code').value = item?.product_code || ''; document.getElementById('product-name').value = item?.product_name || ''; document.getElementById('material-code').value = item?.material_code || ''; document.getElementById('material-name').value = item?.material_name || ''; document.getElementById('unit-qty').value = item?.unit_qty || 1; document.getElementById('unit').value = item?.unit || 'pcs'; document.getElementById('min-package').value = item?.min_package || 1; document.getElementById('supplier').value = item?.supplier || ''; document.getElementById('remark').value = item?.remark || ''; document.getElementById('bom-modal').style.display = 'flex'; } function closeModal() { document.getElementById('bom-modal').style.display = 'none'; editingId = null; } async function save() { const data = { product_code: document.getElementById('product-code').value.trim(), product_name: document.getElementById('product-name').value.trim(), material_code: document.getElementById('material-code').value.trim(), material_name: document.getElementById('material-name').value.trim(), unit_qty: parseFloat(document.getElementById('unit-qty').value) || 1, unit: document.getElementById('unit').value.trim() || 'pcs', min_package: parseInt(document.getElementById('min-package').value) || 1, supplier: document.getElementById('supplier').value.trim(), remark: document.getElementById('remark').value.trim() }; if (!data.product_code || !data.product_name || !data.material_code || !data.material_name) { alert('请填写所有必填项'); return; } try { if (editingId) { await API.put(`/api/bom/${editingId}`, data); alert('更新成功'); } else { await API.post('/api/bom', data); alert('创建成功'); } closeModal(); loadList(); } catch (e) { alert(e.message || '操作失败'); } } async function edit(id) { const item = bomList.find(x => x.id === id); if (item) openModal(item); } async function deleteBom(id) { if (!confirm('确定要删除这条BOM吗?')) return; try { await API.delete(`/api/bom/${id}`); alert('删除成功'); loadList(); } catch (e) { alert(e.message || '删除失败'); } } async function batchDelete() { const checked = document.querySelectorAll('.row-checkbox:checked'); if (checked.length === 0) { alert('请先选择要删除的项'); return; } if (!confirm(`确定要删除选中的 ${checked.length} 条BOM吗?`)) return; const ids = Array.from(checked).map(cb => parseInt(cb.dataset.id)); try { await API.post('/api/bom/batch-delete', { ids }); alert('批量删除成功'); loadList(); } catch (e) { alert(e.message || '批量删除失败'); } } function toggleSelectAll(checkbox) { document.querySelectorAll('.row-checkbox').forEach(cb => cb.checked = checkbox.checked); } function search() { currentPage = 1; renderList(); } function resetSearch() { document.getElementById('search-keyword').value = ''; currentPage = 1; renderList(); } function goPage(page) { currentPage = page; renderList(); } function escapeHtml(str) { if (!str) return ''; return String(str).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); } window.BOM = { search, resetSearch, edit, delete: deleteBom, closeModal, save, toggleSelectAll, goPage }; })();