219 lines
7.3 KiB
JavaScript
219 lines
7.3 KiB
JavaScript
// 状态
|
|
let currentAgent = null;
|
|
let currentSession = null;
|
|
|
|
// API 调用
|
|
async function fetchAPI(endpoint) {
|
|
const response = await fetch(`/api${endpoint}`);
|
|
if (!response.ok) {
|
|
throw new Error(`API Error: ${response.status}`);
|
|
}
|
|
return response.json();
|
|
}
|
|
|
|
// 格式化时间
|
|
function formatTime(isoString) {
|
|
if (!isoString) return '-';
|
|
const date = new Date(isoString);
|
|
return date.toLocaleString('zh-CN', {
|
|
month: '2-digit',
|
|
day: '2-digit',
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
});
|
|
}
|
|
|
|
function formatFullTime(isoString) {
|
|
if (!isoString) return '-';
|
|
const date = new Date(isoString);
|
|
return date.toLocaleString('zh-CN', {
|
|
year: 'numeric',
|
|
month: '2-digit',
|
|
day: '2-digit',
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
second: '2-digit'
|
|
});
|
|
}
|
|
|
|
// 格式化数字
|
|
function formatNumber(num) {
|
|
if (!num) return '0';
|
|
if (num >= 1000000) return (num / 1000000).toFixed(1) + 'M';
|
|
if (num >= 1000) return (num / 1000).toFixed(1) + 'K';
|
|
return num.toString();
|
|
}
|
|
|
|
// 截断文本
|
|
function truncate(text, maxLen = 100) {
|
|
if (!text) return '';
|
|
if (text.length <= maxLen) return text;
|
|
return text.substring(0, maxLen) + '...';
|
|
}
|
|
|
|
// 加载智能体列表
|
|
async function loadAgents() {
|
|
const container = document.getElementById('agents-list');
|
|
container.innerHTML = '<div class="loading">加载中...</div>';
|
|
|
|
try {
|
|
const agents = await fetchAPI('/agents');
|
|
|
|
if (agents.length === 0) {
|
|
container.innerHTML = '<div class="placeholder">没有找到智能体</div>';
|
|
return;
|
|
}
|
|
|
|
container.innerHTML = agents.map(agent => `
|
|
<div class="agent-item ${currentAgent === agent.name ? 'active' : ''}"
|
|
onclick="selectAgent('${agent.name}')">
|
|
<div class="name">🤖 ${agent.name}</div>
|
|
<div class="meta">
|
|
<span>💬 ${agent.sessionCount}</span>
|
|
<span>${formatTime(agent.lastActive)}</span>
|
|
</div>
|
|
</div>
|
|
`).join('');
|
|
} catch (error) {
|
|
container.innerHTML = `<div class="placeholder">加载失败: ${error.message}</div>`;
|
|
}
|
|
}
|
|
|
|
// 选择智能体
|
|
async function selectAgent(agentName) {
|
|
currentAgent = agentName;
|
|
currentSession = null;
|
|
|
|
// 更新UI
|
|
document.querySelectorAll('.agent-item').forEach(el => el.classList.remove('active'));
|
|
event.target.closest('.agent-item')?.classList.add('active');
|
|
|
|
document.getElementById('agent-name-display').textContent = `- ${agentName}`;
|
|
document.getElementById('session-id-display').textContent = '';
|
|
document.getElementById('messages-list').innerHTML = '<div class="placeholder">请选择一个会话</div>';
|
|
|
|
// 加载会话列表
|
|
await loadSessions(agentName);
|
|
}
|
|
|
|
// 加载会话列表
|
|
async function loadSessions(agentName) {
|
|
const container = document.getElementById('sessions-list');
|
|
container.innerHTML = '<div class="loading">加载中...</div>';
|
|
|
|
try {
|
|
const sessions = await fetchAPI(`/agents/${agentName}/sessions`);
|
|
|
|
if (sessions.length === 0) {
|
|
container.innerHTML = '<div class="placeholder">没有找到会话</div>';
|
|
return;
|
|
}
|
|
|
|
container.innerHTML = sessions.map(session => `
|
|
<div class="session-item ${currentSession === session.sessionId ? 'active' : ''}"
|
|
onclick="selectSession('${agentName}', '${session.sessionId}')">
|
|
<div class="channel ${session.channel}">${session.channel}</div>
|
|
<span class="status ${session.status}">${session.status}</span>
|
|
<div class="meta">
|
|
<span class="model">${session.model}</span>
|
|
<span>${formatTime(session.updatedAt)}</span>
|
|
</div>
|
|
<div class="target">${truncate(session.target, 50)}</div>
|
|
<div class="tokens">
|
|
📊 ${formatNumber(session.totalTokens)} tokens
|
|
(${formatNumber(session.inputTokens)} in / ${formatNumber(session.outputTokens)} out)
|
|
</div>
|
|
</div>
|
|
`).join('');
|
|
} catch (error) {
|
|
container.innerHTML = `<div class="placeholder">加载失败: ${error.message}</div>`;
|
|
}
|
|
}
|
|
|
|
// 选择会话
|
|
async function selectSession(agentName, sessionId) {
|
|
currentSession = sessionId;
|
|
|
|
// 更新UI
|
|
document.querySelectorAll('.session-item').forEach(el => el.classList.remove('active'));
|
|
event.target.closest('.session-item')?.classList.add('active');
|
|
|
|
document.getElementById('session-id-display').textContent = `- ${sessionId.substring(0, 8)}...`;
|
|
|
|
// 加载消息
|
|
await loadMessages(agentName, sessionId);
|
|
}
|
|
|
|
// 加载消息
|
|
async function loadMessages(agentName, sessionId) {
|
|
const container = document.getElementById('messages-list');
|
|
container.innerHTML = '<div class="loading">加载中...</div>';
|
|
|
|
try {
|
|
const messages = await fetchAPI(`/agents/${agentName}/sessions/${sessionId}/messages`);
|
|
|
|
if (messages.length === 0) {
|
|
container.innerHTML = '<div class="placeholder">没有找到消息</div>';
|
|
return;
|
|
}
|
|
|
|
container.innerHTML = messages.map((msg, index) => `
|
|
<div class="message-item ${msg.role}" data-agent="${agentName}" data-session="${sessionId}" data-msg-id="${msg.id}">
|
|
<div class="header">
|
|
<span class="role ${msg.role}">${msg.role}</span>
|
|
<span class="time">${formatFullTime(msg.timestamp)}</span>
|
|
</div>
|
|
<div class="content ${msg.fullContent ? 'truncated' : ''}">${escapeHtml(msg.content)}</div>
|
|
${msg.fullContent ? `<button class="expand-btn" onclick="showFullMessage('${agentName}', '${sessionId}', '${msg.id}')">查看完整内容</button>` : ''}
|
|
</div>
|
|
`).join('');
|
|
} catch (error) {
|
|
container.innerHTML = `<div class="placeholder">加载失败: ${error.message}</div>`;
|
|
}
|
|
}
|
|
|
|
// 显示完整消息
|
|
async function showFullMessage(agentName, sessionId, messageId) {
|
|
const modal = document.getElementById('message-modal');
|
|
const content = document.getElementById('modal-content');
|
|
|
|
modal.classList.remove('hidden');
|
|
content.textContent = '加载中...';
|
|
|
|
try {
|
|
const msg = await fetchAPI(`/agents/${agentName}/sessions/${sessionId}/messages/${messageId}`);
|
|
document.getElementById('modal-title').textContent = `${msg.role.toUpperCase()} - ${formatFullTime(msg.timestamp)}`;
|
|
content.textContent = msg.content;
|
|
} catch (error) {
|
|
content.textContent = `加载失败: ${error.message}`;
|
|
}
|
|
}
|
|
|
|
// 关闭模态框
|
|
function closeModal() {
|
|
document.getElementById('message-modal').classList.add('hidden');
|
|
}
|
|
|
|
// HTML 转义
|
|
function escapeHtml(text) {
|
|
const div = document.createElement('div');
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
}
|
|
|
|
// 键盘事件
|
|
document.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Escape') {
|
|
closeModal();
|
|
}
|
|
});
|
|
|
|
// 点击模态框外部关闭
|
|
document.getElementById('message-modal').addEventListener('click', (e) => {
|
|
if (e.target === e.currentTarget) {
|
|
closeModal();
|
|
}
|
|
});
|
|
|
|
// 初始化
|
|
loadAgents(); |