function batchDelete() { if (!hasPerm('deleteShip')) { showToast('无删除权限', 'err'); return; } const ids = Array.from(document.querySelectorAll('.row-check:checked')).map(cb => cb.value); if (!ids.length) { showToast('请先勾选要删除的货件', 'warn'); return; } if (!confirm(`确认删除选中的 ${ids.length} 条货件?此操作不可撤销。`)) return; if (window.api) { api.batchDelete(ids).then(() => { shipments = shipments.filter(s => !ids.includes(s.id)); ids.forEach(id => delete riskResults[id]); recalcAll(false); renderList(); renderStats(); showToast(`✓ 已删除 ${ids.length} 条货件`); }).catch(err => showToast('删除失败:' + err.message, 'err')); return; } // 本地模式 shipments = shipments.filter(s => !ids.includes(s.id)); ids.forEach(id => delete riskResults[id]); saveShipments(); recalcAll(false); renderList(); renderStats(); showToast(`✓ 已删除 ${ids.length} 条货件`); } function initAuth() { // ── API 模式:检查 JWT token ──────────────────────────────────────────── if (window.api && window.isApiLoggedIn && window.isApiLoggedIn()) { api.getMe().then(user => { currentUser = user; document.getElementById('login-screen').style.display = 'none'; document.getElementById('header-username').textContent = user.displayName || user.username; applyPermissions(); _loadAllFromAPI(); }).catch(() => { // Token 失效,显示登录界面 window.apiClearToken && window.apiClearToken(); document.getElementById('login-screen').style.display = 'flex'; }); return; } // ── 本地模式兜底 ────────────────────────────────────────────────────── if (!users || users.length === 0) { const setupModal = document.getElementById('modal-first-run'); if (setupModal) setupModal.style.display = 'flex'; document.getElementById('login-screen').style.display = 'none'; return; } document.getElementById('login-screen').style.display = 'flex'; const sess = sessionStorage.getItem('fsp_session'); if (sess) { try { const s = JSON.parse(sess); const u = users.find(x => x.id === s.id && x.username === s.username && x.active); if (u) { currentUser = u; document.getElementById('login-screen').style.display = 'none'; document.getElementById('header-username').textContent = u.displayName || u.username; applyPermissions(); return; } } catch(e) {} } } // ── 从 API 加载所有核心数据 ──────────────────────────────────────────────── async function _loadAllFromAPI() { if (!window.api) return; try { showToast('正在从服务器加载数据...', 'info'); const [ apiShipments, apiChannels, apiCustomersList, apiSuppliersList, apiCarriersList, apiAR, apiAP, apiLeads, apiContracts, apiForex ] = await Promise.all([ api.getShipments().catch(()=>[]), api.getChannels().catch(()=>[]), api.getCustomers().catch(()=>[]), api.getSuppliers().catch(()=>[]), api.getCarriers().catch(()=>[]), api.getAR().catch(()=>[]), api.getAP().catch(()=>[]), api.getLeads().catch(()=>[]), api.getContracts().catch(()=>[]), api.getForex().catch(()=>[]), ]); shipments = apiShipments; channels = apiChannels.length ? apiChannels : channels; customers = apiCustomersList.length ? apiCustomersList : customers; receivables = apiAR; payables = apiAP; crmLeads = apiLeads; crmContracts = apiContracts; if (apiForex.length) { apiForex.forEach(f => { forexRates[f.base] = f.rate; }); } recalcAll(false); renderTableHeader(); setFilter('all'); showToast('✅ 数据加载完成,共 ' + shipments.length + ' 票货件'); } catch(e) { console.error('API load error:', e); showToast('数据加载部分失败,使用本地缓存', 'warn'); } }
| 公司名称 | 联系人 | 电话/微信 | 货运需求 | 跟进阶段 | 预计月票 | 最近跟进 | 下次跟进 | 备注 | 操作 |
|---|