// 期初库存管理
(() => {
let stockList = [];
let currentPage = 1;
const pageSize = 20;
let editingId = null;
Router.register('/plan-mgmt/initial-stock', async () => {
const html = `
`;
setTimeout(() => {
document.getElementById('add-stock-btn')?.addEventListener('click', () => openModal());
document.getElementById('import-stock-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') InitialStock.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/initial-stock/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 = ['PCB-001', '主控PCB板', '50', '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 = '期初库存导入模板.csv';
link.click();
}
async function loadList() {
try {
const res = await API.get('/api/initial-stock');
stockList = res.list || [];
renderList();
} catch (e) {
console.error('加载库存列表失败:', e);
document.getElementById('stock-list').innerHTML = '| 加载失败 |
';
}
}
function renderList() {
const tbody = document.getElementById('stock-list');
const keyword = (document.getElementById('search-keyword')?.value || '').toLowerCase();
let filtered = stockList;
if (keyword) {
filtered = stockList.filter(item =>
(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.material_code || '')} |
${escapeHtml(item.material_name || '')} |
${item.stock_qty || 0} |
${escapeHtml(item.unit || 'pcs')} |
${item.min_package || 1} |
${escapeHtml(item.supplier || '-')} |
${escapeHtml(item.remark || '-')} |
${formatTime(item.updated_at)} |
|
`).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 ? '编辑期初库存' : '新增期初库存';
document.getElementById('material-code').value = item?.material_code || '';
document.getElementById('material-name').value = item?.material_name || '';
document.getElementById('stock-qty').value = item?.stock_qty || 0;
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('stock-modal').style.display = 'flex';
}
function closeModal() {
document.getElementById('stock-modal').style.display = 'none';
editingId = null;
}
async function save() {
const data = {
material_code: document.getElementById('material-code').value.trim(),
material_name: document.getElementById('material-name').value.trim(),
stock_qty: parseInt(document.getElementById('stock-qty').value) || 0,
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.material_code || !data.material_name) {
alert('请填写物料编码和物料名称');
return;
}
try {
if (editingId) {
await API.put(`/api/initial-stock/${editingId}`, data);
alert('更新成功');
} else {
await API.post('/api/initial-stock', data);
alert('创建成功');
}
closeModal();
loadList();
} catch (e) {
alert(e.message || '操作失败');
}
}
async function edit(id) {
const item = stockList.find(x => x.id === id);
if (item) openModal(item);
}
async function deleteStock(id) {
if (!confirm('确定要删除这条库存记录吗?')) return;
try {
await API.delete(`/api/initial-stock/${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/initial-stock/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 formatTime(ts) {
if (!ts) return '-';
try {
const d = new Date(ts);
return d.toLocaleString('zh-CN', { hour12: false });
} catch {
return ts;
}
}
function escapeHtml(str) {
if (!str) return '';
return String(str).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"');
}
window.InitialStock = {
search,
resetSearch,
edit,
delete: deleteStock,
closeModal,
save,
toggleSelectAll,
goPage
};
})();