Files
xk-hy-transit-go/internal/logweb/web/app.js
2026-06-05 16:36:39 +08:00

128 lines
3.8 KiB
JavaScript
Raw 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.
async function api(path, opts) {
const r = await fetch(path, opts);
const j = await r.json().catch(() => ({}));
if (!r.ok) {
const err = new Error(j.error || r.statusText);
if (j.detail) err.detail = j.detail;
if (j.uploadUrl) err.uploadUrl = j.uploadUrl;
throw err;
}
return j;
}
/** 右下角 Toasttype: ok | err | warn | info */
function showToast(message, type) {
if (!message) return;
let root = document.getElementById('toast-root');
if (!root) {
root = document.createElement('div');
root.id = 'toast-root';
root.className = 'toast-root';
document.body.appendChild(root);
}
const el = document.createElement('div');
el.className = 'toast ' + (type || 'info');
el.textContent = message;
root.appendChild(el);
requestAnimationFrame(() => el.classList.add('show'));
setTimeout(() => {
el.classList.remove('show');
setTimeout(() => el.remove(), 300);
}, 4500);
}
function showInlineError(el, message) {
if (!el) return;
el.innerHTML = '<div class="flash err">' + esc(message) + '</div>';
}
function firstLine(s) {
if (!s) return '';
const i = s.indexOf('\n');
return i >= 0 ? s.slice(0, i) : s;
}
function qs(name) {
return new URLSearchParams(location.search).get(name) || '';
}
/** 状态徽章text 为中文标签code 为原始值(用于配色) */
function badge(code, text) {
const display = text != null && text !== '' ? text : (code || '-');
const s = (code || display || '').toLowerCase();
let cls = 'info';
if (s === 'success' || s === 'done') cls = 'ok';
else if (s === 'failed' || s === 'fail' || s === 'error') cls = 'err';
else if (s === 'skipped' || s === 'warning' || s === 'waiting' || s === 'pending') cls = 'warn';
else if (s === 'running' || s === 'pulling') cls = 'info';
return `<span class="badge ${cls}">${esc(display)}</span>`;
}
function esc(s) {
const d = document.createElement('div');
d.textContent = s == null ? '' : String(s);
return d.innerHTML;
}
function fmtTime(nt) {
if (!nt || !nt.Valid || !nt.Time) return '-';
try { return new Date(nt.Time).toLocaleString(); } catch { return nt.Time; }
}
function fmtTimeAny(t) {
if (!t) return '-';
if (typeof t === 'string') {
try { return new Date(t).toLocaleString(); } catch { return t; }
}
return fmtTime(t);
}
async function loadMeta() {
try {
const m = await api('/api/meta');
const el = document.getElementById('meta-footer');
if (el) {
el.innerHTML = `日志目录: ${esc(m.logDir)} | MySQL: ${m.mysqlOk ? '<span class="badge ok">已连接</span>' : '<span class="badge err">' + esc(m.mysqlError || '未连接') + '</span>'}`;
}
return m;
} catch (e) {
return null;
}
}
function fillStepSelect(sel, options, includeAll) {
if (!sel || !options) return;
sel.innerHTML = options.map(o =>
`<option value="${esc(o.value)}">${esc(o.label)}</option>`
).join('');
}
const MAIN_NAV = [
{ href: '/', id: 'home', label: '首页' },
{ href: '/config', id: 'config', label: '配置' },
{ href: '/fileauth', id: 'fileauth', label: '上传凭证' },
{ href: '/upload', id: 'upload', label: '上传' },
{ href: '/supervise-step', id: 'supervise-step', label: '处方核销' },
{ href: '/runs', id: 'runs', label: '流水' },
{ href: '/logs', id: 'logs', label: '日志' },
{ href: '/test', id: 'test', label: '测试' },
];
function renderMainNav(activeId) {
const el = document.getElementById('main-nav');
if (!el) return;
el.innerHTML = MAIN_NAV.map(n => {
const cur = n.id === activeId ? ' style="border-color:var(--accent)"' : '';
return `<a href="${n.href}"${cur}>${esc(n.label)}</a>`;
}).join('');
}
async function retryRecord(id, onOk) {
await api('/api/runs/retry', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id: Number(id) }),
});
if (onOk) onOk();
}