/* webtest-agent 前端逻辑 */
const $ = s => document.querySelector(s);
const API = '';
async function refreshHealth() {
try {
const r = await fetch(API + '/health');
const d = await r.json();
$('#health').textContent = `服务正常 | 模型: ${d.llm_model} | 运行中任务: ${d.concurrent}`;
$('#health').classList.add('ok');
} catch (e) {
$('#health').textContent = '服务异常';
$('#health').classList.remove('ok');
}
}
const STATUS_MAP = {
queued: '排队中', running: '测试中', finished: '已完成', stopped: '已停止'
};
const RESULT_MAP = {
pending: '待定', pass: '✅ 通过', fail: '❌ 失败', error: '⚠️ 错误', stopped: '⏹️ 停止'
};
function esc(s) {
return String(s ?? '').replace(/[&<>"']/g, c => (
{'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
}
async function loadTasks() {
try {
const r = await fetch(API + '/api/tasks');
const d = await r.json();
const tb = $('#task-table tbody');
if (!d.tasks.length) {
tb.innerHTML = '
| 暂无任务,先发起一个吧 |
';
return;
}
tb.innerHTML = d.tasks.map(t => {
const ops = t.status === 'running' || t.status === 'queued'
? ` `
: '';
const report = t.status === 'finished'
? `查看报告`
: '—';
return `
| ${esc(t.id)} |
${esc(t.url)} |
${esc(t.goal)} |
${STATUS_MAP[t.status] || esc(t.status)} |
${RESULT_MAP[t.result] || esc(t.result)} |
${t.steps || 0} |
${esc(t.created)} |
${ops}${report} |
`;
}).join('');
} catch (e) {
$('#task-table tbody').innerHTML = '| 加载失败 |
';
}
}
async function stopTask(tid) {
if (!confirm('确定停止该任务?')) return;
await fetch(API + `/api/tasks/${tid}/stop`, { method: 'POST' });
setTimeout(loadTasks, 500);
}
function openReport(tid) {
$('#report-frame').src = API + `/api/tasks/${tid}/report`;
$('#modal-title').textContent = `测试报告 - ${tid}`;
$('#report-modal').classList.remove('hidden');
}
$('#modal-close').onclick = () => $('#report-modal').classList.add('hidden');
$('#report-modal').onclick = e => { if (e.target === $('#report-modal')) $('#report-modal').classList.add('hidden'); };
$('#submit').onclick = async () => {
const url = $('#url').value.trim();
const goal = $('#goal').value.trim();
if (!url || !goal) { alert('请填写网址和测试目标'); return; }
const btn = $('#submit');
btn.disabled = true; btn.textContent = '提交中...';
try {
const r = await fetch(API + '/api/tasks', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
url, goal,
max_steps: parseInt($('#max_steps').value) || 30,
timeout: parseInt($('#timeout').value) || 600
})
});
const d = await r.json();
if (d.error) { alert('提交失败: ' + d.error); }
else {
$('#goal').value = '';
loadTasks();
// 轮询直到该任务结束
pollTask(d.task_id);
}
} catch (e) { alert('提交失败: ' + e); }
finally { btn.disabled = false; btn.textContent = '🚀 开始测试'; }
};
function pollTask(tid, count = 0) {
if (count > 400) return;
setTimeout(async () => {
try {
const r = await fetch(API + `/api/tasks/${tid}`);
const t = await r.json();
loadTasks();
if (t.status === 'finished' || t.status === 'stopped') return;
pollTask(tid, count + 1);
} catch (e) { /* ignore */ }
}, 3000);
}
$('#refresh').onclick = loadTasks;
refreshHealth();
loadTasks();
setInterval(refreshHealth, 30000);
setInterval(loadTasks, 5000);