Files
task-board/public/index.html
T

885 lines
27 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
2026-02-15 21:51:05 +08:00
<title>多智能体任务管理系统 v2.0</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
2026-02-15 21:51:05 +08:00
max-width: 1600px;
margin: 0 auto;
}
header {
text-align: center;
color: white;
margin-bottom: 30px;
}
h1 {
font-size: 2.5em;
margin-bottom: 10px;
}
2026-02-15 21:51:05 +08:00
.version {
background: rgba(255,255,255,0.2);
padding: 5px 15px;
border-radius: 20px;
display: inline-block;
font-size: 0.9em;
}
.tabs {
display: flex;
gap: 10px;
margin-bottom: 20px;
background: white;
padding: 10px;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.tab {
padding: 12px 24px;
border: none;
background: #f0f0f0;
border-radius: 8px;
cursor: pointer;
font-weight: bold;
transition: all 0.3s;
}
.tab.active {
background: #667eea;
color: white;
}
.tab:hover:not(.active) {
background: #e0e0e0;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin-bottom: 30px;
}
.stat-card {
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
text-align: center;
}
.stat-card h3 {
color: #666;
font-size: 0.9em;
margin-bottom: 10px;
}
.stat-card .number {
font-size: 2em;
font-weight: bold;
color: #667eea;
}
.panel {
background: white;
border-radius: 10px;
padding: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
2026-02-15 21:51:05 +08:00
margin-bottom: 20px;
}
.panel h2 {
color: #333;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 2px solid #667eea;
}
2026-02-15 21:51:05 +08:00
.grid-2 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
2026-02-15 21:51:05 +08:00
.form-group {
margin-bottom: 15px;
}
2026-02-15 21:51:05 +08:00
.form-group label {
display: block;
margin-bottom: 5px;
color: #333;
font-weight: bold;
}
2026-02-15 21:51:05 +08:00
.form-group input,
.form-group textarea,
.form-group select {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 1em;
}
2026-02-15 21:51:05 +08:00
.form-group textarea {
resize: vertical;
min-height: 100px;
font-family: 'Courier New', monospace;
}
button {
padding: 10px 20px;
border: none;
border-radius: 5px;
background: #667eea;
color: white;
font-weight: bold;
cursor: pointer;
transition: all 0.3s;
}
button:hover {
background: #764ba2;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
2026-02-15 21:51:05 +08:00
button.secondary {
background: #95a5a6;
}
2026-02-15 21:51:05 +08:00
button.secondary:hover {
background: #7f8c8d;
}
2026-02-15 21:51:05 +08:00
button.danger {
background: #e74c3c;
}
2026-02-15 21:51:05 +08:00
button.danger:hover {
background: #c0392b;
}
2026-02-15 21:51:05 +08:00
.item-card {
padding: 15px;
margin-bottom: 10px;
border-radius: 8px;
background: #f8f9fa;
border-left: 4px solid #667eea;
cursor: pointer;
transition: all 0.3s;
}
.item-card:hover {
transform: translateX(5px);
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.item-card.selected {
background: #e8f4f8;
border-left-color: #3498db;
}
.badge {
display: inline-block;
padding: 4px 12px;
border-radius: 20px;
font-size: 0.85em;
2026-02-15 21:51:05 +08:00
font-weight: bold;
margin-left: 10px;
}
2026-02-15 21:51:05 +08:00
.badge.initial { background: #3498db; color: white; }
.badge.in_progress { background: #f39c12; color: white; }
.badge.testing_pending { background: #9b59b6; color: white; }
.badge.testing { background: #e67e22; color: white; }
.badge.completed { background: #27ae60; color: white; }
.badge.high { background: #e74c3c; color: white; }
.badge.normal { background: #3498db; color: white; }
.badge.low { background: #95a5a6; color: white; }
.badge.requirement { background: #9b59b6; color: white; }
.badge.design { background: #3498db; color: white; }
.badge.development { background: #2ecc71; color: white; }
.badge.testing { background: #e67e22; color: white; }
.badge.deployment { background: #e74c3c; color: white; }
.empty-state {
text-align: center;
color: #999;
padding: 40px;
}
2026-02-15 21:51:05 +08:00
.markdown-preview {
background: #f8f9fa;
padding: 15px;
border-radius: 5px;
border: 1px solid #ddd;
margin-top: 10px;
}
.markdown-preview h1 { font-size: 1.8em; margin-bottom: 10px; }
.markdown-preview h2 { font-size: 1.5em; margin-bottom: 8px; }
.markdown-preview h3 { font-size: 1.2em; margin-bottom: 5px; }
.markdown-preview p { margin-bottom: 10px; line-height: 1.6; }
.markdown-preview ul, .markdown-preview ol { margin-left: 20px; margin-bottom: 10px; }
.markdown-preview code { background: #e8e8e8; padding: 2px 6px; border-radius: 3px; }
.markdown-preview pre { background: #2d2d2d; color: #f8f8f2; padding: 15px; border-radius: 5px; overflow-x: auto; }
.note-item {
background: #fff9e6;
padding: 15px;
margin-bottom: 10px;
border-radius: 8px;
border-left: 4px solid #f39c12;
}
.note-meta {
font-size: 0.85em;
color: #666;
margin-top: 8px;
}
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
z-index: 1000;
align-items: center;
justify-content: center;
}
.modal.active {
display: flex;
}
.modal-content {
background: white;
padding: 30px;
border-radius: 10px;
max-width: 600px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
}
.refresh-btn {
float: right;
padding: 5px 15px;
font-size: 0.9em;
}
2026-02-15 21:51:05 +08:00
#rulesEditor {
font-family: 'Courier New', monospace;
min-height: 200px;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>🤖 多智能体任务管理系统</h1>
2026-02-15 21:51:05 +08:00
<span class="version">v2.0 - 项目管理版</span>
</header>
2026-02-15 21:51:05 +08:00
<div class="stats" id="stats"></div>
<div class="tabs">
<button class="tab active" onclick="switchTab('projects')">📁 项目管理</button>
<button class="tab" onclick="switchTab('tasks')">📋 任务列表</button>
<button class="tab" onclick="switchTab('agents')">🤖 智能体</button>
<button class="tab" onclick="switchTab('rules')">📜 协作规约</button>
</div>
2026-02-15 21:51:05 +08:00
<!-- 项目管理标签页 -->
<div id="tab-projects" class="tab-content active">
<div class="grid-2">
<div class="panel">
<h2>项目列表 <button class="refresh-btn" onclick="loadProjects()">刷新</button></h2>
<div id="projectList"></div>
<button onclick="showCreateProjectModal()" style="margin-top: 15px;">创建新项目</button>
</div>
2026-02-15 21:51:05 +08:00
<div class="panel">
<h2>项目详情</h2>
<div id="projectDetail">
<div class="empty-state">请选择一个项目</div>
</div>
</div>
</div>
</div>
<!-- 任务列表标签页 -->
<div id="tab-tasks" class="tab-content">
<div class="panel">
<h2>任务列表 <button class="refresh-btn" onclick="loadTasks()">刷新</button></h2>
<div style="margin-bottom: 15px;">
<select id="filterProject" onchange="loadTasks()" style="width: 200px; margin-right: 10px;">
<option value="">所有项目</option>
</select>
<select id="filterStatus" onchange="loadTasks()" style="width: 150px; margin-right: 10px;">
<option value="">所有状态</option>
<option value="initial">初始</option>
<option value="in_progress">进行中</option>
<option value="testing_pending">待测试</option>
<option value="testing">测试中</option>
<option value="completed">完成</option>
</select>
</div>
<div id="taskList"></div>
<button onclick="showCreateTaskModal()" style="margin-top: 15px;">创建新任务</button>
</div>
</div>
<!-- 智能体标签页 -->
<div id="tab-agents" class="tab-content">
<div class="panel">
<h2>智能体列表 <button class="refresh-btn" onclick="loadAgents()">刷新</button></h2>
<div id="agentList"></div>
</div>
</div>
<!-- 协作规约标签页 -->
<div id="tab-rules" class="tab-content">
<div class="panel">
<h2>全局协作规约</h2>
<div id="globalRulesContent"></div>
<button onclick="showEditGlobalRulesModal()">编辑全局规约</button>
</div>
<div class="panel">
2026-02-15 21:51:05 +08:00
<h2>项目协作规约</h2>
<p style="color: #666; margin-bottom: 15px;">请在"项目管理"标签页中选择项目后设置项目规约</p>
</div>
</div>
</div>
2026-02-15 21:51:05 +08:00
<!-- 创建项目模态框 -->
<div id="createProjectModal" class="modal">
<div class="modal-content">
<h2>创建新项目</h2>
<form id="createProjectForm">
<div class="form-group">
<label>项目名称 *</label>
<input type="text" id="projectName" required>
</div>
<div class="form-group">
<label>项目描述</label>
<textarea id="projectDesc"></textarea>
</div>
<button type="submit">创建</button>
<button type="button" class="secondary" onclick="closeModal('createProjectModal')">取消</button>
</form>
</div>
</div>
<!-- 创建任务模态框 -->
<div id="createTaskModal" class="modal">
<div class="modal-content">
<h2>创建新任务</h2>
<form id="createTaskForm">
<div class="form-group">
<label>所属项目 *</label>
<select id="taskProject" required></select>
</div>
<div class="form-group">
<label>任务标题 *</label>
<input type="text" id="taskTitle" required>
</div>
<div class="form-group">
<label>任务描述(Markdown</label>
<textarea id="taskDesc"></textarea>
</div>
<div class="form-group">
<label>任务类型</label>
<select id="taskType">
<option value="requirement">需求</option>
<option value="design">设计</option>
<option value="development" selected>开发</option>
<option value="testing">测试</option>
<option value="deployment">部署</option>
</select>
</div>
<div class="form-group">
<label>优先级</label>
<select id="taskPriority">
<option value="low"></option>
<option value="normal" selected>普通</option>
<option value="high"></option>
</select>
</div>
<button type="submit">创建</button>
<button type="button" class="secondary" onclick="closeModal('createTaskModal')">取消</button>
</form>
</div>
</div>
<!-- 编辑全局规约模态框 -->
<div id="editGlobalRulesModal" class="modal">
<div class="modal-content">
<h2>编辑全局协作规约</h2>
<form id="editGlobalRulesForm">
<div class="form-group">
<label>规约内容(Markdown</label>
<textarea id="rulesEditor"></textarea>
</div>
<button type="submit">保存</button>
<button type="button" class="secondary" onclick="closeModal('editGlobalRulesModal')">取消</button>
</form>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script>
2026-02-15 21:51:05 +08:00
const API_BASE = '/api';
let currentProject = null;
let projects = [];
// 标签页切换
function switchTab(tabName) {
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(t => t.classList.remove('active'));
event.target.classList.add('active');
document.getElementById(`tab-${tabName}`).classList.add('active');
// 加载对应数据
if (tabName === 'projects') loadProjects();
if (tabName === 'tasks') loadTasks();
if (tabName === 'agents') loadAgents();
if (tabName === 'rules') loadGlobalRules();
}
// 加载统计数据
async function loadStats() {
try {
2026-02-15 21:51:05 +08:00
const response = await fetch(`${API_BASE}/stats`);
const stats = await response.json();
document.getElementById('stats').innerHTML = `
2026-02-15 21:51:05 +08:00
<div class="stat-card">
<h3>项目总数</h3>
<div class="number">${stats.total_projects || 0}</div>
</div>
<div class="stat-card">
<h3>智能体总数</h3>
<div class="number">${stats.total_agents || 0}</div>
</div>
<div class="stat-card">
<h3>活跃智能体</h3>
<div class="number">${stats.active_agents || 0}</div>
</div>
<div class="stat-card">
2026-02-15 21:51:05 +08:00
<h3>任务总数</h3>
<div class="number">${stats.total_tasks || 0}</div>
</div>
<div class="stat-card">
<h3>进行中任务</h3>
2026-02-15 21:51:05 +08:00
<div class="number">${stats.tasks_by_status.in_progress || 0}</div>
</div>
<div class="stat-card">
<h3>已完成任务</h3>
2026-02-15 21:51:05 +08:00
<div class="number">${stats.tasks_by_status.completed || 0}</div>
</div>
`;
} catch (error) {
console.error('加载统计数据失败:', error);
}
}
2026-02-15 21:51:05 +08:00
// 加载项目列表
async function loadProjects() {
try {
2026-02-15 21:51:05 +08:00
const response = await fetch(`${API_BASE}/projects`);
projects = await response.json();
2026-02-15 21:51:05 +08:00
const projectList = document.getElementById('projectList');
2026-02-15 21:51:05 +08:00
if (projects.length === 0) {
projectList.innerHTML = '<div class="empty-state">暂无项目</div>';
return;
}
2026-02-15 21:51:05 +08:00
projectList.innerHTML = projects.map(project => `
<div class="item-card ${currentProject?.id === project.id ? 'selected' : ''}"
onclick="selectProject('${project.id}')">
<strong>${project.name}</strong>
<span class="badge" style="background: ${project.status === 'active' ? '#27ae60' : '#95a5a6'}">${project.status === 'active' ? '活跃' : '归档'}</span>
<div style="margin-top: 8px; font-size: 0.9em; color: #666;">
${project.description || '无描述'}
</div>
2026-02-15 21:51:05 +08:00
<div style="margin-top: 5px; font-size: 0.85em; color: #999;">
创建时间: ${new Date(parseInt(project.created_at)).toLocaleString('zh-CN')}
</div>
</div>
`).join('');
// 更新任务创建表单中的项目选择
updateProjectSelects();
} catch (error) {
2026-02-15 21:51:05 +08:00
console.error('加载项目列表失败:', error);
}
}
// 选择项目
async function selectProject(projectId) {
currentProject = projects.find(p => p.id === projectId);
loadProjects(); // 重新渲染高亮
// 加载项目详情
const detail = document.getElementById('projectDetail');
detail.innerHTML = '<div style="text-align: center; padding: 20px;">加载中...</div>';
try {
// 获取项目任务
const tasksResponse = await fetch(`${API_BASE}/tasks?project_id=${projectId}`);
const tasks = await tasksResponse.json();
// 获取项目规约
const rulesResponse = await fetch(`${API_BASE}/projects/${projectId}/rules`);
const rules = await rulesResponse.json();
// 获取项目笔记
const notesResponse = await fetch(`${API_BASE}/projects/${projectId}/notes`);
const notes = await notesResponse.json();
detail.innerHTML = `
<h3>${currentProject.name}</h3>
<p style="color: #666; margin-bottom: 20px;">${currentProject.description || '无描述'}</p>
<h4>📋 任务统计</h4>
<div style="margin-bottom: 20px;">
<span class="badge">总计: ${tasks.length}</span>
<span class="badge initial">初始: ${tasks.filter(t => t.status === 'initial').length}</span>
<span class="badge in_progress">进行中: ${tasks.filter(t => t.status === 'in_progress').length}</span>
<span class="badge completed">完成: ${tasks.filter(t => t.status === 'completed').length}</span>
</div>
<h4>📜 项目规约</h4>
<div style="margin-bottom: 20px;">
${rules ? `<div class="markdown-preview">${marked.parse(rules.content)}</div>` : '<p style="color: #999;">未设置项目规约</p>'}
<button onclick="editProjectRules('${projectId}')" style="margin-top: 10px;">编辑规约</button>
</div>
<h4>📝 信息共享清单 (${notes.length})</h4>
<div id="projectNotes">
${notes.length > 0 ? notes.map(note => `
<div class="note-item">
<div class="markdown-preview">${marked.parse(note.content)}</div>
<div class="note-meta">
${note.created_by} 创建于 ${new Date(parseInt(note.created_at)).toLocaleString('zh-CN')}
</div>
</div>
`).join('') : '<p style="color: #999;">暂无共享笔记</p>'}
</div>
<button onclick="addProjectNote('${projectId}')">添加笔记</button>
`;
} catch (error) {
detail.innerHTML = '<div class="empty-state">加载失败</div>';
console.error('加载项目详情失败:', error);
}
}
// 加载任务列表
async function loadTasks() {
try {
2026-02-15 21:51:05 +08:00
const projectFilter = document.getElementById('filterProject')?.value || '';
const statusFilter = document.getElementById('filterStatus')?.value || '';
let url = `${API_BASE}/tasks?`;
if (projectFilter) url += `project_id=${projectFilter}&`;
if (statusFilter) url += `status=${statusFilter}&`;
const response = await fetch(url);
const tasks = await response.json();
const taskList = document.getElementById('taskList');
if (tasks.length === 0) {
taskList.innerHTML = '<div class="empty-state">暂无任务</div>';
return;
}
taskList.innerHTML = tasks.map(task => {
const statusText = {
2026-02-15 21:51:05 +08:00
'initial': '初始',
'in_progress': '进行中',
2026-02-15 21:51:05 +08:00
'testing_pending': '待测试',
'testing': '测试中',
'completed': '完成'
}[task.status] || task.status;
2026-02-15 21:51:05 +08:00
const typeText = {
'requirement': '需求',
'design': '设计',
'development': '开发',
'testing': '测试',
'deployment': '部署'
}[task.task_type] || task.task_type;
const priorityText = {
'low': '低',
'normal': '普通',
'high': '高'
}[task.priority] || task.priority;
return `
2026-02-15 21:51:05 +08:00
<div class="item-card">
<strong>${task.title}</strong>
<span class="badge ${task.status}">${statusText}</span>
2026-02-15 21:51:05 +08:00
<span class="badge ${task.task_type}">${typeText}</span>
<span class="badge ${task.priority}">${priorityText}</span>
2026-02-15 21:51:05 +08:00
${task.description ? `<div class="markdown-preview" style="margin-top: 10px;">${marked.parse(task.description)}</div>` : ''}
<div style="margin-top: 8px; font-size: 0.85em; color: #666;">
创建时间: ${new Date(parseInt(task.created_at)).toLocaleString('zh-CN')} |
${task.assigned_to ? `执行者: ${task.assigned_to}` : '未分配'}
</div>
</div>
`;
}).join('');
} catch (error) {
console.error('加载任务列表失败:', error);
}
}
2026-02-15 21:51:05 +08:00
// 加载智能体列表
async function loadAgents() {
try {
const response = await fetch(`${API_BASE}/agents`);
const agents = await response.json();
const agentList = document.getElementById('agentList');
if (agents.length === 0) {
agentList.innerHTML = '<div class="empty-state">暂无智能体</div>';
return;
}
agentList.innerHTML = agents.map(agent => `
<div class="item-card">
<strong>${agent.name}</strong>
<span class="badge ${agent.status}">${agent.status === 'idle' ? '空闲' : '忙碌'}</span>
<div style="margin-top: 8px; font-size: 0.9em; color: #666;">
类型: ${agent.type} | 最后心跳: ${new Date(parseInt(agent.last_heartbeat)).toLocaleString('zh-CN')}
</div>
</div>
`).join('');
} catch (error) {
console.error('加载智能体列表失败:', error);
}
}
// 加载全局规约
async function loadGlobalRules() {
try {
const response = await fetch(`${API_BASE}/rules/global`);
const rules = await response.json();
const content = document.getElementById('globalRulesContent');
if (rules) {
content.innerHTML = `<div class="markdown-preview">${marked.parse(rules.content)}</div>`;
} else {
content.innerHTML = '<p style="color: #999;">未设置全局规约</p>';
}
} catch (error) {
console.error('加载全局规约失败:', error);
}
}
// 更新项目选择器
function updateProjectSelects() {
const selects = [document.getElementById('taskProject'), document.getElementById('filterProject')];
selects.forEach(select => {
if (!select) return;
const isFilter = select.id === 'filterProject';
select.innerHTML = (isFilter ? '<option value="">所有项目</option>' : '') +
projects.map(p => `<option value="${p.id}">${p.name}</option>`).join('');
});
}
// 模态框控制
function showCreateProjectModal() {
document.getElementById('createProjectModal').classList.add('active');
}
function showCreateTaskModal() {
updateProjectSelects();
document.getElementById('createTaskModal').classList.add('active');
}
function showEditGlobalRulesModal() {
fetch(`${API_BASE}/rules/global`)
.then(r => r.json())
.then(rules => {
document.getElementById('rulesEditor').value = rules?.content || '';
document.getElementById('editGlobalRulesModal').classList.add('active');
});
}
function closeModal(modalId) {
document.getElementById(modalId).classList.remove('active');
}
function editProjectRules(projectId) {
fetch(`${API_BASE}/projects/${projectId}/rules`)
.then(r => r.json())
.then(rules => {
const content = prompt('编辑项目规约(Markdown:', rules?.content || '');
if (content !== null) {
fetch(`${API_BASE}/projects/${projectId}/rules`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({content, created_by: 'human'})
}).then(() => {
alert('规约已更新');
selectProject(projectId);
});
}
});
}
function addProjectNote(projectId) {
const content = prompt('添加共享笔记(Markdown:');
if (content) {
fetch(`${API_BASE}/projects/${projectId}/notes`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({content, created_by: 'human'})
}).then(() => {
alert('笔记已添加');
selectProject(projectId);
});
}
}
// 表单提交
document.getElementById('createProjectForm').addEventListener('submit', async (e) => {
e.preventDefault();
2026-02-15 21:51:05 +08:00
const data = {
name: document.getElementById('projectName').value,
description: document.getElementById('projectDesc').value,
created_by: 'human'
};
try {
2026-02-15 21:51:05 +08:00
const response = await fetch(`${API_BASE}/projects`, {
method: 'POST',
2026-02-15 21:51:05 +08:00
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data)
});
if (response.ok) {
2026-02-15 21:51:05 +08:00
alert('✅ 项目创建成功!');
closeModal('createProjectModal');
document.getElementById('createProjectForm').reset();
loadProjects();
loadStats();
}
} catch (error) {
alert('❌ 创建失败: ' + error.message);
}
});
2026-02-15 21:51:05 +08:00
document.getElementById('createTaskForm').addEventListener('submit', async (e) => {
e.preventDefault();
const data = {
project_id: document.getElementById('taskProject').value,
title: document.getElementById('taskTitle').value,
description: document.getElementById('taskDesc').value,
task_type: document.getElementById('taskType').value,
priority: document.getElementById('taskPriority').value,
created_by: 'human'
};
try {
const response = await fetch(`${API_BASE}/tasks`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data)
});
if (response.ok) {
alert('✅ 任务创建成功!');
closeModal('createTaskModal');
document.getElementById('createTaskForm').reset();
loadTasks();
loadStats();
}
} catch (error) {
alert('❌ 创建失败: ' + error.message);
}
});
document.getElementById('editGlobalRulesForm').addEventListener('submit', async (e) => {
e.preventDefault();
const content = document.getElementById('rulesEditor').value;
try {
const response = await fetch(`${API_BASE}/rules/global`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({content, created_by: 'human'})
});
if (response.ok) {
alert('✅ 全局规约已更新!');
closeModal('editGlobalRulesModal');
loadGlobalRules();
}
} catch (error) {
alert('❌ 更新失败: ' + error.message);
}
});
// 初始加载
loadStats();
2026-02-15 21:51:05 +08:00
loadProjects();
2026-02-15 21:51:05 +08:00
// 自动刷新(每30秒)
setInterval(() => {
loadStats();
2026-02-15 21:51:05 +08:00
const activeTab = document.querySelector('.tab.active').textContent;
if (activeTab.includes('项目')) loadProjects();
if (activeTab.includes('任务')) loadTasks();
if (activeTab.includes('智能体')) loadAgents();
}, 30000);
</script>
</body>
</html>