564 lines
22 KiB
JavaScript
564 lines
22 KiB
JavaScript
|
|
const Upload = (() => {
|
|||
|
|
// 使用localStorage保存上传记录
|
|||
|
|
const STORAGE_KEY = 'mac_upload_history';
|
|||
|
|
|
|||
|
|
// 事件监听器清理
|
|||
|
|
const eventListeners = [];
|
|||
|
|
const addListener = (element, event, handler) => {
|
|||
|
|
if(element){
|
|||
|
|
element.addEventListener(event, handler);
|
|||
|
|
eventListeners.push({element, event, handler});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
const cleanupListeners = () => {
|
|||
|
|
eventListeners.forEach(({element, event, handler}) => {
|
|||
|
|
element.removeEventListener(event, handler);
|
|||
|
|
});
|
|||
|
|
eventListeners.length = 0;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
Router.onBeforeEach((path) => {
|
|||
|
|
if(!path.startsWith('/upload')){
|
|||
|
|
cleanupListeners();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
function getHistory() {
|
|||
|
|
try {
|
|||
|
|
return JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
|
|||
|
|
} catch {
|
|||
|
|
return [];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function saveToHistory(records) {
|
|||
|
|
try {
|
|||
|
|
const history = getHistory();
|
|||
|
|
const newRecords = records.map(r => ({
|
|||
|
|
...r,
|
|||
|
|
timestamp: new Date().toISOString()
|
|||
|
|
}));
|
|||
|
|
const updated = [...newRecords, ...history].slice(0, 100); // 保留最近100条
|
|||
|
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(updated));
|
|||
|
|
} catch (e) {
|
|||
|
|
console.error('保存历史记录失败:', e);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function clearHistory() {
|
|||
|
|
localStorage.removeItem(STORAGE_KEY);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function section(title, inner) {
|
|||
|
|
return `<div class="card"><div style="font-weight:600;margin-bottom:8px">${title}</div>${inner}</div>`;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function filePicker(id,label,accept){
|
|||
|
|
return `<div class="field"><label>${label}</label><input id="${id}" type="file" class="input" accept="${accept}" /></div>`;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function numberInput(id,label){return `<div class="field"><label>${label}</label><input id="${id}" type="number" class="input" /></div>`}
|
|||
|
|
function textarea(id,label,placeholder=''){return `<div class="field"><label>${label}</label><textarea id="${id}" class="input" rows="4" placeholder="${placeholder}"></textarea></div>`}
|
|||
|
|
|
|||
|
|
async function renderMac(){
|
|||
|
|
return section('MAC与批次',`
|
|||
|
|
<div class="field">
|
|||
|
|
<label>上传类型</label>
|
|||
|
|
<select id="mac-type" class="input">
|
|||
|
|
<option value="pdd">拼多多</option>
|
|||
|
|
<option value="yt">圆通</option>
|
|||
|
|
<option value="tx">兔喜</option>
|
|||
|
|
</select>
|
|||
|
|
</div>
|
|||
|
|
${filePicker('mac-file','批量导入(Excel)','application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel')}
|
|||
|
|
<div class="actions"><button class="btn" id="mac-upload">上传</button></div>
|
|||
|
|
<div id="upload-log" style="margin-top:12px;display:none">
|
|||
|
|
<div style="font-weight:600;margin-bottom:8px">上传日志</div>
|
|||
|
|
<pre style="background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:12px;max-height:300px;overflow-y:auto;font-size:12px;color:var(--text);white-space:pre-wrap"></pre>
|
|||
|
|
</div>
|
|||
|
|
<div style="margin-top:12px">
|
|||
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">
|
|||
|
|
<span style="font-weight:600">最新记录</span>
|
|||
|
|
<div style="display:flex;gap:8px">
|
|||
|
|
<button class="btn btn-secondary" id="mac-show-history" style="font-size:12px;padding:4px 8px">查看历史</button>
|
|||
|
|
<button class="btn btn-secondary" id="mac-clear-display" style="font-size:12px;padding:4px 8px">清空显示</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<ul id="mac-list" class="list" style="max-height:300px;overflow-y:auto"></ul>
|
|||
|
|
</div>
|
|||
|
|
`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function renderStats(){
|
|||
|
|
return section('良/不良统计',`
|
|||
|
|
<div style="background:rgba(79,140,255,0.08);border:1px solid rgba(79,140,255,0.2);border-radius:8px;padding:12px;margin-bottom:12px;font-size:13px;line-height:1.6">
|
|||
|
|
<div style="font-weight:600;margin-bottom:6px;color:var(--primary)">📊 数据说明</div>
|
|||
|
|
<div style="color:var(--text)">
|
|||
|
|
<div><strong>直通良品数:</strong>一次检测就通过的产品数量</div>
|
|||
|
|
<div><strong>良品数:</strong>最终通过检测的产品总数(包含直通良品 + 返修后通过的产品)</div>
|
|||
|
|
<div><strong>不良品数:</strong>最终未通过检测的产品数量(报废或待返修)</div>
|
|||
|
|
<div style="margin-top:6px;padding-top:6px;border-top:1px solid rgba(79,140,255,0.2);color:var(--text-2)">
|
|||
|
|
💡 <strong>计算公式:</strong><br>
|
|||
|
|
直通良品率 = 直通良品数 / (良品数 + 不良品数) × 100%<br>
|
|||
|
|
总良品率 = 良品数 / (良品数 + 不良品数) × 100%
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="field">
|
|||
|
|
<label>平台类型</label>
|
|||
|
|
<select id="stats-platform" class="input">
|
|||
|
|
<option value="pdd">拼多多</option>
|
|||
|
|
<option value="yt">圆通</option>
|
|||
|
|
<option value="tx">兔喜</option>
|
|||
|
|
</select>
|
|||
|
|
</div>
|
|||
|
|
${numberInput('fpy-good-count','直通良品数量(一次检测通过)')}
|
|||
|
|
${numberInput('good-count','良品数量(最终通过检测的总数)')}
|
|||
|
|
${numberInput('bad-count','不良品数量(最终未通过)')}
|
|||
|
|
<div class="field">
|
|||
|
|
<label>不良明细(可选)</label>
|
|||
|
|
<textarea id="bad-details" class="input" rows="4" placeholder="每行一个不良记录,格式:MAC地址,批次号 例如:90:A9:F7:DD:EE:FF,D20250000000001"></textarea>
|
|||
|
|
</div>
|
|||
|
|
<div class="actions"><button class="btn" id="stats-upload">上传</button></div>
|
|||
|
|
<div style="margin-top:12px">
|
|||
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">
|
|||
|
|
<span style="font-weight:600">最新记录</span>
|
|||
|
|
<div style="display:flex;gap:8px">
|
|||
|
|
<button class="btn btn-secondary" id="stats-show-history" style="font-size:12px;padding:4px 8px">查看历史</button>
|
|||
|
|
<button class="btn btn-secondary" id="stats-clear-display" style="font-size:12px;padding:4px 8px">清空显示</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<ul id="stats-list" class="list" style="max-height:300px;overflow-y:auto"></ul>
|
|||
|
|
</div>
|
|||
|
|
`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function renderRepairs(){
|
|||
|
|
return section('返修记录',`
|
|||
|
|
${numberInput('repair-qty','返修完成数量')}
|
|||
|
|
${textarea('repair-note','备注(可选)','例如:批次号、问题描述等')}
|
|||
|
|
<div class="actions"><button class="btn" id="repairs-upload">上传</button></div>
|
|||
|
|
<div style="margin-top:12px">
|
|||
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">
|
|||
|
|
<span style="font-weight:600">最新记录</span>
|
|||
|
|
<button class="btn btn-secondary" id="repairs-show-all" style="font-size:12px;padding:4px 8px">查看全部</button>
|
|||
|
|
</div>
|
|||
|
|
<ul id="repairs-list" class="list" style="max-height:300px;overflow-y:auto"></ul>
|
|||
|
|
</div>
|
|||
|
|
`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function renderDefects(){
|
|||
|
|
return section('不良明细',`
|
|||
|
|
${filePicker('defects-file','批量导入不良MAC与批次(Excel/CSV)','text/csv,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel')}
|
|||
|
|
${textarea('defects-manual','手动输入','AA:BB:...,BATCH-xyz; ...')}
|
|||
|
|
<div class="actions"><button class="btn" id="defects-upload">上传</button></div>
|
|||
|
|
<div style="margin-top:12px"><div style="font-weight:600;margin-bottom:8px">最新记录</div><ul id="defects-list" class="list"></ul></div>
|
|||
|
|
`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Router.register('/upload/mac', async () => {
|
|||
|
|
const html = await renderMac();
|
|||
|
|
setTimeout(bindMacEvents,0);
|
|||
|
|
setTimeout(()=>{
|
|||
|
|
// 显示历史记录中的最新10条
|
|||
|
|
const history = getHistory();
|
|||
|
|
const listEl=document.getElementById('mac-list');
|
|||
|
|
if(listEl && history.length > 0){
|
|||
|
|
listEl.innerHTML = history.slice(0, 10).map(r=>`<li><span>${r.mac}</span><span class="badge">${r.batch}</span></li>`).join('');
|
|||
|
|
} else if(listEl) {
|
|||
|
|
listEl.innerHTML = '<li>暂无数据</li>';
|
|||
|
|
}
|
|||
|
|
},0);
|
|||
|
|
return html;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
Router.register('/upload/stats', async () => {
|
|||
|
|
const html = await renderStats();
|
|||
|
|
setTimeout(bindStatsEvents,0);
|
|||
|
|
setTimeout(()=>{
|
|||
|
|
// 显示历史记录中的最新10条
|
|||
|
|
const STATS_STORAGE_KEY = 'stats_upload_history';
|
|||
|
|
try {
|
|||
|
|
const history = JSON.parse(localStorage.getItem(STATS_STORAGE_KEY) || '[]');
|
|||
|
|
const listEl=document.getElementById('stats-list');
|
|||
|
|
if(listEl && history.length > 0){
|
|||
|
|
listEl.innerHTML = history.slice(0, 10).map(r=>{
|
|||
|
|
const platformName = {pdd: '拼多多', yt: '圆通', tx: '兔喜'}[r.platform] || '';
|
|||
|
|
const platformText = platformName ? `${platformName} - ` : '';
|
|||
|
|
let html = `<li><span>${platformText}直通良:${r.fpy_good||0} 良:${r.good} 不良:${r.bad}</span>`;
|
|||
|
|
if(r.details && r.details.length > 0){
|
|||
|
|
html += `<span class="badge">${r.details.length}条明细</span>`;
|
|||
|
|
}
|
|||
|
|
html += '</li>';
|
|||
|
|
return html;
|
|||
|
|
}).join('');
|
|||
|
|
} else if(listEl) {
|
|||
|
|
listEl.innerHTML = '<li>暂无数据</li>';
|
|||
|
|
}
|
|||
|
|
} catch(e) {
|
|||
|
|
console.error('加载历史记录失败:', e);
|
|||
|
|
}
|
|||
|
|
},0);
|
|||
|
|
return html;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
Router.register('/upload/repairs', async () => {
|
|||
|
|
const html = await renderRepairs();
|
|||
|
|
setTimeout(bindRepairsEvents,0);
|
|||
|
|
setTimeout(async ()=>{
|
|||
|
|
const listEl=document.getElementById('repairs-list');
|
|||
|
|
const data=await API.listRepairs().catch(()=>({list:[]}));
|
|||
|
|
listEl.innerHTML=(data.list||[]).slice(0,10).map(r=>{
|
|||
|
|
const ts = new Date(r.ts).toLocaleString('zh-CN');
|
|||
|
|
return `<li><span>数量: ${r.qty}</span><span class="badge">${r.note||'无备注'}</span><span style="font-size:11px;color:var(--text-2)">${ts}</span></li>`;
|
|||
|
|
}).join('')||'<li>暂无数据</li>';
|
|||
|
|
},0);
|
|||
|
|
return html;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
Router.register('/upload/defects', async () => {
|
|||
|
|
const html = await renderDefects();
|
|||
|
|
setTimeout(bindDefectsEvents,0);
|
|||
|
|
setTimeout(async ()=>{
|
|||
|
|
const listEl=document.getElementById('defects-list');
|
|||
|
|
const data=await API.listDefects().catch(()=>({list:[]}));
|
|||
|
|
listEl.innerHTML=(data.list||[]).slice(0,10).map(r=>`<li><span>${r.mac}</span><span class="badge">${r.batch}</span></li>`).join('')||'<li>暂无数据</li>';
|
|||
|
|
},0);
|
|||
|
|
return html;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
function readText(file){return new Promise((resolve,reject)=>{const r=new FileReader();r.onload=()=>resolve(r.result);r.onerror=reject;r.readAsText(file)})}
|
|||
|
|
|
|||
|
|
function parseManual(text){
|
|||
|
|
return text.split(/\n+/).map(l=>l.trim()).filter(Boolean).map(l=>{
|
|||
|
|
const [mac,batch]=l.split(',');
|
|||
|
|
return { mac, batch };
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function bindMacEvents(){
|
|||
|
|
const fileEl=document.getElementById('mac-file');
|
|||
|
|
const btn = document.getElementById('mac-upload');
|
|||
|
|
const showHistoryBtn = document.getElementById('mac-show-history');
|
|||
|
|
const clearDisplayBtn = document.getElementById('mac-clear-display');
|
|||
|
|
|
|||
|
|
// 文件选择后立即验证
|
|||
|
|
addListener(fileEl, 'change', async ()=>{
|
|||
|
|
const file = fileEl.files[0];
|
|||
|
|
if(!file) return;
|
|||
|
|
|
|||
|
|
try{
|
|||
|
|
const formData = new FormData();
|
|||
|
|
formData.append('file', file);
|
|||
|
|
const res = await fetch('/api/validate/mac-file', {
|
|||
|
|
method: 'POST',
|
|||
|
|
body: formData
|
|||
|
|
});
|
|||
|
|
const result = await res.json();
|
|||
|
|
|
|||
|
|
if(!result.valid){
|
|||
|
|
API.toast(result.message || '文件格式不正确');
|
|||
|
|
fileEl.value = '';
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
API.toast(result.message || '文件验证通过');
|
|||
|
|
}catch(e){
|
|||
|
|
API.toast('文件验证失败');
|
|||
|
|
fileEl.value = '';
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 查看历史按钮
|
|||
|
|
addListener(showHistoryBtn, 'click', ()=>{
|
|||
|
|
const history = getHistory();
|
|||
|
|
const listEl = document.getElementById('mac-list');
|
|||
|
|
if(listEl){
|
|||
|
|
if(history.length > 0){
|
|||
|
|
listEl.innerHTML = history.map(r=>`<li><span>${r.mac}</span><span class="badge">${r.batch}</span><span style="font-size:11px;color:var(--text-2);margin-left:8px">${new Date(r.timestamp).toLocaleString('zh-CN')}</span></li>`).join('');
|
|||
|
|
API.toast(`显示全部 ${history.length} 条历史记录`);
|
|||
|
|
} else {
|
|||
|
|
listEl.innerHTML = '<li>暂无历史记录</li>';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 清空显示按钮
|
|||
|
|
addListener(clearDisplayBtn, 'click', ()=>{
|
|||
|
|
const listEl = document.getElementById('mac-list');
|
|||
|
|
if(listEl){
|
|||
|
|
listEl.innerHTML = '<li>已清空显示</li>';
|
|||
|
|
API.toast('已清空显示(历史记录仍保留)');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
addListener(btn, 'click', async ()=>{
|
|||
|
|
const file = fileEl.files[0];
|
|||
|
|
if(!file){
|
|||
|
|
API.toast('请选择文件');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const typeEl = document.getElementById('mac-type');
|
|||
|
|
const uploadType = typeEl ? typeEl.value : 'pdd';
|
|||
|
|
const logContainer = document.getElementById('upload-log');
|
|||
|
|
const logPre = logContainer ? logContainer.querySelector('pre') : null;
|
|||
|
|
|
|||
|
|
try{
|
|||
|
|
btn.disabled = true;
|
|||
|
|
if(logContainer) logContainer.style.display = 'block';
|
|||
|
|
if(logPre) logPre.textContent = '正在上传文件...\n';
|
|||
|
|
|
|||
|
|
const formData = new FormData();
|
|||
|
|
formData.append('file', file);
|
|||
|
|
formData.append('type', uploadType);
|
|||
|
|
|
|||
|
|
const res = await fetch('/api/upload/mac-file', {
|
|||
|
|
method: 'POST',
|
|||
|
|
body: formData
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const result = await res.json();
|
|||
|
|
|
|||
|
|
if(logPre){
|
|||
|
|
logPre.textContent = result.output || '上传完成';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(result.ok){
|
|||
|
|
API.toast('上传成功');
|
|||
|
|
|
|||
|
|
// 解析并显示成功上传的记录
|
|||
|
|
const output = result.output || '';
|
|||
|
|
const jsonMatch = output.match(/=== 成功导入的数据 ===\n([\s\S]*?)\n=== 数据输出结束 ===/);
|
|||
|
|
if(jsonMatch && jsonMatch[1]){
|
|||
|
|
try{
|
|||
|
|
const records = JSON.parse(jsonMatch[1].trim());
|
|||
|
|
if(records.length > 0){
|
|||
|
|
// 保存到历史记录
|
|||
|
|
saveToHistory(records);
|
|||
|
|
|
|||
|
|
// 显示最新记录
|
|||
|
|
const listEl = document.getElementById('mac-list');
|
|||
|
|
if(listEl){
|
|||
|
|
listEl.innerHTML = records.map(r=>`<li><span>${r.mac}</span><span class="badge">${r.batch}</span></li>`).join('');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}catch(e){
|
|||
|
|
console.error('解析上传记录失败:', e);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
API.toast(result.error || '上传失败');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(fileEl) fileEl.value = '';
|
|||
|
|
}catch(e){
|
|||
|
|
API.toast('上传失败: ' + e.message);
|
|||
|
|
if(logPre) logPre.textContent += '\n错误: ' + e.message;
|
|||
|
|
} finally {
|
|||
|
|
if (btn) btn.disabled = false;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function bindStatsEvents(){
|
|||
|
|
const STATS_STORAGE_KEY = 'stats_upload_history';
|
|||
|
|
|
|||
|
|
const getStatsHistory = () => {
|
|||
|
|
try {
|
|||
|
|
return JSON.parse(localStorage.getItem(STATS_STORAGE_KEY) || '[]');
|
|||
|
|
} catch {
|
|||
|
|
return [];
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const saveToStatsHistory = (record) => {
|
|||
|
|
try {
|
|||
|
|
const history = getStatsHistory();
|
|||
|
|
const newRecord = {
|
|||
|
|
...record,
|
|||
|
|
timestamp: new Date().toISOString()
|
|||
|
|
};
|
|||
|
|
const updated = [newRecord, ...history].slice(0, 100);
|
|||
|
|
localStorage.setItem(STATS_STORAGE_KEY, JSON.stringify(updated));
|
|||
|
|
} catch (e) {
|
|||
|
|
console.error('保存历史记录失败:', e);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const btn = document.getElementById('stats-upload');
|
|||
|
|
const showHistoryBtn = document.getElementById('stats-show-history');
|
|||
|
|
const clearDisplayBtn = document.getElementById('stats-clear-display');
|
|||
|
|
|
|||
|
|
// 查看历史按钮
|
|||
|
|
addListener(showHistoryBtn, 'click', ()=>{
|
|||
|
|
const history = getStatsHistory();
|
|||
|
|
const listEl = document.getElementById('stats-list');
|
|||
|
|
if(listEl){
|
|||
|
|
if(history.length > 0){
|
|||
|
|
listEl.innerHTML = history.map(r=>{
|
|||
|
|
const platformName = {pdd: '拼多多', yt: '圆通', tx: '兔喜'}[r.platform] || '';
|
|||
|
|
const platformText = platformName ? `${platformName} - ` : '';
|
|||
|
|
let html = `<li><span>${platformText}直通良:${r.fpy_good||0} 良:${r.good} 不良:${r.bad}</span>`;
|
|||
|
|
if(r.details && r.details.length > 0){
|
|||
|
|
html += `<span class="badge">${r.details.length}条明细</span>`;
|
|||
|
|
}
|
|||
|
|
html += `<span style="font-size:11px;color:var(--text-2);margin-left:8px">${new Date(r.timestamp).toLocaleString('zh-CN')}</span></li>`;
|
|||
|
|
return html;
|
|||
|
|
}).join('');
|
|||
|
|
API.toast(`显示全部 ${history.length} 条历史记录`);
|
|||
|
|
} else {
|
|||
|
|
listEl.innerHTML = '<li>暂无历史记录</li>';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 清空显示按钮
|
|||
|
|
addListener(clearDisplayBtn, 'click', ()=>{
|
|||
|
|
const listEl = document.getElementById('stats-list');
|
|||
|
|
if(listEl){
|
|||
|
|
listEl.innerHTML = '<li>已清空显示</li>';
|
|||
|
|
API.toast('已清空显示(历史记录仍保留)');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
addListener(btn, 'click', async ()=>{
|
|||
|
|
const platform = document.getElementById('stats-platform').value;
|
|||
|
|
const fpyGood=parseInt(document.getElementById('fpy-good-count').value||'0',10);
|
|||
|
|
const good=parseInt(document.getElementById('good-count').value||'0',10);
|
|||
|
|
const bad=parseInt(document.getElementById('bad-count').value||'0',10);
|
|||
|
|
const detailsText = document.getElementById('bad-details')?.value.trim() || '';
|
|||
|
|
|
|||
|
|
if(fpyGood<0||good<0||bad<0){return API.toast('数量不能为负数')}
|
|||
|
|
|
|||
|
|
// 解析不良明细
|
|||
|
|
const details = [];
|
|||
|
|
if(detailsText){
|
|||
|
|
const lines = detailsText.split('\n').filter(l => l.trim());
|
|||
|
|
for(const line of lines){
|
|||
|
|
const [mac, batch] = line.split(',').map(s => s.trim());
|
|||
|
|
if(mac && batch){
|
|||
|
|
details.push({mac, batch});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
btn.disabled = true;
|
|||
|
|
try{
|
|||
|
|
await API.uploadStats({platform, fpy_good: fpyGood, good, bad, details});
|
|||
|
|
API.toast('上传成功');
|
|||
|
|
|
|||
|
|
// 保存到历史记录
|
|||
|
|
saveToStatsHistory({platform, fpy_good: fpyGood, good, bad, details});
|
|||
|
|
|
|||
|
|
// 显示最新记录
|
|||
|
|
const listEl=document.getElementById('stats-list');
|
|||
|
|
if(listEl){
|
|||
|
|
const platformName = {pdd: '拼多多', yt: '圆通', tx: '兔喜'}[platform] || platform;
|
|||
|
|
let html = `<li><span>${platformName} - 直通良:${fpyGood} 良:${good} 不良:${bad}</span>`;
|
|||
|
|
if(details.length > 0){
|
|||
|
|
html += `<span class="badge">${details.length}条明细</span>`;
|
|||
|
|
}
|
|||
|
|
html += '</li>';
|
|||
|
|
listEl.innerHTML = html;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 清空输入
|
|||
|
|
document.getElementById('fpy-good-count').value = '';
|
|||
|
|
document.getElementById('good-count').value = '';
|
|||
|
|
document.getElementById('bad-count').value = '';
|
|||
|
|
document.getElementById('bad-details').value = '';
|
|||
|
|
}catch(e){
|
|||
|
|
API.toast('上传失败');
|
|||
|
|
} finally {
|
|||
|
|
if (btn) btn.disabled = false;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function bindRepairsEvents(){
|
|||
|
|
const btn = document.getElementById('repairs-upload');
|
|||
|
|
const showAllBtn = document.getElementById('repairs-show-all');
|
|||
|
|
|
|||
|
|
// 查看全部按钮
|
|||
|
|
addListener(showAllBtn, 'click', async ()=>{
|
|||
|
|
const listEl = document.getElementById('repairs-list');
|
|||
|
|
const data = await API.listRepairs().catch(()=>({list:[]}));
|
|||
|
|
if(listEl){
|
|||
|
|
if(data.list && data.list.length > 0){
|
|||
|
|
listEl.innerHTML = data.list.map(r=>{
|
|||
|
|
const ts = new Date(r.ts).toLocaleString('zh-CN');
|
|||
|
|
return `<li><span>数量: ${r.qty}</span><span class="badge">${r.note||'无备注'}</span><span style="font-size:11px;color:var(--text-2)">${ts}</span></li>`;
|
|||
|
|
}).join('');
|
|||
|
|
API.toast(`显示全部 ${data.list.length} 条记录`);
|
|||
|
|
} else {
|
|||
|
|
listEl.innerHTML = '<li>暂无记录</li>';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
addListener(btn, 'click', async ()=>{
|
|||
|
|
const qty = parseInt(document.getElementById('repair-qty').value||'0', 10);
|
|||
|
|
const note = document.getElementById('repair-note')?.value.trim() || '';
|
|||
|
|
|
|||
|
|
if(qty <= 0){
|
|||
|
|
return API.toast('请输入有效的返修数量');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
btn.disabled = true;
|
|||
|
|
try{
|
|||
|
|
await API.uploadRepairs({qty, note});
|
|||
|
|
API.toast('上传成功');
|
|||
|
|
|
|||
|
|
// 刷新列表
|
|||
|
|
const listEl = document.getElementById('repairs-list');
|
|||
|
|
const data = await API.listRepairs().catch(()=>({list:[]}));
|
|||
|
|
if(listEl){
|
|||
|
|
listEl.innerHTML = (data.list||[]).slice(0,10).map(r=>{
|
|||
|
|
const ts = new Date(r.ts).toLocaleString('zh-CN');
|
|||
|
|
return `<li><span>数量: ${r.qty}</span><span class="badge">${r.note||'无备注'}</span><span style="font-size:11px;color:var(--text-2)">${ts}</span></li>`;
|
|||
|
|
}).join('')||'<li>暂无数据</li>';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 清空输入
|
|||
|
|
document.getElementById('repair-qty').value = '';
|
|||
|
|
document.getElementById('repair-note').value = '';
|
|||
|
|
}catch(e){
|
|||
|
|
API.toast('上传失败');
|
|||
|
|
} finally {
|
|||
|
|
if (btn) btn.disabled = false;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function bindDefectsEvents(){
|
|||
|
|
const btn = document.getElementById('defects-upload');
|
|||
|
|
addListener(btn, 'click', async ()=>{
|
|||
|
|
try{
|
|||
|
|
const fileEl=document.getElementById('defects-file');
|
|||
|
|
const manual=document.getElementById('defects-manual').value.trim();
|
|||
|
|
let rows=[];
|
|||
|
|
if(fileEl.files[0]){
|
|||
|
|
const text=await readText(fileEl.files[0]);
|
|||
|
|
rows=text.split(/\n+/).map(l=>l.split(','));
|
|||
|
|
rows=rows.map(([mac,batch])=>({mac,batch}));
|
|||
|
|
}else if(manual){
|
|||
|
|
rows=parseManual(manual);
|
|||
|
|
}
|
|||
|
|
if(!rows.length){throw new Error('请提供文件或手动输入')}
|
|||
|
|
btn.disabled = true;
|
|||
|
|
await API.uploadDefects({rows});
|
|||
|
|
API.toast('上传成功');
|
|||
|
|
const listEl=document.getElementById('defects-list');
|
|||
|
|
const data=await API.listDefects().catch(()=>({list:[]}));
|
|||
|
|
if(listEl) listEl.innerHTML=(data.list||[]).slice(0,10).map(r=>`<li><span>${r.mac}</span><span class="badge">${r.batch}</span></li>`).join('')||'<li>暂无数据</li>';
|
|||
|
|
}catch(e){
|
|||
|
|
API.toast('上传失败');
|
|||
|
|
} finally {
|
|||
|
|
if (btn) btn.disabled = false;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
})();
|