dy-dashboard/var/confirmation-filter-preview.html
2026-10-28 11:14:17 +08:00

295 lines
23 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html><html lang="zh"><meta charset="utf-8"><link rel="stylesheet" href="file:///home/dieya/DYCG/dy-dashboard/static/css/style.css"><style>body{margin:0;padding:28px;background:#f5f7fa;font-family:Arial,"Noto Sans CJK SC",sans-serif}main{background:white;padding:24px;border-radius:10px}h1{font-size:22px}.page-desc{color:#888}</style><main id="preview"></main><script>
window._hasPagePerm=()=>true;
function escHtml(v){return String(v).replaceAll('&','&amp;').replaceAll('<','&lt;').replaceAll('"','&quot;');}
function escJs(v){return String(v);}
function applySpotConfirmationActions(){}
function renderSpotConfirmationPagination(){}
window.fetch=()=>Promise.resolve({json:()=>Promise.resolve({code:0,data:{rows:[],total:0,page:1,size:20,filter_options:{brand:['','洁维塔','泊紫汀兰'],product_name:['采样产品A','采样产品B'],sample_status:['样品已到货','待寄样'],applicant:['测试申请人']}}})});
function loadSpotConfirmationPage(container) {
closeSpotConfirmationFilter();
var fields = [
{key:'application_id', label:'申请编号'},
{key:'application_date', label:'日期'},
{key:'brand', label:'品牌'},
{key:'product_name', label:'产品名称'},
{key:'image_path', label:'图片'},
{key:'barcode', label:'条形码'},
{key:'sample_quantity', label:'采样数量'},
{key:'product_link', label:'产品链接'},
{key:'department', label:'部门'},
{key:'applicant', label:'申请人'},
{key:'manufacturer', label:'厂家名称'},
{key:'recipient_info', label:'收件信息'},
{key:'sample_status', label:'样品状态'},
{key:'product_unit_price', label:'产品单价'},
{key:'oa_approval_no', label:'OA审批单号'},
{key:'express_tracking_no', label:'寄样快递单号'},
{key:'arrival_date', label:'到样日期'},
{key:'remark', label:'备注'},
{key:'receiving_department', label:'领样部门'},
{key:'recipient', label:'领样人'},
{key:'sample_confirmed', label:'样品确认'},
{key:'sample_confirmation_reason', label:'样品确认原因'},
{key:'product_code', label:'产品编码'},
{key:'final_product_name', label:'货品名称(最终名称)'},
{key:'platform', label:'平台'}
];
var filterKeys = ['brand', 'product_name', 'sample_status', 'applicant'];
var headerHtml = fields.map(function(field) {
var filter = filterKeys.indexOf(field.key) >= 0 ?
'<button type="button" id="spotConfirmFilterButton_' + field.key + '" class="spot-confirm-filter-button" aria-label="筛选' + escHtml(field.label) + '" aria-haspopup="dialog" aria-expanded="false" title="筛选' + escHtml(field.label) + '" onclick="openSpotConfirmationFilter(\'' + field.key + '\',event)"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><path d="M3 4h18l-7 8v7l-4 2v-9z"/></svg></button>' +
'<select id="spotConfirmFilter_' + field.key + '" hidden aria-hidden="true" tabindex="-1"><option value="">全部</option></select>' : '';
return '<th><span class="spot-confirm-header-label">' + escHtml(field.label) + filter + '</span></th>';
}).join('') +
'<th class="spot-confirm-actions"><span class="spot-confirm-action-content">操作</span><button type="button" class="spot-confirm-action-toggle" onclick="toggleSpotConfirmationActions()" aria-label="折叠操作列" aria-expanded="true">»</button></th>';
container.innerHTML = `
<style>
.spot-confirm-toolbar { display:flex; justify-content:space-between; align-items:center; gap:12px; margin-bottom:16px; flex-wrap:wrap; }
.spot-confirm-search { display:flex; gap:8px; align-items:center; }
.spot-confirm-header-label{display:inline-flex;align-items:center;gap:5px}
.spot-confirm-filter-button{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;padding:3px;border:0;border-radius:4px;background:transparent;color:#8c8c8c;cursor:pointer;vertical-align:middle}
.spot-confirm-filter-button:hover,.spot-confirm-filter-button[aria-expanded="true"]{background:#e6f4ff;color:#1677ff}
.spot-confirm-filter-button.active{color:#1677ff;background:#e6f4ff}
.spot-confirm-filter-menu{position:fixed;z-index:10001;box-sizing:border-box;width:280px;max-width:calc(100vw - 20px);display:flex;flex-direction:column;gap:8px;padding:12px;background:#fff;border:1px solid #d9d9d9;border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,.15);font-size:13px;color:#333}
.spot-confirm-filter-title{font-weight:600;padding-bottom:8px;border-bottom:1px solid #f0f0f0}
.spot-confirm-filter-menu .form-input{width:100%;box-sizing:border-box;font-size:12px}
.spot-confirm-filter-options{overflow-y:auto;min-height:36px;max-height:200px}
.spot-confirm-filter-option{display:flex;align-items:center;gap:8px;padding:7px 8px;border-radius:4px;cursor:pointer;overflow-wrap:anywhere;line-height:1.5}
.spot-confirm-filter-option:hover,.spot-confirm-filter-option:has(input:checked){background:#e6f4ff}
.spot-confirm-filter-option input{flex-shrink:0;margin:0;accent-color:#1677ff}
.spot-confirm-filter-foot{display:flex;justify-content:flex-end;gap:6px;padding-top:8px;border-top:1px solid #f0f0f0}
.spot-confirm-table-wrap { background:#fff; border:1px solid var(--border,#e8e8e8); border-radius:8px; overflow:auto; max-height:calc(100vh - 260px); position:relative; }
.spot-confirm-table { width:100%; min-width:3200px; border-collapse:separate; border-spacing:0; font-size:13px; }
.spot-confirm-table th { background:#fafafa; color:#555; font-weight:600; white-space:nowrap; position:sticky; top:0; z-index:5; box-shadow:0 1px 0 #d9d9d9; }
.spot-confirm-table th,.spot-confirm-table td { padding:10px 12px; border:0; border-right:1px solid #e8e8e8; border-bottom:1px solid #e8e8e8; text-align:left; vertical-align:middle; }
.spot-confirm-table tbody tr:hover { background:#f7fbff; }
.spot-confirm-table td { max-width:220px; white-space:pre-wrap; overflow-wrap:anywhere; }
.spot-confirm-table td.spot-confirm-nowrap { white-space:nowrap; }
.spot-confirm-image { width:54px; height:54px; object-fit:cover; border:1px solid #eee; border-radius:5px; cursor:pointer; }
.spot-confirm-empty { padding:56px 20px; text-align:center; color:#aaa; }
.spot-confirm-pagination { display:flex; justify-content:center; align-items:center; gap:5px; padding:14px 0 4px; flex-wrap:wrap; }
.spot-confirm-pagination button { min-width:32px; padding:5px 9px; border:1px solid #d9d9d9; border-radius:4px; background:#fff; cursor:pointer; }
.spot-confirm-pagination button.active { color:#fff; background:#1677ff; border-color:#1677ff; }
.spot-confirm-pagination button:disabled { opacity:.5; cursor:not-allowed; }
.spot-confirm-pagination .spot-page-jump { display:inline-flex; align-items:center; gap:5px; margin-left:6px; color:#666; white-space:nowrap; }
.spot-confirm-pagination .spot-page-jump input { box-sizing:border-box; width:58px; height:30px; padding:3px 6px; border:1px solid #d9d9d9; border-radius:4px; text-align:center; }
.spot-confirm-table .spot-confirm-actions{position:sticky!important;right:0;min-width:100px;white-space:nowrap;border-left:2px solid #b8c3ce;box-shadow:-5px 0 10px rgba(31,45,61,.12);background-clip:border-box;z-index:2}
.spot-confirm-table tbody td.spot-confirm-actions{background:#fff;isolation:isolate}
.spot-confirm-table tbody tr:hover td.spot-confirm-actions{background:#f0f7f3}
.spot-confirm-table thead th.spot-confirm-actions{z-index:8;background:#344454;color:#fff}
.spot-confirm-action-toggle{border:0;background:transparent;cursor:pointer;color:inherit;font-size:18px;margin-left:8px;border-radius:4px}
.spot-confirm-action-toggle:hover{background:rgba(255,255,255,.16)}
.spot-confirm-table .spot-confirm-detail-button{background:#21884c;border:1px solid #21884c;color:#fff;border-radius:6px;padding:6px 16px;font-weight:600}
.spot-confirm-table .spot-confirm-detail-button:hover{background:#176b3b;border-color:#176b3b;color:#fff}
.spot-confirm-table.actions-collapsed .spot-confirm-action-content{display:none}
.spot-confirm-table.actions-collapsed .spot-confirm-actions{min-width:30px;width:30px;padding:8px}
.spot-confirm-detail-overlay{position:fixed;inset:0;background:rgba(0,0,0,.42);z-index:15000;display:flex;align-items:center;justify-content:center;padding:16px}
.spot-confirm-detail{width:1120px;max-width:94vw;max-height:90vh;display:flex;flex-direction:column;overflow:hidden;background:#fff;border-radius:14px;box-shadow:0 20px 70px rgba(20,35,50,.25);box-sizing:border-box}
.spot-confirm-detail-head,.spot-confirm-detail-foot{display:flex;justify-content:space-between;align-items:center;gap:12px;padding:18px 24px;flex-shrink:0}
.spot-confirm-detail-head{background:#f1f5f9;border-bottom:1px solid #d9e2eb}
.spot-confirm-detail-head h3{margin:0;color:#24364b;font-size:18px}
.spot-confirm-detail-head p{margin:6px 0 0;color:#738296;font-size:12px}
#spotConfirmationDetailForm{display:flex;flex-direction:column;min-height:0;overflow:hidden;margin:0}
.spot-confirm-detail-body{padding:20px 24px;overflow:auto;min-height:0;background:#fafbfd}
.spot-confirm-detail-foot{justify-content:flex-end;border-top:1px solid #d9e2eb;background:#fff}
.spot-confirm-detail-list{display:block;width:100%;margin:0;border:0;font-size:13px}
.spot-confirm-detail-list tbody{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px 16px}
.spot-confirm-detail-list tr{display:grid;grid-template-columns:132px minmax(0,1fr);border:1px solid #d9e2eb;border-radius:8px;overflow:hidden;min-width:0;background:#fff}
.spot-confirm-detail-list tr.spot-confirm-detail-wide{grid-column:1 / -1}
.spot-confirm-detail-list th,.spot-confirm-detail-list td{border:0;padding:12px 14px;text-align:left;vertical-align:middle;min-width:0;display:flex;flex-direction:column;justify-content:center}
.spot-confirm-detail-list th{background:#eaf0f6;color:#41556d;font-weight:600;border-right:1px solid #d9e2eb;line-height:1.6}
.spot-confirm-detail-list td{background:#fff;color:#25364a;white-space:pre-wrap;overflow-wrap:anywhere;line-height:1.6;min-height:44px;box-sizing:border-box}
.spot-confirm-detail-list input,.spot-confirm-detail-list textarea,.spot-confirm-detail-list select{width:100%;min-width:0;box-sizing:border-box;padding:8px 10px;border:1px solid #cbd5e1;border-radius:6px;font:inherit;background:#fff;color:#25364a}
.spot-confirm-detail-list input:focus,.spot-confirm-detail-list textarea:focus,.spot-confirm-detail-list select:focus{outline:2px solid #bddcca;outline-offset:1px;border-color:#21884c}
.spot-confirm-detail-list textarea{min-height:80px;resize:vertical}
@media(max-width:760px){.spot-confirm-detail-list tbody{grid-template-columns:minmax(0,1fr)}.spot-confirm-detail-list tr{grid-template-columns:110px minmax(0,1fr)}.spot-confirm-detail-body{padding:14px}.spot-confirm-detail-head,.spot-confirm-detail-foot{padding:14px}}
</style>
<div class="page-header"><h1>✅ 现货采样确认</h1><p class="page-desc">现货采样申请确认信息列表</p></div>
<div class="spot-confirm-toolbar">
<div class="spot-confirm-search">
<input id="spotConfirmSearch" class="form-input" autocomplete="off" placeholder="搜索品牌、产品名称、厂家名称、条形码、申请人或产品编码" style="width:360px" onkeydown="if(event.key==='Enter' && !event.isComposing){event.preventDefault();searchSpotConfirmation();}">
<button type="button" class="btn btn-primary" onclick="searchSpotConfirmation()">搜索</button>
<button type="button" class="btn btn-outline" onclick="clearSpotConfirmationSearch()">清除</button>
</div>
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap">
<span id="spotConfirmTotal" style="color:#999;font-size:12px"></span>
<a class="btn btn-outline" href="/api/spot/confirmation/import_template">下载导入模板</a>
${window._hasPagePerm('spot_confirmation','import') ? '<button class="btn btn-primary" onclick="openSpotConfirmationImportModal()">导入</button>' : ''}
</div>
</div>
<div class="spot-confirm-table-wrap">
<table class="spot-confirm-table"><thead><tr>${headerHtml}</tr></thead><tbody id="spotConfirmRows"><tr><td colspan="26" class="spot-confirm-empty">加载中...</td></tr></tbody></table>
</div>
<div id="spotConfirmPagination" class="spot-confirm-pagination"></div>
`;
window._spotConfirmFields = fields;
window._spotConfirmRows = {};
window._spotConfirmPage = 1;
window._spotConfirmNeedOptions = true;
loadSpotConfirmationList();
}
function searchSpotConfirmation() {
window._spotConfirmPage = 1;
loadSpotConfirmationList();
}
function clearSpotConfirmationSearch() {
closeSpotConfirmationFilter();
document.getElementById('spotConfirmSearch').value = '';
['brand', 'product_name', 'sample_status', 'applicant'].forEach(function(key) {
var select = document.getElementById('spotConfirmFilter_' + key);
if (select) select.value = '';
});
updateSpotConfirmationFilterButtons();
searchSpotConfirmation();
}
function updateSpotConfirmationFilterButtons() {
['brand', 'product_name', 'sample_status', 'applicant'].forEach(function(key) {
var select = document.getElementById('spotConfirmFilter_' + key);
var button = document.getElementById('spotConfirmFilterButton_' + key);
if (!select || !button) return;
button.classList.toggle('active', !!select.value);
var label = (window._spotConfirmFields || []).find(function(field) { return field.key === key; });
button.title = (label ? label.label : key) + (select.value ? ':' + (select.value === '__EMPTY__' ? '(空白)' : select.value) : ':全部');
});
}
function closeSpotConfirmationFilter() {
var menu = document.getElementById('spotConfirmationFilterMenu');
if (!menu) return;
if (menu._cleanup) menu._cleanup();
menu.remove();
}
function openSpotConfirmationFilter(key, event) {
event.stopPropagation();
var button = event.currentTarget;
var existing = document.getElementById('spotConfirmationFilterMenu');
var same = existing && existing.dataset.field === key;
closeSpotConfirmationFilter();
if (same) return;
var select = document.getElementById('spotConfirmFilter_' + key);
if (!select) return;
var field = (window._spotConfirmFields || []).find(function(item) { return item.key === key; });
var menu = document.createElement('div');
menu.id = 'spotConfirmationFilterMenu'; menu.className = 'spot-confirm-filter-menu';
menu.dataset.field = key;
menu.setAttribute('role', 'dialog'); menu.setAttribute('aria-label', '筛选' + (field ? field.label : key));
menu.innerHTML = '<div class="spot-confirm-filter-title">' + escHtml(field ? field.label : key) + '</div>' +
'<input class="form-input" type="search" autocomplete="off" placeholder="搜索筛选项..." aria-label="搜索筛选项">' +
'<div class="spot-confirm-filter-options"></div>' +
'<div class="spot-confirm-filter-foot"><button type="button" class="btn btn-sm btn-primary" data-apply>确定</button><button type="button" class="btn btn-sm" data-reset>重置</button></div>';
document.body.appendChild(menu);
button.setAttribute('aria-expanded', 'true');
var list = menu.querySelector('.spot-confirm-filter-options');
var search = menu.querySelector('input[type="search"]');
function renderOptions() {
var keyword = search.value.trim().toLowerCase();
list.innerHTML = Array.from(select.options).filter(function(option) {
return !keyword || option.textContent.toLowerCase().indexOf(keyword) >= 0;
}).map(function(option) {
return '<label class="spot-confirm-filter-option"><input type="radio" name="spotConfirmFilterChoice" value="' + escHtml(option.value) + '"' + (option.value === menu._selected ? ' checked' : '') + '><span>' + escHtml(option.textContent) + '</span></label>';
}).join('') || '<div style="padding:12px;color:#999;text-align:center">无匹配选项</div>';
}
menu._selected = select.value;
menu._renderOptions = renderOptions;
renderOptions();
search.addEventListener('input', renderOptions);
list.addEventListener('change', function(e) { menu._selected = e.target.value; });
function apply(value) {
select.value = value;
closeSpotConfirmationFilter();
updateSpotConfirmationFilterButtons();
searchSpotConfirmation();
button.focus();
}
menu.querySelector('[data-apply]').onclick = function() { apply(menu._selected); };
menu.querySelector('[data-reset]').onclick = function() { apply(''); };
function outside(e) { if (!menu.contains(e.target) && !button.contains(e.target)) closeSpotConfirmationFilter(); }
function keyboard(e) {
if (e.key === 'Escape') { closeSpotConfirmationFilter(); button.focus(); }
if (e.key === 'Enter' && !e.isComposing && e.target === search) { e.preventDefault(); apply(menu._selected); }
}
function scrolled(e) { if (!menu.contains(e.target)) closeSpotConfirmationFilter(); }
document.addEventListener('click', outside);
document.addEventListener('keydown', keyboard);
document.addEventListener('scroll', scrolled, true);
window.addEventListener('resize', closeSpotConfirmationFilter);
menu._cleanup = function() {
document.removeEventListener('click', outside);
document.removeEventListener('keydown', keyboard);
document.removeEventListener('scroll', scrolled, true);
window.removeEventListener('resize', closeSpotConfirmationFilter);
button.setAttribute('aria-expanded', 'false');
};
var rect = button.getBoundingClientRect();
menu.style.left = Math.max(10, Math.min(rect.left, window.innerWidth - menu.offsetWidth - 10)) + 'px';
menu.style.top = Math.max(10, Math.min(rect.bottom + 4, window.innerHeight - menu.offsetHeight - 10)) + 'px';
search.focus();
}
function loadSpotConfirmationList() {
var body = document.getElementById('spotConfirmRows');
if (!body) return;
var page = window._spotConfirmPage || 1;
var searchEl = document.getElementById('spotConfirmSearch');
var search = searchEl ? searchEl.value.trim() : '';
var url = '/api/spot/confirmation/list?page=' + page + '&size=20&search=' + encodeURIComponent(search);
['brand', 'product_name', 'sample_status', 'applicant'].forEach(function(key) {
var select = document.getElementById('spotConfirmFilter_' + key);
if (select && select.value) url += '&filter_' + key + '=' + encodeURIComponent(select.value);
});
if (window._spotConfirmNeedOptions) url += '&include_filter_options=1';
var requestId = window._spotConfirmRequestId = (window._spotConfirmRequestId || 0) + 1;
fetch(url, {headers:{'X-Requested-With':'XMLHttpRequest'}}).then(function(r){ return r.json(); }).then(function(res) {
if (requestId !== window._spotConfirmRequestId) return;
if (res.code !== 0) { body.innerHTML = '<tr><td colspan="26" class="spot-confirm-empty">' + escHtml(res.msg || '加载失败') + '</td></tr>'; return; }
var data = res.data;
if (data.filter_options) {
Object.keys(data.filter_options).forEach(function(key) {
var select = document.getElementById('spotConfirmFilter_' + key);
if (!select) return;
var selected = select.value;
select.innerHTML = '<option value="">全部</option>' + data.filter_options[key].map(function(value) {
return '<option value="' + escHtml(value === '' ? '__EMPTY__' : value) + '">' + escHtml(value || '(空白)') + '</option>';
}).join('');
select.value = selected;
});
window._spotConfirmNeedOptions = false;
var filterMenu = document.getElementById('spotConfirmationFilterMenu');
if (filterMenu && filterMenu._renderOptions) filterMenu._renderOptions();
}
updateSpotConfirmationFilterButtons();
var fields = window._spotConfirmFields || [];
var totalEl = document.getElementById('spotConfirmTotal');
if (totalEl) totalEl.textContent = '共 ' + data.total + ' 条确认记录';
if (!data.rows.length) {
body.innerHTML = '<tr><td colspan="26" class="spot-confirm-empty">暂无现货采样确认记录</td></tr>';
} else {
body.innerHTML = data.rows.map(function(row) {
window._spotConfirmRows[row.application_id] = row;
return '<tr>' + fields.map(function(field) {
var value = row[field.key] === null || row[field.key] === undefined ? '' : row[field.key];
if (field.key === 'image_path') {
return value ? '<td class="spot-confirm-nowrap"><img class="spot-confirm-image" src="' + escHtml(value) + '" onclick="previewSpotApplicationImage(\'' + escJs(value) + '\')" alt="产品图片"></td>' : '<td class="spot-confirm-nowrap"><span style="color:#bbb">—</span></td>';
}
if (field.key === 'product_link') {
return value ? '<td class="spot-confirm-nowrap"><a href="' + escHtml(value) + '" target="_blank" rel="noopener" style="color:#1677ff">查看链接</a></td>' : '<td class="spot-confirm-nowrap"><span style="color:#bbb">—</span></td>';
}
var cls = ['application_id','application_date','arrival_date','barcode','sample_quantity','oa_approval_no','express_tracking_no','product_code'].indexOf(field.key) >= 0 ? ' class="spot-confirm-nowrap"' : '';
return '<td' + cls + '>' + escHtml(value) + '</td>';
}).join('') + '<td class="spot-confirm-actions"><button type="button" class="btn btn-sm spot-confirm-detail-button spot-confirm-action-content" onclick="openSpotConfirmationDetail(' + Number(row.application_id) + ')">详情</button></td></tr>';
}).join('');
}
renderSpotConfirmationPagination(data.total, data.size, data.page);
applySpotConfirmationActions();
}).catch(function(){ if (requestId === window._spotConfirmRequestId) body.innerHTML = '<tr><td colspan="26" class="spot-confirm-empty">网络异常,请稍后重试</td></tr>'; });
}
loadSpotConfirmationPage(document.getElementById('preview'));
openSpotConfirmationFilter('brand',{stopPropagation(){},currentTarget:document.getElementById('spotConfirmFilterButton_brand')});
</script></html>