(() => { const categories = [ { id: 'cost', name: '自家成本' }, { id: 'retail', name: '售价价格' }, { id: 'dealer', name: '经销商价格' } ]; const token = () => window.__manyiToken || ''; const escapeHtml = (value) => String(value ?? '').replace(/[&<>"']/g, (character) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[character])); const api = (url, options = {}) => fetch(url, { ...options, headers: { Authorization: `Bearer ${token()}`, ...(options.headers || {}) } }).then(async (response) => { const body = await response.json(); if (!response.ok) throw new Error(body.error || '操作失败'); return body; }); const init = () => { const card = document.querySelector('#files .card'); if (!card || document.getElementById('priceLibrary')) return; const isAdmin = window.__manyiUser?.role === '管理员'; const visibleCategories = isAdmin ? categories : categories.filter((item) => item.id === 'retail'); const box = document.createElement('section'); box.id = 'priceLibrary'; box.innerHTML = `

价格资料

各类价格独立保存和索引,敏感价格仅管理员可见。

${visibleCategories.map((item, index) => ``).join('')}

单号查询员保存的文件

按收录日期整理到文件夹,点击日期可展开当天的表格。

${isAdmin ? '可一次上传多个表格' : ''}

正在加载…

`; card.appendChild(box); const priceStyle = document.createElement('style'); priceStyle.textContent = '#priceLibrary .price-files{margin-top:12px}#priceLibrary .price-file{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 0;border-bottom:1px solid #edf1f5}#priceLibrary .price-file>span{min-width:0;word-break:break-word}#priceLibrary .price-delete{width:32px;height:32px;flex:none;padding:0;font-size:20px;line-height:1}.order-query-files{margin-top:26px;padding-top:18px;border-top:1px solid #e1e7ee}.order-query-toolbar{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin:12px 0}.order-query-toolbar .input{margin:0;max-width:360px}.order-query-upload-status{font-size:12px}.order-query-folder{margin:16px 0}.order-query-folder summary{display:flex;justify-content:space-between;align-items:center;cursor:pointer;padding:8px 0;color:#506275;font-weight:700;border-bottom:1px solid #dfe7ee}.order-query-folder summary small{color:#8794a1;font-weight:400}.order-query-file{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 0;border-bottom:1px solid #edf1f5}.order-query-file small{display:block;color:#788696;margin-top:3px}.order-query-file a{color:#2f78df;text-decoration:none;white-space:nowrap}.order-query-file-actions{display:flex;align-items:center;gap:10px;white-space:nowrap}.order-query-delete{border:0;background:transparent;color:#b94343;font-size:20px;line-height:1;cursor:pointer;padding:0}'; document.head.appendChild(priceStyle); const panel = box.querySelector('#pricePanel'); const orderFileList = box.querySelector('.order-query-file-list'); const loadOrderFiles = async (query = '') => { try { const result = await api(`/api/order-query/files?q=${encodeURIComponent(query)}`); const folders = result.folders || [{ date: null, items: result.items || [] }]; const renderFile = (file) => `
${escapeHtml(file.fileName)}${file.records} 条记录 · ${escapeHtml(file.sheetSummary || file.sheets.join('、'))} · ${formatFileSize(file.size)} · ${new Date(file.modifiedAt).toLocaleString()}打开${isAdmin ? `` : ''}
`; orderFileList.innerHTML = result.items.length ? folders.map((folder, index) => `
${escapeHtml(folder.date || '日期未记录')}${folder.items.length} 个文件${folder.items.map(renderFile).join('')}
`).join('') : '

暂无单号查询员保存的文件

'; orderFileList.querySelectorAll('[data-order-delete]').forEach((button) => button.onclick = async () => { if (!confirm(`确定删除“${button.dataset.orderFileName}”?本地文件和查询索引会同步删除。`)) return; try { await api(`/api/order-query/files/${button.dataset.orderDelete}`, { method: 'DELETE' }); await loadOrderFiles(box.querySelector('.order-query-search').value.trim()); } catch (error) { alert(error.message); } }); } catch (error) { orderFileList.innerHTML = `

${escapeHtml(error.message)}

`; } }; const formatFileSize = (bytes) => Number(bytes || 0) < 1048576 ? `${(Number(bytes || 0) / 1024).toFixed(1)} KB` : `${(Number(bytes || 0) / 1048576).toFixed(1)} MB`; let active = visibleCategories[0]?.id || 'retail'; let uploadStatus = ''; const defaultStatus = '可一次选择多个文件,逐个建立价格索引'; const render = async () => { const category = visibleCategories.find((item) => item.id === active); const data = await api(`/api/price-files?category=${active}`); const adminControls = isAdmin ? `
${uploadStatus || defaultStatus}
` : ''; const files = data.items.map((file) => { const summary = file.status === 'failed' ? `录入失败${file.error ? `:${escapeHtml(file.error)}` : ''}` : `${file.recordCount} 条记录`; return `
${escapeHtml(file.fileName)} · ${summary}${isAdmin ? `` : ''}
`; }).join(''); panel.innerHTML = `

${escapeHtml(category.name)}

${adminControls}
${files || '

暂无价格文件

'}
`; const input = panel.querySelector('.price-input'); if (input) input.onchange = async (event) => { const filesToUpload = Array.from(event.target.files || []); if (!filesToUpload.length) return; const results = []; const status = panel.querySelector('.price-status'); for (const file of filesToUpload) { uploadStatus = `正在处理 ${escapeHtml(file.name)}…`; if (status) status.innerHTML = uploadStatus; try { const response = await fetch('/api/price-files', { method: 'POST', headers: { Authorization: `Bearer ${token()}`, 'X-Price-Category': active, 'X-File-Name-Encoded': encodeURIComponent(file.name) }, body: await file.arrayBuffer() }); const result = await response.json(); if (!response.ok) throw new Error(result.error || '价格资料录入失败'); results.push(`✓ ${escapeHtml(file.name)}:${result.file.recordCount} 条`); } catch (error) { results.push(`✗ ${escapeHtml(file.name)}:${escapeHtml(error.message)}`); } } uploadStatus = results.join('
'); await render(); event.target.value = ''; }; panel.querySelectorAll('.price-delete').forEach((button) => { button.onclick = async () => { const file = data.items.find((item) => item.id === button.dataset.id); if (!file || !confirm(`确定删除“${category.name}”中的 ${file.fileName}?本地文件也会同步删除。`)) return; try { await api(`/api/price-files/${file.id}?category=${active}`, { method: 'DELETE' }); await render(); } catch (error) { alert(error.message); } }; }); }; box.querySelectorAll('.price-tab').forEach((button) => button.onclick = async () => { active = button.dataset.category; box.querySelectorAll('.price-tab').forEach((item) => item.classList.toggle('secondary', item !== button)); uploadStatus = ''; await render(); }); box.querySelector('.order-query-search-button').onclick = () => loadOrderFiles(box.querySelector('.order-query-search').value.trim()); box.querySelector('.order-query-search').onkeydown = (event) => { if (event.key === 'Enter') loadOrderFiles(event.target.value.trim()); }; const orderInput = box.querySelector('.order-query-input'); if (orderInput) orderInput.onchange = async (event) => { const files = Array.from(event.target.files || []); if (!files.length) return; const status = box.querySelector('.order-query-upload-status'); const results = []; for (const file of files) { if (!/\.(xls|xlsx|csv|tsv)$/i.test(file.name)) { results.push(`✗ ${escapeHtml(file.name)}:不支持的表格格式`); continue; } if (status) status.textContent = `正在归档 ${file.name}…`; try { const response = await fetch('/api/order-query/files', { method: 'POST', headers: { Authorization: `Bearer ${token()}`, 'X-File-Name-Encoded': encodeURIComponent(file.name) }, body: await file.arrayBuffer() }); const result = await response.json(); if (!response.ok) throw new Error(result.error || '表格录入失败'); results.push(`✓ ${escapeHtml(file.name)}:已归档到 ${escapeHtml(result.day)}`); } catch (error) { results.push(`✗ ${escapeHtml(file.name)}:${escapeHtml(error.message)}`); } } if (status) status.innerHTML = results.join('
'); event.target.value = ''; await loadOrderFiles(box.querySelector('.order-query-search').value.trim()); }; render().catch((error) => { panel.textContent = error.message; }); loadOrderFiles(); }; document.querySelector('[data-view="files"]')?.addEventListener('click', () => setTimeout(init, 80)); })();