feat: v3.1 - 统一规约视图、项目树导航、移动端支持
主要更新: - 统一规约管理视图(全局/项目规约共享表格) - 项目树形导航(看板/项目规约子菜单) - 智能高亮与状态持久化 - 移动端响应式支持(汉堡菜单) - 规约类型更新为7个专业分类 - 文件清理(85→15文件,减少82%) 修复问题: - 修复创建项目功能(JavaScript语法错误) - 修复协作规约错误高亮 - 修复看板需要点击2次才高亮 - 修复项目树自动收起问题 新增工具: - debug.html(项目创建测试) - test.html(移动端测试) - status.html(系统诊断) 技术改进: - 上下文状态管理(currentRuleContext) - 展开状态持久化(projectExpandedState) - 触摸事件支持 - 移除console.log提升兼容性
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>调试 - 新建项目</title>
|
||||
<style>
|
||||
body { padding: 20px; font-family: Arial; }
|
||||
.log { background: #f5f5f5; padding: 10px; margin: 10px 0; border-radius: 5px; }
|
||||
button { padding: 10px 20px; background: #667eea; color: white; border: none; border-radius: 5px; cursor: pointer; margin: 5px; }
|
||||
input, textarea { width: 100%; padding: 8px; margin: 5px 0; border: 1px solid #ddd; border-radius: 4px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>新建项目调试页面</h1>
|
||||
|
||||
<div>
|
||||
<h3>测试1: 按钮点击</h3>
|
||||
<button id="test-btn">点击测试</button>
|
||||
<div id="log1" class="log">等待点击...</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3>测试2: 创建项目</h3>
|
||||
<input type="text" id="project-name" placeholder="项目名称">
|
||||
<textarea id="project-desc" placeholder="项目描述" rows="3"></textarea>
|
||||
<button id="create-btn">创建项目</button>
|
||||
<div id="log2" class="log">等待创建...</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3>测试3: 项目列表</h3>
|
||||
<button id="list-btn">加载项目</button>
|
||||
<div id="log3" class="log">等待加载...</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const log = (id, msg) => {
|
||||
document.getElementById(id).innerHTML += `<div>${new Date().toLocaleTimeString()}: ${msg}</div>`;
|
||||
};
|
||||
|
||||
// 测试1
|
||||
document.getElementById('test-btn').addEventListener('click', () => {
|
||||
log('log1', '✅ 按钮点击事件正常');
|
||||
});
|
||||
|
||||
// 测试2
|
||||
document.getElementById('create-btn').addEventListener('click', async () => {
|
||||
const name = document.getElementById('project-name').value;
|
||||
const desc = document.getElementById('project-desc').value;
|
||||
|
||||
if (!name) {
|
||||
log('log2', '❌ 请输入项目名称');
|
||||
return;
|
||||
}
|
||||
|
||||
log('log2', '发送请求...');
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/projects', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name, description: desc, created_by: 'debug' })
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
log('log2', `✅ 创建成功: ${JSON.stringify(data)}`);
|
||||
} catch (error) {
|
||||
log('log2', `❌ 错误: ${error.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
// 测试3
|
||||
document.getElementById('list-btn').addEventListener('click', async () => {
|
||||
log('log3', '加载中...');
|
||||
try {
|
||||
const response = await fetch('/api/projects');
|
||||
const data = await response.json();
|
||||
log('log3', `✅ 获取到 ${data.length} 个项目`);
|
||||
data.forEach(p => log('log3', `- ${p.name}`));
|
||||
} catch (error) {
|
||||
log('log3', `❌ 错误: ${error.message}`);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,459 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>多智能体任务管理系统</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 {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
header {
|
||||
text-align: center;
|
||||
color: white;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5em;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 2fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.panel {
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.panel h2 {
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid #667eea;
|
||||
}
|
||||
|
||||
.agent-item, .task-item {
|
||||
padding: 15px;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 8px;
|
||||
background: #f8f9fa;
|
||||
border-left: 4px solid #667eea;
|
||||
}
|
||||
|
||||
.agent-item.busy {
|
||||
border-left-color: #f39c12;
|
||||
background: #fff9e6;
|
||||
}
|
||||
|
||||
.agent-item.offline {
|
||||
border-left-color: #95a5a6;
|
||||
background: #ecf0f1;
|
||||
}
|
||||
|
||||
.task-item.pending {
|
||||
border-left-color: #3498db;
|
||||
}
|
||||
|
||||
.task-item.in_progress {
|
||||
border-left-color: #f39c12;
|
||||
background: #fff9e6;
|
||||
}
|
||||
|
||||
.task-item.completed {
|
||||
border-left-color: #27ae60;
|
||||
background: #e8f8f5;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.85em;
|
||||
font-weight: bold;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.badge.idle {
|
||||
background: #3498db;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.badge.busy {
|
||||
background: #f39c12;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.badge.pending {
|
||||
background: #3498db;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.badge.in_progress {
|
||||
background: #f39c12;
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.form-group input, .form-group textarea, .form-group select {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.form-group textarea {
|
||||
resize: vertical;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
.task-meta {
|
||||
font-size: 0.85em;
|
||||
color: #666;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
#createTaskForm {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
float: right;
|
||||
padding: 5px 15px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>🤖 多智能体任务管理系统</h1>
|
||||
<p>Multi-Agent Task Management System</p>
|
||||
</header>
|
||||
|
||||
<div class="stats" id="stats">
|
||||
<!-- 统计数据将动态加载 -->
|
||||
</div>
|
||||
|
||||
<div class="main-content">
|
||||
<div class="panel">
|
||||
<h2>
|
||||
智能体列表
|
||||
<button class="refresh-btn" onclick="loadAgents()">刷新</button>
|
||||
</h2>
|
||||
<div id="agentList">
|
||||
<div class="empty-state">加载中...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<h2>
|
||||
任务列表
|
||||
<button class="refresh-btn" onclick="loadTasks()">刷新</button>
|
||||
</h2>
|
||||
|
||||
<div id="taskList">
|
||||
<div class="empty-state">加载中...</div>
|
||||
</div>
|
||||
|
||||
<form id="createTaskForm">
|
||||
<h3 style="margin-top: 30px; margin-bottom: 15px;">创建新任务</h3>
|
||||
<div class="form-group">
|
||||
<label>任务标题 *</label>
|
||||
<input type="text" id="taskTitle" required placeholder="输入任务标题">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>任务描述</label>
|
||||
<textarea id="taskDescription" placeholder="详细描述任务内容"></textarea>
|
||||
</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>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const API_BASE = '';
|
||||
|
||||
// 加载统计数据
|
||||
async function loadStats() {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/api/stats`);
|
||||
const stats = await response.json();
|
||||
|
||||
document.getElementById('stats').innerHTML = `
|
||||
<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">
|
||||
<h3>待处理任务</h3>
|
||||
<div class="number">${stats.pending_tasks || 0}</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3>进行中任务</h3>
|
||||
<div class="number">${stats.in_progress_tasks || 0}</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3>已完成任务</h3>
|
||||
<div class="number">${stats.completed_tasks || 0}</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3>任务总数</h3>
|
||||
<div class="number">${stats.total_tasks || 0}</div>
|
||||
</div>
|
||||
`;
|
||||
} catch (error) {
|
||||
console.error('加载统计数据失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 加载智能体列表
|
||||
async function loadAgents() {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/api/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 => {
|
||||
const statusClass = agent.status === 'busy' ? 'busy' : 'idle';
|
||||
const lastHeartbeat = new Date(agent.last_heartbeat).toLocaleString('zh-CN');
|
||||
|
||||
return `
|
||||
<div class="agent-item ${statusClass}">
|
||||
<strong>${agent.name}</strong>
|
||||
<span class="badge ${agent.status}">${agent.status === 'idle' ? '空闲' : '忙碌'}</span>
|
||||
<div class="task-meta">
|
||||
类型: ${agent.type || '通用'} |
|
||||
注册时间: ${new Date(agent.registered_at).toLocaleString('zh-CN')} |
|
||||
最后心跳: ${lastHeartbeat}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
} catch (error) {
|
||||
console.error('加载智能体列表失败:', error);
|
||||
document.getElementById('agentList').innerHTML =
|
||||
'<div class="empty-state">加载失败</div>';
|
||||
}
|
||||
}
|
||||
|
||||
// 加载任务列表
|
||||
async function loadTasks() {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/api/tasks`);
|
||||
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 = {
|
||||
'pending': '待处理',
|
||||
'in_progress': '进行中',
|
||||
'completed': '已完成'
|
||||
}[task.status] || task.status;
|
||||
|
||||
const priorityText = {
|
||||
'low': '低',
|
||||
'normal': '普通',
|
||||
'high': '高'
|
||||
}[task.priority] || task.priority;
|
||||
|
||||
return `
|
||||
<div class="task-item ${task.status}">
|
||||
<strong>${task.title}</strong>
|
||||
<span class="badge ${task.status}">${statusText}</span>
|
||||
<span class="badge ${task.priority}">${priorityText}</span>
|
||||
${task.description ? `<div style="margin-top: 8px; color: #666;">${task.description}</div>` : ''}
|
||||
<div class="task-meta">
|
||||
创建时间: ${new Date(task.created_at).toLocaleString('zh-CN')} |
|
||||
${task.assigned_to ? `执行者: ${task.assigned_to}` : '未分配'} |
|
||||
${task.completed_at ? `完成时间: ${new Date(task.completed_at).toLocaleString('zh-CN')}` : ''}
|
||||
</div>
|
||||
${task.result ? `<div style="margin-top: 8px; padding: 8px; background: #e8f4f8; border-radius: 4px;"><strong>结果:</strong> ${task.result}</div>` : ''}
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
} catch (error) {
|
||||
console.error('加载任务列表失败:', error);
|
||||
document.getElementById('taskList').innerHTML =
|
||||
'<div class="empty-state">加载失败</div>';
|
||||
}
|
||||
}
|
||||
|
||||
// 创建任务表单提交
|
||||
document.getElementById('createTaskForm').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const title = document.getElementById('taskTitle').value;
|
||||
const description = document.getElementById('taskDescription').value;
|
||||
const priority = document.getElementById('taskPriority').value;
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/api/tasks`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
title,
|
||||
description,
|
||||
priority,
|
||||
created_by: 'human'
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
alert('✅ 任务创建成功!');
|
||||
document.getElementById('createTaskForm').reset();
|
||||
loadTasks();
|
||||
loadStats();
|
||||
} else {
|
||||
alert('❌ 创建失败: ' + result.error);
|
||||
}
|
||||
} catch (error) {
|
||||
alert('❌ 创建失败: ' + error.message);
|
||||
}
|
||||
});
|
||||
|
||||
// 初始加载
|
||||
loadStats();
|
||||
loadAgents();
|
||||
loadTasks();
|
||||
|
||||
// 自动刷新(每10秒)
|
||||
setInterval(() => {
|
||||
loadStats();
|
||||
loadAgents();
|
||||
loadTasks();
|
||||
}, 10000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+3022
-788
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,128 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>系统状态</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 800px;
|
||||
margin: 50px auto;
|
||||
padding: 20px;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.status-card {
|
||||
background: white;
|
||||
padding: 30px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.status-ok { color: #4caf50; }
|
||||
.status-error { color: #f44336; }
|
||||
h1 { color: #667eea; }
|
||||
.info { margin: 10px 0; padding: 10px; background: #f9f9f9; border-radius: 5px; }
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
background: #667eea;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
}
|
||||
button:hover { opacity: 0.9; }
|
||||
#log { font-family: monospace; font-size: 12px; white-space: pre-wrap; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="status-card">
|
||||
<h1>🚀 系统状态检查</h1>
|
||||
|
||||
<div class="info">
|
||||
<strong>服务器:</strong> <span id="server-status" class="status-ok">✅ 运行中</span><br>
|
||||
<strong>时间:</strong> <span id="current-time"></span><br>
|
||||
<strong>浏览器:</strong> <span id="browser"></span>
|
||||
</div>
|
||||
|
||||
<h3>功能测试</h3>
|
||||
<button onclick="testAPI()">测试 API</button>
|
||||
<button onclick="testProjects()">测试项目列表</button>
|
||||
<button onclick="testRules()">测试规约</button>
|
||||
<button onclick="clearCache()">清除缓存</button>
|
||||
<button onclick="location.href='/'">返回主页</button>
|
||||
|
||||
<div id="log" class="info" style="margin-top: 20px; max-height: 300px; overflow-y: auto;">
|
||||
等待测试...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const log = (msg, isError = false) => {
|
||||
const logDiv = document.getElementById('log');
|
||||
const time = new Date().toLocaleTimeString();
|
||||
const prefix = isError ? '❌' : '✅';
|
||||
logDiv.innerHTML += `\n${time} ${prefix} ${msg}`;
|
||||
logDiv.scrollTop = logDiv.scrollHeight;
|
||||
};
|
||||
|
||||
document.getElementById('current-time').textContent = new Date().toLocaleString('zh-CN');
|
||||
document.getElementById('browser').textContent = navigator.userAgent.split(' ').pop();
|
||||
|
||||
async function testAPI() {
|
||||
log('测试 /api/stats ...');
|
||||
try {
|
||||
const res = await fetch('/api/stats');
|
||||
const data = await res.json();
|
||||
log(`API 正常 - 项目:${data.projects} 任务:${data.tasks}`);
|
||||
} catch (e) {
|
||||
log('API 错误: ' + e.message, true);
|
||||
}
|
||||
}
|
||||
|
||||
async function testProjects() {
|
||||
log('测试 /api/projects ...');
|
||||
try {
|
||||
const res = await fetch('/api/projects');
|
||||
const data = await res.json();
|
||||
log(`获取到 ${data.length} 个项目`);
|
||||
data.forEach(p => log(` - ${p.name}`));
|
||||
} catch (e) {
|
||||
log('项目列表错误: ' + e.message, true);
|
||||
}
|
||||
}
|
||||
|
||||
async function testRules() {
|
||||
log('测试 /api/rules/global ...');
|
||||
try {
|
||||
const res = await fetch('/api/rules/global');
|
||||
const data = await res.json();
|
||||
log(`获取到 ${data.length} 条全局规约`);
|
||||
} catch (e) {
|
||||
log('规约错误: ' + e.message, true);
|
||||
}
|
||||
}
|
||||
|
||||
function clearCache() {
|
||||
log('清除缓存...');
|
||||
if ('caches' in window) {
|
||||
caches.keys().then(names => {
|
||||
names.forEach(name => caches.delete(name));
|
||||
});
|
||||
}
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
log('缓存已清除,请刷新页面');
|
||||
}
|
||||
|
||||
// 自动运行基础测试
|
||||
window.addEventListener('load', () => {
|
||||
setTimeout(() => {
|
||||
log('=== 自动测试开始 ===');
|
||||
testAPI();
|
||||
}, 500);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,88 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>测试页面</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
padding: 20px;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.test-box {
|
||||
background: white;
|
||||
padding: 30px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
max-width: 600px;
|
||||
margin: 50px auto;
|
||||
text-align: center;
|
||||
}
|
||||
h1 {
|
||||
color: #667eea;
|
||||
}
|
||||
.status {
|
||||
margin: 20px 0;
|
||||
padding: 15px;
|
||||
background: #e7f5e7;
|
||||
border-radius: 5px;
|
||||
color: #2d6a2d;
|
||||
}
|
||||
button {
|
||||
padding: 12px 24px;
|
||||
background: #667eea;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
}
|
||||
button:active {
|
||||
background: #5568d3;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="test-box">
|
||||
<h1>✅ 服务器正常运行</h1>
|
||||
<div class="status">
|
||||
<strong>状态:</strong> 连接成功<br>
|
||||
<strong>时间:</strong> <span id="time"></span><br>
|
||||
<strong>User-Agent:</strong> <span id="ua"></span>
|
||||
</div>
|
||||
<button onclick="testAPI()">测试 API</button>
|
||||
<button onclick="location.href='/'">返回主页</button>
|
||||
<div id="result" style="margin-top: 20px;"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('time').textContent = new Date().toLocaleString('zh-CN');
|
||||
document.getElementById('ua').textContent = navigator.userAgent;
|
||||
|
||||
async function testAPI() {
|
||||
const result = document.getElementById('result');
|
||||
result.innerHTML = '正在测试...';
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/stats');
|
||||
const data = await response.json();
|
||||
result.innerHTML = `
|
||||
<div style="background: #e7f5e7; padding: 15px; border-radius: 5px; margin-top: 10px;">
|
||||
✅ API 正常<br>
|
||||
项目数: ${data.projects || 0}<br>
|
||||
任务数: ${data.tasks || 0}
|
||||
</div>
|
||||
`;
|
||||
} catch (error) {
|
||||
result.innerHTML = `
|
||||
<div style="background: #ffe7e7; padding: 15px; border-radius: 5px; margin-top: 10px; color: #d32f2f;">
|
||||
❌ API 错误: ${error.message}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user