(function(){
"use strict";
const csrf = document.querySelector('meta[name="staff-csrf-token"]').content;
const fileInput = document.getElementById('order-file');
const fileName = document.getElementById('file-name');
const form = document.getElementById('order-import-form');
const status = document.getElementById('import-status');
const results = document.getElementById('import-results');
const confirmButton = document.getElementById('confirm-import');
let selectedFile = null;
let canConfirm = false;
let orderBefore = null;
function setStatus(message, isError){
status.textContent = message || '';
status.classList.toggle('is-error', Boolean(isError));
}
function escapeText(value){
return String(value == null ? '' : value).replace(/[&<>"']/g, function(character){
return {'&':'&','<':'<','>':'>','"':'"',"'":'''}[character];
});
}
function showResponse(data){
results.hidden = false;
const summary = data.summary || {};
document.getElementById('import-summary').innerHTML = [
['文件数据行', summary.total_rows], ['有效行', summary.valid_rows],
['重复保留', summary.duplicate_rows], ['错误行', summary.error_rows]
].map(function(item){
return '
'+escapeText(item[0])+''+escapeText(item[1] || 0)+'
';
}).join('');
document.getElementById('preview-rows').innerHTML = (data.preview || []).map(function(row){
return '| '+escapeText(row.row)+' | '+escapeText(row.tenant_id)+' | '+escapeText(row.order_no)+' | '+escapeText(row.status)+' | '+escapeText(row.inbound_code)+' | '+(row.duplicate ? '已存在,保留原数据' : '可新增')+' |
';
}).join('') || '| 没有可预览的有效数据 |
';
document.getElementById('preview-limit-note').hidden = !data.preview_limited;
const errors = document.getElementById('import-errors');
errors.innerHTML = (data.errors || []).map(function(item){
return '第 '+escapeText(item.row)+' 行:'+escapeText(item.message)+'
';
}).join('');
errors.hidden = !data.errors || !data.errors.length;
document.getElementById('results-caption').textContent = errors.hidden
? '请确认租户、订单号及重复记录处理方式。'
: '发现错误行;请修正文件后重新上传,当前不会写入数据。';
canConfirm = Boolean(data.ok && summary.valid_rows);
confirmButton.hidden = !canConfirm;
}
async function sendFile(path){
const body = new FormData();
body.append('file', selectedFile);
const response = await fetch(path, {method:'POST', body:body, credentials:'same-origin',
headers:{'X-CSRF-Token':csrf, 'Accept':'application/json'}, signal:AbortSignal.timeout(30000)});
const data = await response.json();
if(!response.ok && !data.summary) throw new Error(data.error || '请求失败,请稍后重试。');
return data;
}
async function requestJson(path, options){
options = options || {};
options.credentials = 'same-origin';
options.headers = Object.assign({'Accept':'application/json'}, options.headers || {});
const response = await fetch(path, options);
const data = await response.json();
if(!response.ok) throw new Error(data.error || '请求失败,请稍后重试。');
return data;
}
function renderOrders(data, append){
const body = document.getElementById('order-list-rows');
const rows = (data.orders || []).map(function(row){
return '| '+escapeText(row.tenant_id)+' | '+escapeText(row.order_no)+' | '+escapeText(row.status)+' | '+escapeText(row.inbound_code)+' | '+escapeText(row.shipment_id)+' | '+escapeText(row.country_code)+' | '+escapeText(row.source_updated_at)+' |
';
}).join('');
if(!append) body.innerHTML = '';
body.insertAdjacentHTML('beforeend', rows || (!append ? '| 没有找到订单 |
' : ''));
orderBefore = data.next_before || null;
document.getElementById('load-more-orders').hidden = !orderBefore;
document.getElementById('order-list-status').textContent = '共 '+escapeText(data.total || 0)+' 条订单';
}
async function loadOrders(append){
const params = new URLSearchParams({q:document.getElementById('order-query').value.trim(), tenant:document.getElementById('order-tenant').value.trim(), status:document.getElementById('order-status').value, limit:'50'});
if(append && orderBefore) params.set('before', orderBefore);
const data = await requestJson('/api/admin/orders/list?'+params.toString());
renderOrders(data, append);
}
async function loadBatches(){
const data = await requestJson('/api/admin/orders/import/batches');
document.getElementById('import-batch-rows').innerHTML = (data.batches || []).map(function(batch){
const active = batch.state === 'active';
const action = active ? '' : '已撤销(删 '+escapeText(batch.deleted_rows)+',留 '+escapeText(batch.preserved_on_undo)+')';
return '| '+escapeText(batch.imported_at)+' | '+escapeText(batch.filename)+' | '+escapeText(batch.imported_by)+' | '+escapeText(batch.imported_rows)+' | '+escapeText(batch.preserved_rows)+' | '+escapeText(active ? '可撤销' : '已撤销')+' | '+action+' |
';
}).join('') || '| 暂无导入记录 |
';
document.querySelectorAll('.batch-undo').forEach(function(button){
button.addEventListener('click', async function(){
if(!await SupportUI.confirm('撤销后只会删除本批次中仍未被修改的新增订单,确定继续吗?',{title:'撤销导入'})) return;
button.disabled = true;
try{
const result = await requestJson('/api/admin/orders/import/batches/'+encodeURIComponent(button.dataset.batchId)+'/undo',{method:'POST',headers:{'X-CSRF-Token':csrf}});
setStatus('批次已处理:删除 '+result.deleted+' 条,保留 '+result.preserved+' 条。');
await Promise.all([loadBatches(), loadOrders(false)]);
}catch(error){ setStatus(error.message, true); button.disabled = false; }
});
});
}
fileInput.addEventListener('change', function(){
selectedFile = fileInput.files[0] || null;
fileName.textContent = selectedFile ? selectedFile.name : '选择订单文件';
results.hidden = true;
canConfirm = false;
confirmButton.hidden = true;
setStatus('');
});
form.addEventListener('submit', async function(event){
event.preventDefault();
if(!selectedFile) return;
const button = document.getElementById('preview-import');
button.disabled = true;
setStatus('正在读取并校验文件…');
try{
const data = await sendFile('/api/admin/orders/import/preview');
showResponse(data);
setStatus(data.ok ? '校验完成,请检查结果。' : '发现错误行,请先修正文件。', !data.ok);
}catch(error){ setStatus(error.message || '校验失败,请检查网络后重试。', true); }
finally{ button.disabled = false; }
});
confirmButton.addEventListener('click', async function(){
if(!canConfirm || !selectedFile) return;
confirmButton.disabled = true;
setStatus('正在重新校验并导入…');
try{
const data = await sendFile('/api/admin/orders/import');
canConfirm = false;
confirmButton.hidden = true;
fileInput.value = '';
selectedFile = null;
fileName.textContent = '选择订单文件';
setStatus('导入完成:新增 '+data.imported+' 条,重复保留 '+data.preserved+' 条。');
const refreshed = await Promise.allSettled([loadBatches(), loadOrders(false)]);
if(refreshed[0].status === 'rejected'){
document.getElementById('import-batch-rows').innerHTML = '| 导入已完成,但记录刷新失败,请刷新页面重试。 |
';
}
if(refreshed[1].status === 'rejected'){
document.getElementById('order-list-status').textContent = '导入已完成,但订单列表刷新失败:'+refreshed[1].reason.message;
}
if(refreshed.some(function(result){ return result.status === 'rejected'; })){
setStatus('导入已完成,但部分列表刷新失败,请刷新页面查看。', true);
}
}catch(error){ setStatus(error.message || '导入失败,请检查网络后重试。', true); }
finally{ confirmButton.disabled = false; }
});
document.getElementById('order-search-form').addEventListener('submit', async function(event){
event.preventDefault();
try { await loadOrders(false); } catch(error) { document.getElementById('order-list-status').textContent = error.message; }
});
document.getElementById('load-more-orders').addEventListener('click', async function(){
this.disabled = true;
try { await loadOrders(true); } catch(error) { document.getElementById('order-list-status').textContent = error.message; }
finally { this.disabled = false; }
});
Promise.all([loadOrders(false), loadBatches()]).catch(function(error){ document.getElementById('order-list-status').textContent = error.message; });
document.getElementById('staff-logout').addEventListener('click', async function(){
this.disabled = true;
try{
await fetch('/admin/logout', {method:'POST', credentials:'same-origin', headers:{'X-CSRF-Token':csrf}});
}finally{ location.assign('/admin'); }
});
})();