// 采购需求清单管理 (() => { let demandList = []; let productList = []; let currentPage = 1; const pageSize = 20; const statusMap = { 'pending': { text: '待处理', color: 'var(--warning)' }, 'ordered': { text: '已下单', color: 'var(--info)' }, 'received': { text: '已收货', color: 'var(--primary)' }, 'completed': { text: '已完成', color: 'var(--success)' }, 'cancelled': { text: '已取消', color: 'var(--text-2)' } }; Router.register('/plan-mgmt/purchase-demand', async () => { const html = `
需求编号 物料编码 物料名称 订单数量 BOM用量 总需求 期初库存 净需求 最小包装 实际采购 单位 供应商 状态 操作
加载中...
`; setTimeout(() => { document.getElementById('calc-demand-btn')?.addEventListener('click', () => openCalcModal()); document.getElementById('import-excel-btn')?.addEventListener('click', () => openImportModal()); document.getElementById('batch-delete-btn')?.addEventListener('click', () => batchDelete()); document.getElementById('search-keyword')?.addEventListener('keypress', (e) => { if (e.key === 'Enter') PurchaseDemand.search(); }); document.getElementById('excel-file')?.addEventListener('change', handleFileSelect); loadList(); loadProducts(); }, 0); return html; }); async function loadList() { try { const res = await API.get('/api/purchase-demand'); demandList = res.list || []; console.log('加载采购需求列表:', demandList.length, '条'); // 调试日志 console.log('前3条数据:', demandList.slice(0, 3)); // 调试日志 console.log('完整数据结构:', demandList[0]); // 查看第一条数据的完整结构 console.log('数据中的所有字段:', Object.keys(demandList[0] || {})); // 查看所有字段名 console.log('第一条数据的product_code:', demandList[0]?.product_code); // 直接查看第一条的product_code console.log('第二条数据的product_code:', demandList[1]?.product_code); // 直接查看第二条的product_code // 更新产品标签 updateProductTabs(); renderList(); } catch (e) { console.error('加载采购需求失败:', e); document.getElementById('demand-list').innerHTML = '加载失败'; } } function updateProductTabs() { // 提取所有产品编码 const products = new Set(); demandList.forEach((item, index) => { // 优先使用 product_code 字段,如果没有则从 demand_no 中提取 let productCode = item.product_code; console.log(`第${index + 1}条数据的product_code:`, productCode); // 调试每条数据 if (productCode) { products.add(productCode); } }); console.log('提取到的产品编码:', Array.from(products)); // 调试日志 console.log('检查数据是否有product_code字段:', demandList.some(item => 'product_code' in item)); // 检查字段是否存在 // 更新标签页 const tabsContainer = document.getElementById('product-tabs-container'); if (!tabsContainer) return; const productArray = Array.from(products).sort(); let html = ``; productArray.forEach(product => { const isActive = product === currentProduct ? 'active' : ''; html += ``; }); tabsContainer.innerHTML = html; } function switchProduct(productCode) { currentProduct = productCode; // 保存当前选中的产品 window.PurchaseDemand.currentProduct = productCode; // 也在全局对象中保存 // 更新标签样式 document.querySelectorAll('.product-tab').forEach(tab => { if (tab.dataset.product === productCode) { tab.classList.add('active'); } else { tab.classList.remove('active'); } }); renderList(); } async function loadProducts() { try { // 从采购需求表中获取产品列表 const res = await API.get('/api/purchase-demand'); const demandData = res.list || []; // 提取唯一的产品编码 const products = {}; demandData.forEach(item => { if (item.product_code) { products[item.product_code] = item.product_code; } }); productList = Object.keys(products).map(code => ({ product_code: code, product_name: code // 使用产品编码作为名称 })); console.log('从采购需求表加载到的产品列表:', productList); // 调试日志 const select = document.getElementById('product-select'); if (select) { select.innerHTML = '' + productList.map(p => ``).join(''); } } catch (e) { console.error('加载产品列表失败:', e); } } let currentProduct = ''; // 当前选中的产品 function toggleSelectAll(source) { const checkboxes = document.querySelectorAll('.row-checkbox'); checkboxes.forEach(checkbox => { checkbox.checked = source.checked; }); } function renderList() { const tbody = document.getElementById('demand-list'); const keyword = (document.getElementById('search-keyword')?.value || '').toLowerCase(); const filterStatus = document.getElementById('filter-status')?.value || ''; // 按产品分组 let grouped = {}; demandList.forEach(item => { const productCode = item.product_code || ''; if (!grouped[productCode]) { grouped[productCode] = []; } grouped[productCode].push(item); }); // 如果选择了特定产品,只显示该产品的数据 if (currentProduct) { grouped = currentProduct in grouped ? { [currentProduct]: grouped[currentProduct] } : {}; } // 合并所有数据用于搜索和过滤 let allData = []; Object.values(grouped).forEach(items => { allData = allData.concat(items); }); // 应用搜索过滤 if (keyword) { allData = allData.filter(item => (item.demand_no || '').toLowerCase().includes(keyword) || (item.material_code || '').toLowerCase().includes(keyword) || (item.material_name || '').toLowerCase().includes(keyword) ); } if (filterStatus) { allData = allData.filter(item => item.status === filterStatus); } // 分页 const totalPages = Math.ceil(allData.length / pageSize); const start = (currentPage - 1) * pageSize; const pageData = allData.slice(start, start + pageSize); if (pageData.length === 0) { tbody.innerHTML = '暂无数据'; } else { // 按产品分组渲染 let html = ''; let currentGroup = null; // 计算每个产品的总采购数量(跨所有页面) const productTotals = {}; allData.forEach(item => { if (item.product_code) { productTotals[item.product_code] = (productTotals[item.product_code] || 0) + (item.actual_purchase_qty || 0); } }); pageData.forEach(item => { if (item.product_code !== currentGroup) { currentGroup = item.product_code; // 只在有产品编码时才添加分组标题 if (currentGroup) { const productTotal = productTotals[currentGroup] || 0; html += ` 产品: ${currentGroup} 总采购数量: ${productTotal} `; } } const status = statusMap[item.status] || { text: item.status, color: 'var(--text-2)' }; html += ` ${escapeHtml(item.demand_no || '')} ${escapeHtml(item.material_code || '')} ${escapeHtml(item.material_name || '')} ${item.order_qty || 0} ${item.bom_unit_qty || 1} ${item.total_demand || 0} ${item.initial_stock || 0} ${item.net_demand || 0} ${item.min_package || 1} ${item.actual_purchase_qty || 0} ${escapeHtml(item.unit || 'pcs')} ${escapeHtml(item.supplier || '-')} ${status.text} `; }); tbody.innerHTML = html; } 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 openCalcModal() { document.getElementById('product-select').value = ''; document.getElementById('order-qty').value = 1; document.getElementById('calc-modal').style.display = 'flex'; // 重新加载产品列表 loadProducts(); } function closeCalcModal() { document.getElementById('calc-modal').style.display = 'none'; } async function doCalculate() { const productCode = document.getElementById('product-select').value; const orderQty = parseInt(document.getElementById('order-qty').value) || 0; const useInitialStock = document.getElementById('use-initial-stock').checked; if (!productCode) { alert('请选择产品'); return; } if (orderQty <= 0) { alert('订单数量必须大于0'); return; } try { // 基于现有采购需求重新计算 const res = await API.post('/api/purchase-demand/recalculate', { product_code: productCode, order_qty: orderQty, use_initial_stock: useInitialStock }); closeCalcModal(); alert(res.message || '计算完成'); loadList(); // 显示计算结果摘要 if (res.list && res.list.length > 0) { const totalPurchase = res.list.reduce((sum, item) => sum + (item.actual_purchase_qty || 0), 0); console.log(`采购需求重新计算完成,需求编号: ${res.demand_no},共 ${res.list.length} 种物料,总采购数量: ${totalPurchase}`); } } catch (e) { alert(e.message || '计算失败'); } } async function calcFromOrders() { if (!confirm('将根据所有客户订单自动计算采购需求,确定继续吗?')) { return; } try { const res = await API.post('/api/purchase-demand/calculate-from-orders', {}); alert(res.message || '计算完成'); loadList(); } catch (e) { alert(e.message || '计算失败'); } } let updatingId = null; function updateStatus(id) { updatingId = id; const item = demandList.find(x => x.id === id); document.getElementById('update-status').value = item?.status || 'pending'; document.getElementById('update-remark').value = item?.remark || ''; document.getElementById('status-modal').style.display = 'flex'; } function closeStatusModal() { document.getElementById('status-modal').style.display = 'none'; updatingId = null; } async function doUpdateStatus() { if (!updatingId) return; const status = document.getElementById('update-status').value; const remark = document.getElementById('update-remark').value.trim(); try { await API.put(`/api/purchase-demand/${updatingId}`, { status, remark }); closeStatusModal(); alert('更新成功'); loadList(); } catch (e) { alert(e.message || '更新失败'); } } async function deleteDemand(id) { if (!confirm('确定要删除这条采购需求吗?')) return; try { await API.delete(`/api/purchase-demand/${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} 条采购需求吗?`)) return; const ids = Array.from(checked).map(cb => parseInt(cb.dataset.id)); try { await API.post('/api/purchase-demand/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 = ''; document.getElementById('filter-status').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, '"'); } // Excel导入相关函数 let excelData = []; function openImportModal() { document.getElementById('excel-file').value = ''; document.getElementById('import-preview').style.display = 'none'; document.getElementById('import-excel-modal').style.display = 'flex'; } function closeImportModal() { document.getElementById('import-excel-modal').style.display = 'none'; excelData = []; } // 测试XLSX库 window.testXLSX = function() { console.log('测试XLSX库...'); console.log('typeof XLSX:', typeof XLSX); if (typeof XLSX !== 'undefined') { console.log('XLSX版本:', XLSX.version); API.toast('XLSX库已加载,版本: ' + XLSX.version); } else { console.error('XLSX库未加载'); // 尝试动态加载 loadXLSXLibrary(); } }; // 动态加载XLSX库 function loadXLSXLibrary() { if (typeof XLSX !== 'undefined') { return Promise.resolve(); } return new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = './assets/xlsx.min.js'; script.onload = () => { console.log('XLSX库动态加载成功,版本:', XLSX.version); API.toast('XLSX库加载成功'); resolve(); }; script.onerror = () => { console.error('XLSX库加载失败'); API.toast('XLSX库加载失败,请刷新页面重试', 'error'); reject(new Error('Failed to load XLSX library')); }; document.head.appendChild(script); }); } // 确保XLSX库已加载 async function ensureXLSXLoaded() { if (typeof XLSX === 'undefined') { try { await loadXLSXLibrary(); } catch (err) { throw err; } } } async function handleFileSelect(e) { const file = e.target.files[0]; if (!file) return; try { // 确保XLSX库已加载 await ensureXLSXLoaded(); // 检查文件大小 if (file.size > 10 * 1024 * 1024) { API.toast('文件大小不能超过10MB', 'error'); e.target.value = ''; return; } // 检查文件类型 const fileName = file.name.toLowerCase(); if (!fileName.endsWith('.xlsx') && !fileName.endsWith('.xls')) { API.toast('请选择Excel文件(.xlsx或.xls格式)', 'error'); e.target.value = ''; return; } // 读取Excel文件 const reader = new FileReader(); reader.onload = async function(e) { try { const data = new Uint8Array(e.target.result); const workbook = XLSX.read(data, { type: 'array' }); const firstSheet = workbook.Sheets[workbook.SheetNames[0]]; const jsonData = XLSX.utils.sheet_to_json(firstSheet, { header: 1 }); console.log('Excel解析结果:', jsonData); // 调试日志 // 验证数据格式 if (jsonData.length < 2) { API.toast('Excel文件没有数据', 'error'); return; } // 获取表头 const headers = jsonData[0]; console.log('表头:', headers); // 调试日志 const requiredHeaders = ['型号', '供应商', '期初库存', 'MOQ', '单机数量']; const missingHeaders = requiredHeaders.filter(h => !headers.includes(h)); if (missingHeaders.length > 0) { API.toast(`缺少必要的列:${missingHeaders.join(', ')}。当前列:${headers.join(', ')}`, 'error'); return; } // 解析数据 excelData = []; let emptyRows = 0; let validRows = 0; for (let i = 1; i < jsonData.length; i++) { const row = jsonData[i]; if (!row) { emptyRows++; continue; } // 获取各列的值 const model = String(row[headers.indexOf('型号')] || '').trim(); const supplier = String(row[headers.indexOf('供应商')] || '').trim(); const initial_stock = row[headers.indexOf('期初库存')] || 0; const moq = row[headers.indexOf('MOQ')] || 1; const per_unit_qty = row[headers.indexOf('单机数量')] || 1; // 跳过完全空白的行 if (!model && !supplier) { emptyRows++; continue; } const item = { model: model, supplier: supplier, initial_stock: parseInt(initial_stock) || 0, moq: parseInt(moq) || 1, per_unit_qty: parseInt(per_unit_qty) || 1 }; console.log(`第${i}行数据:`, item); // 调试日志 excelData.push(item); validRows++; } console.log(`总共处理 ${jsonData.length - 1} 行,有效数据 ${validRows} 条,空行 ${emptyRows} 条`); if (excelData.length === 0) { API.toast('Excel文件没有有效数据', 'error'); return; } // 显示预览 showPreview(excelData.slice(0, 5)); API.toast(`成功读取 ${excelData.length} 条数据`, 'success'); } catch (err) { console.error('解析Excel失败:', err); API.toast(`解析Excel文件失败:${err.message || '请检查文件格式'}`, 'error'); } }; reader.readAsArrayBuffer(file); } catch (err) { console.error('读取文件失败:', err); API.toast('读取文件失败', 'error'); } } function showPreview(data) { const tbody = document.getElementById('preview-tbody'); tbody.innerHTML = data.map(item => ` ${escapeHtml(item.model)} ${escapeHtml(item.supplier)} ${item.initial_stock} ${item.moq} ${item.per_unit_qty} `).join(''); document.getElementById('import-preview').style.display = 'block'; } async function edit(id) { try { const res = await API.get(`/api/purchase-demand/${id}`); const item = res; if (!item) { alert('未找到该采购需求'); return; } // 填充表单 document.getElementById('edit-material-code').value = item.material_code || ''; document.getElementById('edit-material-name').value = item.material_name || ''; document.getElementById('edit-order-qty').value = item.order_qty || 0; document.getElementById('edit-bom-unit-qty').value = item.bom_unit_qty || 0; document.getElementById('edit-initial-stock').value = item.initial_stock || 0; document.getElementById('edit-min-package').value = item.min_package || 1; document.getElementById('edit-actual-purchase-qty').value = item.actual_purchase_qty || 0; document.getElementById('edit-unit').value = item.unit || 'pcs'; document.getElementById('edit-supplier').value = item.supplier || ''; document.getElementById('edit-status').value = item.status || 'pending'; document.getElementById('edit-remark').value = item.remark || ''; // 保存当前编辑的ID window.PurchaseDemand.editingId = id; // 显示弹窗 document.getElementById('edit-modal').style.display = 'flex'; } catch (e) { console.error('获取数据失败:', e); alert('获取数据失败'); } } function closeEditModal() { document.getElementById('edit-modal').style.display = 'none'; window.PurchaseDemand.editingId = null; } async function saveEdit() { const id = window.PurchaseDemand.editingId; if (!id) { alert('无效的编辑ID'); return; } // 获取表单数据 const data = { order_qty: parseInt(document.getElementById('edit-order-qty').value) || 0, bom_unit_qty: parseFloat(document.getElementById('edit-bom-unit-qty').value) || 0, initial_stock: parseInt(document.getElementById('edit-initial-stock').value) || 0, min_package: parseInt(document.getElementById('edit-min-package').value) || 1, actual_purchase_qty: parseInt(document.getElementById('edit-actual-purchase-qty').value) || 0, unit: document.getElementById('edit-unit').value.trim() || 'pcs', supplier: document.getElementById('edit-supplier').value.trim(), status: document.getElementById('edit-status').value, remark: document.getElementById('edit-remark').value.trim() }; try { const res = await API.put(`/api/purchase-demand/${id}`, data); if (res.ok) { alert('保存成功'); closeEditModal(); loadList(); } else { alert(res.error || '保存失败'); } } catch (e) { console.error('保存失败:', e); alert('保存失败'); } } function openBatchEditStatusModal() { // 获取选中的复选框 const checkboxes = document.querySelectorAll('.row-checkbox:checked'); const selectedCount = checkboxes.length; // 更新选中数量显示 document.getElementById('selected-count').textContent = selectedCount; // 加载产品列表 loadProductOptions(); // 重置表单 document.getElementById('batch-status').value = ''; document.getElementById('batch-remark').value = ''; document.querySelector('input[name="batch-scope"][value="selected"]').checked = true; document.getElementById('batch-product-select-container').style.display = 'none'; // 添加事件监听 document.getElementById('batch-scope-all').onchange = function() { document.getElementById('batch-product-select-container').style.display = this.checked ? 'block' : 'none'; }; document.getElementById('batch-scope-selected').onchange = function() { document.getElementById('batch-product-select-container').style.display = this.checked ? 'none' : 'block'; }; // 显示弹窗 document.getElementById('batch-edit-status-modal').style.display = 'flex'; } function loadProductOptions() { // 获取所有产品 const products = new Set(); demandList.forEach(item => { if (item.product_code) { products.add(item.product_code); } }); const productSelect = document.getElementById('batch-product-select'); productSelect.innerHTML = ''; // 添加产品选项 Array.from(products).sort().forEach(product => { const option = document.createElement('option'); option.value = product; option.textContent = product; productSelect.appendChild(option); }); // 如果当前有选中的产品,设为默认值 if (currentProduct) { productSelect.value = currentProduct; } } function closeBatchEditStatusModal() { document.getElementById('batch-edit-status-modal').style.display = 'none'; } async function saveBatchEditStatus() { const status = document.getElementById('batch-status').value; const remark = document.getElementById('batch-remark').value.trim(); const scope = document.querySelector('input[name="batch-scope"]:checked').value; if (!status) { alert('请选择状态'); return; } let requestData = { status: status, remark: remark }; let confirmMessage = ''; if (scope === 'all') { // 更新指定产品的所有记录 const selectedProduct = document.getElementById('batch-product-select').value; if (!selectedProduct) { alert('请选择要更新的产品'); return; } requestData.product_code = selectedProduct; confirmMessage = `确定要将"${selectedProduct}"的所有记录状态更新为"${getStatusText(status)}"吗?`; } else { // 仅更新选中的记录 const checkboxes = document.querySelectorAll('.row-checkbox:checked'); const ids = Array.from(checkboxes).map(cb => parseInt(cb.dataset.id)); if (ids.length === 0) { alert('请选择要更新的记录'); return; } requestData.ids = ids; confirmMessage = `确定要将选中的 ${ids.length} 条记录状态更新为"${getStatusText(status)}"吗?`; } if (!confirm(confirmMessage)) { return; } try { const res = await API.post('/api/purchase-demand/batch-update-status', requestData); if (res.ok) { alert(`成功更新 ${res.count} 条记录`); closeBatchEditStatusModal(); loadList(); // 清除选择 document.getElementById('select-all').checked = false; } else { alert(res.error || '更新失败'); } } catch (e) { console.error('批量更新失败:', e); alert('更新失败'); } } function getStatusText(status) { const statusMap = { 'pending': '待处理', 'ordered': '已下单', 'received': '已收货', 'completed': '已完成', 'cancelled': '已取消' }; return statusMap[status] || status; } async function syncToInitialStock() { try { // 获取当前显示的数据 const res = await API.get('/api/purchase-demand'); let dataToSync = res.list || []; // 应用当前的筛选条件 const keyword = document.getElementById('search-keyword').value.trim(); const statusFilter = document.getElementById('filter-status').value; const currentProduct = window.PurchaseDemand.currentProduct || ''; if (currentProduct) { dataToSync = dataToSync.filter(item => item.product_code === currentProduct); } if (statusFilter) { dataToSync = dataToSync.filter(item => item.status === statusFilter); } if (keyword) { const lowerKeyword = keyword.toLowerCase(); dataToSync = dataToSync.filter(item => (item.material_code && item.material_code.toLowerCase().includes(lowerKeyword)) || (item.material_name && item.material_name.toLowerCase().includes(lowerKeyword)) || (item.supplier && item.supplier.toLowerCase().includes(lowerKeyword)) ); } if (dataToSync.length === 0) { alert('没有数据可同步'); return; } // 提取唯一物料 const uniqueMaterials = {}; dataToSync.forEach(item => { const key = item.material_code; if (!uniqueMaterials[key] || item.initial_stock > uniqueMaterials[key].initial_stock) { uniqueMaterials[key] = { material_code: item.material_code, material_name: item.material_name, stock_qty: item.initial_stock, unit: item.unit || 'pcs', min_package: item.min_package || 1, supplier: item.supplier || '' }; } }); const materialsToSync = Object.values(uniqueMaterials); if (!confirm(`确定要将 ${materialsToSync.length} 个物料的期初库存同步到期初库存管理吗?\n注意:如果物料已存在,将更新其库存数量。`)) { return; } const res2 = await API.post('/api/purchase-demand/sync-to-initial-stock', { materials: materialsToSync }); if (res2.ok) { alert(`成功同步 ${res2.success_count} 个物料的期初库存`); } else { alert(res2.error || '同步失败'); } } catch (e) { console.error('同步失败:', e); alert('同步失败'); } } async function deleteProductData() { const currentProduct = window.PurchaseDemand.currentProduct || ''; if (!currentProduct) { alert('请先选择一个产品'); return; } if (!confirm(`确定要删除产品 ${currentProduct} 的所有采购需求数据吗?\n此操作不可恢复!`)) { return; } try { const res = await API.post('/api/purchase-demand/delete-product', { product_code: currentProduct }); if (res.ok) { alert(`成功删除产品 ${currentProduct} 的 ${res.count} 条数据`); window.PurchaseDemand.currentProduct = ''; loadList(); } else { alert(res.error || '删除失败'); } } catch (e) { alert('删除失败:' + e.message); } } async function exportExcel() { try { // 获取当前显示的数据(包括筛选条件) const res = await API.get('/api/purchase-demand'); let dataToExport = res.list || []; // 应用当前的筛选条件 const keyword = document.getElementById('search-keyword').value.trim(); const statusFilter = document.getElementById('filter-status').value; const currentProduct = window.PurchaseDemand.currentProduct || ''; if (currentProduct) { dataToExport = dataToExport.filter(item => item.product_code === currentProduct); } if (statusFilter) { dataToExport = dataToExport.filter(item => item.status === statusFilter); } if (keyword) { const lowerKeyword = keyword.toLowerCase(); dataToExport = dataToExport.filter(item => (item.material_code && item.material_code.toLowerCase().includes(lowerKeyword)) || (item.material_name && item.material_name.toLowerCase().includes(lowerKeyword)) || (item.supplier && item.supplier.toLowerCase().includes(lowerKeyword)) ); } if (dataToExport.length === 0) { alert('没有数据可导出'); return; } // 确保XLSX库已加载 await ensureXLSXLoaded(); // 准备导出数据 const exportData = dataToExport.map(item => ({ '需求编号': item.demand_no || '', '产品编码': item.product_code || '', '物料编码': item.material_code || '', '物料名称': item.material_name || '', '订单数量': item.order_qty || 0, '单机用量': item.bom_unit_qty || 0, '总需求': item.total_demand || 0, '期初库存': item.initial_stock || 0, '净需求': item.net_demand || 0, '最小包装': item.min_package || 1, '实际采购数量': item.actual_purchase_qty || 0, '单位': item.unit || 'pcs', '供应商': item.supplier || '', '状态': getStatusText(item.status), '备注': item.remark || '', '创建时间': item.created_at || '', '创建人': item.created_by || '' })); // 创建工作簿 const ws = XLSX.utils.json_to_sheet(exportData); const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "采购需求清单"); // 设置列宽 const colWidths = [ { wch: 20 }, // 需求编号 { wch: 12 }, // 产品编码 { wch: 20 }, // 物料编码 { wch: 30 }, // 物料名称 { wch: 10 }, // 订单数量 { wch: 10 }, // 单机用量 { wch: 10 }, // 总需求 { wch: 10 }, // 期初库存 { wch: 10 }, // 净需求 { wch: 10 }, // 最小包装 { wch: 15 }, // 实际采购数量 { wch: 8 }, // 单位 { wch: 20 }, // 供应商 { wch: 10 }, // 状态 { wch: 20 }, // 备注 { wch: 20 }, // 创建时间 { wch: 10 } // 创建人 ]; ws['!cols'] = colWidths; // 生成文件名 const now = new Date(); const timestamp = now.getFullYear() + String(now.getMonth() + 1).padStart(2, '0') + String(now.getDate()).padStart(2, '0') + '_' + String(now.getHours()).padStart(2, '0') + String(now.getMinutes()).padStart(2, '0'); const filename = `采购需求清单_${currentProduct || '全部'}_${timestamp}.xlsx`; // 导出文件 XLSX.writeFile(wb, filename); console.log(`成功导出 ${dataToExport.length} 条数据到 ${filename}`); } catch (err) { console.error('导出失败:', err); alert('导出失败,请稍后重试'); } } function getStatusText(status) { const statusMap = { 'pending': '待处理', 'ordered': '已下单', 'received': '已收货', 'completed': '已完成', 'cancelled': '已取消' }; return statusMap[status] || status; } async function importExcel() { if (excelData.length === 0) { API.toast('没有可导入的数据', 'error'); return; } // 获取产品编码 const productCode = document.getElementById('import-product-code').value.trim(); if (!productCode) { API.toast('请输入产品编码', 'error'); return; } try { // 确保XLSX库已加载(虽然这里不需要,但为了保持一致性) await ensureXLSXLoaded(); console.log('准备导入的数据:', excelData); // 调试日志 console.log('产品编码:', productCode); // 调试日志 const res = await API.post('/api/purchase-demand/import', { data: excelData, product_code: productCode }); console.log('导入响应:', res); // 调试日志 if (res.ok) { API.toast(`成功导入 ${res.count || excelData.length} 条数据`, 'success'); if (res.errors && res.errors.length > 0) { console.log('导入错误:', res.errors); } closeImportModal(); loadList(); // 重新加载列表 } else { API.toast(res.error || '导入失败', 'error'); } } catch (err) { console.error('导入失败:', err); API.toast('导入失败,请稍后重试', 'error'); } } window.PurchaseDemand = { search, resetSearch, delete: deleteDemand, updateStatus, closeCalcModal, doCalculate, openImportModal, closeImportModal, handleFileSelect, importExcel, exportExcel, deleteProductData, edit, closeEditModal, saveEdit, openBatchEditStatusModal, closeBatchEditStatusModal, saveBatchEditStatus, syncToInitialStock, toggleSelectAll, switchProduct, goPage }; })();