Router.register('/upload/shipments', async () => { setTimeout(() => { const manualStatus = document.getElementById('ship-manual-status'); const defaultPlatformNameMap = {pdd: '拼多多', yt: '圆通', tx: '兔喜', mt: '美团', drf: '大润发', std: '标准版'}; const renderPlatformOptions = (selectEl, list, selectedValue = '') => { if (!selectEl) return; const optsHtml = [ '', ...(list || []).map(o => ``) ].join(''); selectEl.innerHTML = optsHtml; if (selectedValue) selectEl.value = selectedValue; }; const loadPlatforms = async (selectedManual = '', selectedImport = '') => { try { const res = await API.listShipmentPlatforms(); const list = (res && res.list) ? res.list : []; renderPlatformOptions(document.getElementById('ship-manual-platform'), list, selectedManual); renderPlatformOptions(document.getElementById('ship-platform'), list, selectedImport); } catch (e) { // 失败时不阻塞页面,保持当前 options(可能是旧的静态内容) console.warn('加载机种列表失败:', e); } }; const showManageModal = async () => { let list = []; try { const res = await API.listShipmentPlatforms(); list = (res && res.list) ? res.list : []; } catch (e) { API.toast('加载机种失败'); return; } const custom = list.filter(x => !x.is_default); const modal = document.createElement('div'); modal.id = 'ship-platform-manage-modal'; modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.5);z-index:1000;display:flex;align-items:center;justify-content:center;padding:20px'; modal.innerHTML = `
机种管理(仅自定义可删除)
${custom.length ? `
共 ${custom.length} 个自定义机种
` : `
暂无自定义机种
`}
`; document.body.appendChild(modal); const close = () => modal.remove(); modal.addEventListener('click', (e) => { if (e.target === modal) close(); }); modal.querySelector('#ship-platform-manage-close')?.addEventListener('click', close); modal.querySelector('#ship-platform-manage-add')?.addEventListener('click', async () => { const name = (prompt('请输入自定义机种名称') || '').trim(); if (!name) return; try { const added = await API.addShipmentPlatform(name); if (added && added.ok) { API.toast('已添加机种:' + (added.label || name)); close(); await loadPlatforms(added.value, added.value); } } catch (e) {} }); modal.querySelectorAll('.ship-platform-delete').forEach(btn => { btn.addEventListener('click', async () => { const code = btn.dataset.code; const label = btn.closest('li')?.querySelector('div div')?.textContent || code; if (!confirm(`确定删除自定义机种:${label}?\n\n删除后下拉将不再显示该机种。`)) return; try { const r = await API.deleteShipmentPlatform(code); if (r && r.ok) { API.toast('删除成功'); close(); await loadPlatforms(); } } catch (e) {} }); }); }; // 设置默认日期为今天 const dateInput = document.getElementById('ship-date'); if (dateInput && !dateInput.value) { const today = new Date().toISOString().split('T')[0]; dateInput.value = today; } // 动态加载机种下拉,并支持自定义新增 loadPlatforms(); document.getElementById('ship-manage-platforms')?.addEventListener('click', showManageModal); // 手动录入提交 const btn = document.getElementById('ship-upload'); btn?.addEventListener('click', async () => { const date = document.getElementById('ship-date').value; const qty = parseInt(document.getElementById('ship-qty').value || '0', 10); const to = document.getElementById('ship-to').value; const platform = document.getElementById('ship-manual-platform').value; const boxNo = document.getElementById('ship-box-no').value.trim(); // 验证必填字段 if (!date) { manualStatus.textContent = '✗ 请选择发货日期'; manualStatus.className = 'error'; return; } if (!platform) { manualStatus.textContent = '✗ 请选择机种类型'; manualStatus.className = 'error'; return; } if (!to) { manualStatus.textContent = '✗ 请输入接收方'; manualStatus.className = 'error'; return; } if (qty <= 0) { manualStatus.textContent = '✗ 数量必须大于0'; manualStatus.className = 'error'; return; } try { manualStatus.textContent = '提交中...'; manualStatus.className = ''; const payload = { date, qty, to, platform }; if (boxNo) { payload.box_no = boxNo; } await API.uploadShipments(payload); const platformName = defaultPlatformNameMap[platform] || platform; manualStatus.textContent = `✓ 录入成功!机种:${platformName},数量:${qty}`; manualStatus.className = 'success'; // 清空表单(保留日期和机种) document.getElementById('ship-qty').value = ''; document.getElementById('ship-to').value = ''; document.getElementById('ship-box-no').value = ''; } catch(e) { manualStatus.textContent = '✗ 录入失败:' + (e.message || '未知错误'); manualStatus.className = 'error'; } }); const fileInput = document.getElementById('ship-file'); const validateBtn = document.getElementById('ship-validate'); const uploadFileBtn = document.getElementById('ship-upload-file'); const fileStatus = document.getElementById('ship-file-status'); fileInput?.addEventListener('change', () => { fileStatus.textContent = ''; fileStatus.className = ''; }); validateBtn?.addEventListener('click', async () => { const file = fileInput?.files?.[0]; if (!file) { fileStatus.textContent = '请先选择文件'; fileStatus.className = 'error'; return; } const formData = new FormData(); formData.append('file', file); try { const res = await fetch('/api/validate/shipments-file', { method: 'POST', body: formData, credentials: 'include' }); if (!res.ok) { const text = await res.text(); throw new Error(`HTTP ${res.status}: ${text}`); } const data = await res.json(); if (data.valid) { fileStatus.textContent = '✓ ' + data.message; fileStatus.className = 'success'; } else { fileStatus.textContent = '✗ ' + data.message; fileStatus.className = 'error'; } } catch (e) { fileStatus.textContent = '验证失败:' + e.message; fileStatus.className = 'error'; } }); uploadFileBtn?.addEventListener('click', async () => { const file = fileInput?.files?.[0]; const platform = document.getElementById('ship-platform')?.value; if (!platform) { fileStatus.textContent = '✗ 请选择机种类型'; fileStatus.className = 'error'; return; } if (!file) { fileStatus.textContent = '✗ 请先选择文件'; fileStatus.className = 'error'; return; } const formData = new FormData(); formData.append('file', file); formData.append('platform', platform); try { fileStatus.textContent = '上传中...'; fileStatus.className = ''; const res = await fetch('/api/upload/shipments-file', { method: 'POST', body: formData, credentials: 'include' }); if (!res.ok) { const text = await res.text(); throw new Error(`HTTP ${res.status}: ${text}`); } const data = await res.json(); if (data.ok) { const platformName = defaultPlatformNameMap[platform] || platform; fileStatus.textContent = `✓ 上传成功!机种:${platformName},共导入${data.count}个箱次,${data.total_qty}个SN`; fileStatus.className = 'success'; fileInput.value = ''; document.getElementById('ship-platform').value = ''; } else { fileStatus.textContent = '✗ ' + (data.error || '上传失败'); fileStatus.className = 'error'; } } catch (e) { fileStatus.textContent = '上传失败:' + e.message; fileStatus.className = 'error'; } }); }, 0); return `
手动录入
用于快速录入发货汇总信息(不含详细SN)
详细记录批量导入
📋 文件格式要求
• 必需列:出货日期、箱号、SN1、SN2、...、SN20
• 出货日期列支持合并单元格
• 支持格式:Excel (.xlsx, .xls) 或 CSV
`; });