|
|
@@ -13,6 +13,8 @@
|
|
|
mode: 'text', // text | interactive
|
|
|
from: '',
|
|
|
to: '',
|
|
|
+ schools: [],
|
|
|
+ shops: [],
|
|
|
charts: {},
|
|
|
};
|
|
|
|
|
|
@@ -67,6 +69,7 @@
|
|
|
<span class="ops-tilde">~</span>
|
|
|
<input type="date" class="ops-input" id="rpTo">
|
|
|
</div>
|
|
|
+ <div class="rp-filter-chip" id="rpFilterChip" hidden></div>
|
|
|
<div class="rp-tabs" id="rpTabs">
|
|
|
<button type="button" data-mode="text" class="on">图文报告</button>
|
|
|
<button type="button" data-mode="interactive">交互报告</button>
|
|
|
@@ -351,7 +354,7 @@
|
|
|
const { res, body } = await api('/api/ops/reports', {
|
|
|
method: 'POST',
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
- body: JSON.stringify({ from: state.from, to: state.to }),
|
|
|
+ body: JSON.stringify({ from: state.from, to: state.to, schools: state.schools.join(','), shops: state.shops.join(',') }),
|
|
|
});
|
|
|
if (!res.ok) throw new Error((body && (body.detail || body.error)) || ('HTTP ' + res.status));
|
|
|
btn.textContent = '已保存';
|
|
|
@@ -436,6 +439,8 @@
|
|
|
const params = new URLSearchParams();
|
|
|
if (state.from) params.set('from', state.from);
|
|
|
if (state.to) params.set('to', state.to);
|
|
|
+ if (state.schools.length) params.set('schools', state.schools.join(','));
|
|
|
+ if (state.shops.length) params.set('shops', state.shops.join(','));
|
|
|
params.set('ai', '1'); // 默认走 AI,失败自动退回规则版
|
|
|
const { res, body } = await api('/api/ops/report?' + params.toString());
|
|
|
if (!res.ok) throw new Error((body && (body.detail || body.error)) || ('HTTP ' + res.status));
|
|
|
@@ -452,16 +457,42 @@
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ function renderFilterChip() {
|
|
|
+ const chip = $('rpFilterChip');
|
|
|
+ if (!chip) return;
|
|
|
+ const parts = [];
|
|
|
+ if (state.schools.length) parts.push(state.schools.length === 1 ? state.schools[0] : `${state.schools.length} 个学校`);
|
|
|
+ if (state.shops.length) parts.push(state.shops.length === 1 ? `1 家门店` : `${state.shops.length} 家门店`);
|
|
|
+ if (!parts.length) {
|
|
|
+ chip.hidden = true;
|
|
|
+ chip.innerHTML = '';
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ chip.hidden = false;
|
|
|
+ chip.innerHTML = `已按 ${parts.join(' · ')} 筛选 <button type="button" id="rpFilterClear">清除</button>`;
|
|
|
+ $('rpFilterClear').addEventListener('click', () => {
|
|
|
+ state.schools = [];
|
|
|
+ state.shops = [];
|
|
|
+ renderFilterChip();
|
|
|
+ load(true);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
async function initDates() {
|
|
|
const search = new URLSearchParams(location.search);
|
|
|
const qFrom = String(search.get('from') || '').replace(/\D/g, '');
|
|
|
const qTo = String(search.get('to') || '').replace(/\D/g, '');
|
|
|
+ const qShops = String(search.get('shops') || '').split(',').map((s) => s.trim()).filter(Boolean);
|
|
|
+ const qSchools = String(search.get('schools') || '').split(',').map((s) => s.trim()).filter(Boolean);
|
|
|
+ state.shops = qShops;
|
|
|
+ state.schools = qSchools;
|
|
|
const toInput = (ymd) => `${ymd.slice(0, 4)}-${ymd.slice(4, 6)}-${ymd.slice(6, 8)}`;
|
|
|
if (/^\d{8}$/.test(qFrom) && /^\d{8}$/.test(qTo)) {
|
|
|
state.from = qFrom;
|
|
|
state.to = qTo;
|
|
|
$('rpFrom').value = toInput(qFrom);
|
|
|
$('rpTo').value = toInput(qTo);
|
|
|
+ renderFilterChip();
|
|
|
return;
|
|
|
}
|
|
|
const { body } = await api('/api/ops/summary');
|
|
|
@@ -474,6 +505,7 @@
|
|
|
state.from = from < coverage.from ? coverage.from : from;
|
|
|
$('rpFrom').value = toInput(state.from);
|
|
|
$('rpTo').value = toInput(state.to);
|
|
|
+ renderFilterChip();
|
|
|
}
|
|
|
|
|
|
window.shanReportInit = function () {
|