oimp
Version:
A CLI tool for generating OI problem and packages
104 lines (101 loc) • 5.9 kB
JavaScript
import { addBtnTooltip } from './ide-utility.js';
// 与题目状态有关
// 题目状态可视化
let lastStatus = {};
let statusFailCount = 0;
export async function initProblemStatus() {
await fetchStatus();
}
export async function fetchStatus() {
if (statusFailCount >= 5) return;
const panel = document.getElementById('status-panel');
const keys = ['problem', 'std', 'generator', 'validator', 'testsample', 'data', 'check', 'package'];
if (!panel._statusNodes) panel._statusNodes = {};
try {
const res = await fetch('/api/status');
if (!res.ok) throw new Error('status.json 不存在');
const status = await res.json();
// 首次渲染
if (!panel.hasChildNodes() || Object.keys(panel._statusNodes).length !== keys.length) {
panel.innerHTML = '';
keys.forEach(k => {
const s = status[k] || {};
let st = (s.status || 'unknown').toLowerCase();
let desc = s.desc || k;
let dotClass = 'status-dot-unknown', cardClass = 'status-unknown', text = '需重做';
if (st === 'done') { dotClass = 'status-dot-done'; cardClass = 'status-done'; text = '完成'; }
else if (st === 'pending') { dotClass = 'status-dot-pending'; cardClass = 'status-pending'; text = '待处理'; }
else if (st === 'need-redo') { dotClass = 'status-dot-need-redo'; cardClass = 'status-need-redo'; text = '需重做'; }
else if (st === 'error' || st === 'failed') { dotClass = 'status-dot-error'; cardClass = 'status-error'; text = '失败'; }
let tooltip = `<b>${k}</b>\n状态: ${s.status || '未知'}\n说明: ${desc}`;
if (s.time) tooltip += `\n时间: ${s.time}`;
const node = document.createElement('span');
node.className = `status-card ${cardClass}`;
node.setAttribute('data-tooltip', tooltip.replace(/"/g, '"'));
node.innerHTML = `<span class=\"status-dot ${dotClass}\"></span><span style=\"display:block;font-weight:500;line-height:1.1;\">${k}</span><span style=\"display:block;margin-left:0;font-size:11px;line-height:1.1;\">${text}</span>`;
panel.appendChild(node);
panel._statusNodes[k] = node;
});
setupStatusTooltip(panel);
} else {
// 增量刷新
keys.forEach(k => {
const s = status[k] || {};
const last = lastStatus[k] || {};
if (JSON.stringify(s) !== JSON.stringify(last)) {
let st = (s.status || 'unknown').toLowerCase();
let desc = s.desc || k;
let dotClass = 'status-dot-unknown', cardClass = 'status-unknown', text = '需重做';
if (st === 'done') { dotClass = 'status-dot-done'; cardClass = 'status-done'; text = '完成'; }
else if (st === 'pending') { dotClass = 'status-dot-pending'; cardClass = 'status-pending'; text = '待处理'; }
else if (st === 'need-redo') { dotClass = 'status-dot-need-redo'; cardClass = 'status-need-redo'; text = '需重做'; }
else if (st === 'error' || st === 'failed') { dotClass = 'status-dot-error'; cardClass = 'status-error'; text = '失败'; }
let tooltip = `<b>${k}</b>\n状态: ${s.status || '未知'}\n说明: ${desc}`;
if (s.time) tooltip += `\n时间: ${s.time}`;
const node = panel._statusNodes[k];
// 先移除所有动画类
node.classList.remove('status-changed', 'status-done', 'status-pending', 'status-need-redo', 'status-error', 'status-failed');
node.className = `status-card ${cardClass}`;
node.classList.add('status-changed');
node.setAttribute('data-tooltip', tooltip.replace(/"/g, '"'));
node.innerHTML = `<span class=\"status-dot ${dotClass}\"></span><span style=\"display:block;font-weight:500;line-height:1.1;\">${k}</span><span style=\"display:block;margin-left:0;font-size:11px;line-height:1.1;\">${text}</span>`;
setTimeout(() => node.classList.remove('status-changed'), 2000);
}
});
}
lastStatus = status;
} catch (e) {
statusFailCount++;
panel.innerHTML = '<span style="color:#b91c1c;font-size:12px;">状态加载失败</span>';
panel._statusNodes = {};
lastStatus = {};
if (statusFailCount >= 5) {
panel.innerHTML = '<span style="color:#b91c1c;font-size:12px;">状态加载失败(已停止自动刷新)</span>';
}
}
// 添加顶部button的提示
addBtnTooltip(document.getElementById('btn-save'), '保存 (Ctrl+S)');
addBtnTooltip(document.getElementById('btn-reload'), '重载');
}
// tooltip 逻辑
function setupStatusTooltip(panel) {
let tooltip = document.getElementById('status-tooltip');
if (!tooltip) {
tooltip = document.createElement('div');
tooltip.id = 'status-tooltip';
tooltip.className = 'status-tooltip';
document.body.appendChild(tooltip);
}
panel.querySelectorAll('.status-card').forEach(card => {
card.addEventListener('mouseenter', e => {
tooltip.innerHTML = card.getAttribute('data-tooltip').replace(/\n/g, '<br>');
tooltip.style.opacity = 1;
const rect = card.getBoundingClientRect();
tooltip.style.left = (rect.left + window.scrollX + rect.width / 2 - tooltip.offsetWidth / 2) + 'px';
tooltip.style.top = (rect.bottom + window.scrollY + 8) + 'px';
});
card.addEventListener('mouseleave', e => {
tooltip.style.opacity = 0;
});
});
}