UNPKG

oimp

Version:

A CLI tool for generating OI problem and packages

104 lines (101 loc) 5.9 kB
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, '&quot;')); 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, '&quot;')); 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; }); }); }