| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- 'use strict';
- // 数据中台页:本地湖全景 + 云端同步(推送/拉取)
- // 读取 GET /api/reports/lake;推送逐主体执行(POST /api/reports/aitable/push)。
- (function () {
- const $ = (id) => document.getElementById(id);
- let inited = false;
- let loading = false;
- function fmtDate(ymd) {
- const s = String(ymd || '');
- return /^\d{8}$/.test(s) ? `${s.slice(0, 4)}-${s.slice(4, 6)}-${s.slice(6, 8)}` : (s || '–');
- }
- function fmtTime(iso) {
- if (!iso) return '–';
- const d = new Date(iso);
- return Number.isNaN(d.getTime()) ? String(iso) : `${d.getMonth() + 1}/${d.getDate()} ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
- }
- function esc(value) {
- const div = document.createElement('div');
- div.textContent = String(value ?? '');
- return div.innerHTML;
- }
- function setBadge(id, text, cls) {
- const badge = $(id);
- if (!badge) return;
- badge.textContent = text;
- badge.className = 'badge ' + (cls || 'badge-info');
- }
- function renderKpis(data) {
- $('lakeEntityCount').textContent = data.entities.length;
- $('lakeRowCount').textContent = data.totalRows;
- $('lakeCoverage').textContent = data.coverage ? `${fmtDate(data.coverage.from)} ~ ${fmtDate(data.coverage.to)}` : '–';
- const lastSyncs = data.entities.map((e) => e.lastSync && e.lastSync.at).filter(Boolean).sort();
- $('lakeLastSync').textContent = lastSyncs.length ? fmtTime(lastSyncs[lastSyncs.length - 1]) : '–';
- $('lakeRowsBadge').textContent = `${data.entities.length} 主体`;
- }
- function renderEntities(entities) {
- const tbody = $('lakeEntityRows');
- if (!entities.length) {
- tbody.innerHTML = '<tr><td colspan="10" class="empty-cell">本地湖为空 —— 取数机先「同步数据」,其他电脑点右上「从云端拉取」</td></tr>';
- return;
- }
- tbody.innerHTML = entities.map((e) => {
- const cov = e.coverage ? `${fmtDate(e.coverage.from)} ~ ${fmtDate(e.coverage.to)}` : '–';
- const sync = e.lastSync ? `${fmtTime(e.lastSync.at)}(${fmtDate(e.lastSync.from)}~${fmtDate(e.lastSync.to)})${e.lastSync.ok ? '' : ' ⚠'}` : '–';
- const push = e.lastPush ? `${fmtTime(e.lastPush.at)} / ${e.lastPush.pushedRows || 0} 行` : '未推送';
- const pending = typeof e.pending === 'number' && e.pending > 0 ? `<span class="badge badge-warn">待推 ${e.pending} 行</span>` : '';
- return `<tr>
- <td>${esc(e.name || e.key)}</td>
- <td>${e.shopCount || 0} / ${e.chainCount || 0}</td>
- <td>${e.rows}</td>
- <td>${e.byScope.company || 0}</td>
- <td>${e.byScope.chain || 0}</td>
- <td>${e.byScope.shop || 0}</td>
- <td>${e.byScope.summary || 0}</td>
- <td>${cov}</td>
- <td>${esc(sync)}</td>
- <td>${esc(push)} ${pending}</td>
- </tr>`;
- }).join('');
- }
- async function loadLake() {
- if (loading) return;
- loading = true;
- try {
- const res = await fetch('/api/reports/lake');
- const data = await res.json();
- if (!res.ok) throw new Error(data.error || `HTTP ${res.status}`);
- renderKpis(data);
- renderEntities(data.entities);
- if (!data.aitable.enabled) setBadge('lakeSyncBadge', '云端同步未启用(系统设置)', 'badge-info');
- else if (!data.aitable.operatorConfigured) setBadge('lakeSyncBadge', '云端配置不完整', 'badge-fail');
- else setBadge('lakeSyncBadge', '云端就绪', 'badge-ok');
- } catch (error) {
- setBadge('lakeSyncBadge', `加载失败:${error.message}`, 'badge-fail');
- } finally {
- loading = false;
- }
- }
- // 推送动作在数据采集页(取数机的动作);中台只负责看数与从云端拉取
- async function pullAll() {
- const btn = $('btnLakePull');
- btn.disabled = true;
- try {
- await window.shanAitableAction('pull', 'lakeSyncBadge', {});
- await loadLake();
- } catch { /* 徽标已由 aitableAction 写入 */ } finally {
- btn.disabled = false;
- }
- }
- // 页内 tab:湖全景 / 明细数据(明细区首次激活时才初始化报表浏览,懒加载)
- function activateLakeTab(tab) {
- const tabs = $('lakeTabs');
- if (!tabs) return;
- tabs.querySelectorAll('button').forEach((b) => b.classList.toggle('active', b.dataset.tab === tab));
- $('lakeTabOverview').hidden = tab !== 'overview';
- $('lakeTabDetail').hidden = tab !== 'detail';
- if (tab === 'overview') loadLake();
- else if (window.shanReportsInit) window.shanReportsInit();
- }
- window.shanLakeInit = function () {
- if (!inited) {
- inited = true;
- $('btnLakeRefresh').addEventListener('click', loadLake);
- $('btnLakePull').addEventListener('click', pullAll);
- $('lakeTabs').addEventListener('click', (event) => {
- const btn = event.target.closest('button[data-tab]');
- if (btn) activateLakeTab(btn.dataset.tab);
- });
- // 「?」弹窗:与采集页连接帮助同款交互(点按钮开合,点外部关闭)
- const helpBtn = $('btnLakeHelp');
- const helpPop = $('lakeHelpPopover');
- if (helpBtn && helpPop) {
- helpBtn.addEventListener('click', (event) => {
- event.stopPropagation();
- helpPop.hidden = !helpPop.hidden;
- helpBtn.classList.toggle('active', !helpPop.hidden);
- helpBtn.setAttribute('aria-expanded', String(!helpPop.hidden));
- });
- document.addEventListener('click', (event) => {
- if (!helpPop.hidden && !event.target.closest('.conn-help-wrap')) {
- helpPop.hidden = true;
- helpBtn.classList.remove('active');
- helpBtn.setAttribute('aria-expanded', 'false');
- }
- });
- }
- }
- activateLakeTab('overview');
- };
- // 供问数「应用到报表」等外部入口直接落到明细 tab
- window.shanLakeShowDetail = function () {
- activateLakeTab('detail');
- };
- })();
|