Files
gitea-jira-task-bot/public/dashboard.html
2026-01-29 15:38:49 +08:00

297 lines
21 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TaskBot控制台</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<style>
body { font-family: 'Inter', system-ui, sans-serif; }
.log-line { font-family: 'Monaco', 'Courier New', monospace; }
.status-pulse { animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; }
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: .5; }
}
.markdown-body { line-height: 1.6; }
.markdown-body h1 { font-size: 2em; font-weight: bold; margin-top: 1em; margin-bottom: 0.5em; }
.markdown-body h2 { font-size: 1.5em; font-weight: bold; margin-top: 1em; margin-bottom: 0.5em; }
.markdown-body h3 { font-size: 1.25em; font-weight: bold; margin-top: 0.8em; margin-bottom: 0.4em; }
.markdown-body p { margin-bottom: 1em; }
.markdown-body ul, .markdown-body ol { margin-left: 2em; margin-bottom: 1em; }
.markdown-body li { margin-bottom: 0.5em; }
.markdown-body code { background: #f3f4f6; padding: 0.2em 0.4em; border-radius: 3px; font-family: monospace; }
.markdown-body pre { background: #1e293b; color: #e2e8f0; padding: 1em; border-radius: 0.5em; overflow-x: auto; margin-bottom: 1em; }
.markdown-body pre code { background: transparent; padding: 0; }
.markdown-body a { color: #3b82f6; text-decoration: underline; }
.markdown-body blockquote { border-left: 4px solid #e5e7eb; padding-left: 1em; color: #6b7280; margin-bottom: 1em; }
</style>
</head>
<body class="min-h-screen bg-slate-50 text-slate-900">
<div class="flex">
<!-- 侧边栏 -->
<div class="w-64 bg-slate-900 text-slate-300 flex flex-col h-screen fixed left-0 top-0 border-r border-slate-800">
<div class="h-16 flex items-center px-6 border-b border-slate-800 bg-slate-950">
<svg class="w-6 h-6 text-indigo-500 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
</svg>
<span class="font-bold text-white tracking-tight">TaskBot控制台</span>
</div>
<nav class="flex-1 py-6 px-3 space-y-1">
<button onclick="switchTab('dashboard')" id="tab-dashboard" class="tab-btn w-full flex items-center px-3 py-2.5 text-sm font-medium rounded-md transition-colors duration-150 group">
<svg class="w-4 h-4 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
</svg>
运维概览
</button>
<button onclick="switchTab('logs')" id="tab-logs" class="tab-btn w-full flex items-center px-3 py-2.5 text-sm font-medium rounded-md transition-colors duration-150 group">
<svg class="w-4 h-4 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
运行日志
</button>
<button onclick="switchTab('mapping')" id="tab-mapping" class="tab-btn w-full flex items-center px-3 py-2.5 text-sm font-medium rounded-md transition-colors duration-150 group">
<svg class="w-4 h-4 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
</svg>
映射配置
</button>
<button onclick="switchTab('settings')" id="tab-settings" class="tab-btn w-full flex items-center px-3 py-2.5 text-sm font-medium rounded-md transition-colors duration-150 group">
<svg class="w-4 h-4 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
</svg>
系统设置
</button>
<button onclick="switchTab('guide')" id="tab-guide" class="tab-btn w-full flex items-center px-3 py-2.5 text-sm font-medium rounded-md transition-colors duration-150 group">
<svg class="w-4 h-4 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"></path>
</svg>
使用指南
</button>
</nav>
<div class="p-4 border-t border-slate-800">
<div class="flex items-center">
<div class="w-2 h-2 rounded-full bg-emerald-500 mr-2 status-pulse"></div>
<span class="text-xs font-mono text-slate-500">v1.1.0</span>
</div>
</div>
</div>
<!-- 主内容区 -->
<main class="flex-1 ml-64 p-8 overflow-y-auto">
<!-- Dashboard 标签页 -->
<div id="content-dashboard" class="tab-content">
<header class="mb-8 flex justify-between items-end">
<div>
<h1 class="text-2xl font-bold text-slate-900 tracking-tight">运维概览</h1>
<p class="text-sm text-slate-500 mt-1">Jira-Gitea双向同步机器人控制中心</p>
</div>
<div class="flex items-center space-x-2">
<span class="w-2 h-2 rounded-full bg-emerald-500"></span>
<span class="text-sm font-medium text-slate-600" id="uptime">加载中...</span>
</div>
</header>
<div class="space-y-6">
<!-- 指标栏 -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-6">
<div class="bg-white p-5 rounded-lg border border-slate-200 shadow-sm">
<p class="text-sm font-medium text-slate-500">今日同步工单</p>
<p class="mt-1 text-2xl font-bold text-slate-900 tracking-tight" id="today-syncs">--</p>
<p class="mt-1 text-xs text-slate-400">实时统计</p>
</div>
<div class="bg-white p-5 rounded-lg border border-slate-200 shadow-sm">
<p class="text-sm font-medium text-slate-500">配置的仓库</p>
<p class="mt-1 text-2xl font-bold text-slate-900 tracking-tight" id="repo-count">--</p>
<p class="mt-1 text-xs text-slate-400">mappings.json</p>
</div>
<div class="bg-white p-5 rounded-lg border border-slate-200 shadow-sm">
<p class="text-sm font-medium text-slate-500">今日错误</p>
<p class="mt-1 text-2xl font-bold text-rose-600 tracking-tight" id="error-count">--</p>
<p class="mt-1 text-xs text-slate-400">ERROR + FATAL</p>
</div>
<div class="bg-white p-5 rounded-lg border border-slate-200 shadow-sm">
<p class="text-sm font-medium text-slate-500">服务状态</p>
<p class="mt-1 text-2xl font-bold text-slate-900 tracking-tight" id="service-status">运行中</p>
<p class="mt-1 text-xs text-slate-400">实时监控</p>
</div>
</div>
<!-- 历史记录 -->
<div class="bg-white rounded-lg border border-slate-200 shadow-sm p-6">
<h3 class="text-sm font-semibold text-slate-900 uppercase tracking-wider mb-4">近7日同步历史</h3>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-slate-200">
<thead>
<tr>
<th class="px-4 py-2 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">日期</th>
<th class="px-4 py-2 text-right text-xs font-medium text-slate-500 uppercase tracking-wider">同步数</th>
<th class="px-4 py-2 text-right text-xs font-medium text-slate-500 uppercase tracking-wider">错误数</th>
<th class="px-4 py-2 text-right text-xs font-medium text-slate-500 uppercase tracking-wider">严重错误</th>
</tr>
</thead>
<tbody id="history-table" class="divide-y divide-slate-200">
<tr>
<td colspan="4" class="px-4 py-8 text-center text-sm text-slate-500">加载中...</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- 控制区 -->
<div class="bg-white rounded-lg border border-slate-200 shadow-sm p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-sm font-semibold text-slate-900 uppercase tracking-wider">服务控制</h3>
<span id="status-badge" class="px-3 py-1 rounded-full text-xs font-medium flex items-center border bg-emerald-50 text-emerald-600 border-emerald-200">
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
运行中
</span>
</div>
<div class="grid grid-cols-3 gap-3">
<button onclick="controlBot('restart')" class="flex items-center justify-center px-4 py-2 border border-slate-300 text-sm font-medium rounded-md text-slate-700 bg-white hover:bg-slate-50 transition-colors">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
</svg>
重启服务
</button>
<button onclick="clearLogs()" class="flex items-center justify-center px-4 py-2 border border-slate-300 text-sm font-medium rounded-md text-slate-700 bg-white hover:bg-slate-50 transition-colors">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
</svg>
清空日志
</button>
<button onclick="refreshStatus()" class="flex items-center justify-center px-4 py-2 border border-slate-300 text-sm font-medium rounded-md text-slate-700 bg-white hover:bg-slate-50 transition-colors">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
</svg>
刷新状态
</button>
</div>
</div>
</div>
</div>
<!-- Logs 标签页 -->
<div id="content-logs" class="tab-content hidden">
<header class="mb-6">
<h1 class="text-2xl font-bold text-slate-900 tracking-tight">运行日志</h1>
<p class="text-sm text-slate-500 mt-1">实时监控系统运行状态</p>
</header>
<div class="bg-slate-900 rounded-lg shadow-sm border border-slate-800 overflow-hidden" style="height: calc(100vh - 200px);">
<div class="bg-slate-950 px-4 py-2 border-b border-slate-800 flex justify-between items-center">
<div class="flex items-center space-x-2">
<svg class="w-4 h-4 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
</svg>
<span class="text-xs font-mono text-slate-300" id="log-filename">加载中...</span>
</div>
<div class="flex items-center space-x-2">
<span class="w-2 h-2 rounded-full bg-emerald-500 status-pulse"></span>
<span class="text-xs text-slate-500">实时监测中</span>
</div>
</div>
<div id="log-viewer" class="overflow-y-auto p-4 font-mono text-xs text-slate-300 space-y-1" style="height: calc(100% - 44px);">
<div class="text-slate-500 text-center py-8">加载日志中...</div>
</div>
</div>
</div>
<!-- Mapping 标签页 - 嵌入原有的映射编辑器 -->
<div id="content-mapping" class="tab-content hidden">
<iframe src="/editor/error.html?code=施工中" class="w-full border-0 rounded-lg shadow-sm bg-white" style="height: calc(100vh - 100px);"></iframe>
</div>
<!-- Settings 标签页 -->
<div id="content-settings" class="tab-content hidden">
<header class="mb-6">
<h1 class="text-2xl font-bold text-slate-900 tracking-tight">系统设置</h1>
<p class="text-sm text-slate-500 mt-1">编辑环境变量配置文件 (.env)</p>
</header>
<div class="bg-white rounded-lg border border-slate-200 shadow-sm p-6">
<div class="mb-6 flex justify-between items-center border-b border-slate-200 pb-4">
<div>
<h3 class="text-sm font-semibold text-slate-900">环境变量配置</h3>
<p class="text-xs text-slate-500 mt-1">修改后需要重启服务才能生效</p>
</div>
<div class="flex gap-2">
<button onclick="loadEnvFile()" class="text-slate-600 hover:text-slate-900 px-3 py-2 rounded text-sm font-medium transition-colors border border-slate-300 hover:bg-slate-50">
<svg class="w-4 h-4 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
</svg>
重新加载
</button>
<button onclick="saveEnvFile()" id="saveEnvBtn" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded text-sm font-medium transition-colors">
保存配置
</button>
</div>
</div>
<div id="envEditor" class="space-y-3" style="max-height: 500px; overflow-y: auto;">
<div class="text-center py-8 text-slate-400">
<svg class="w-8 h-8 mx-auto mb-2 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
</svg>
<p class="text-sm">加载中...</p>
</div>
</div>
<div class="mt-4 bg-amber-50 border border-amber-200 rounded p-3 text-sm text-amber-800">
<div class="flex items-start">
<svg class="w-5 h-5 mr-2 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
</svg>
<div>
<p class="font-medium">注意事项</p>
<ul class="mt-1 text-xs space-y-1">
<li>• 修改前会自动备份为 .env.backup</li>
<li>• 保存后需要手动重启服务才能生效</li>
<li>• 请勿泄露敏感信息API Token、密码等</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<!-- Guide 标签页 -->
<div id="content-guide" class="tab-content hidden">
<header class="mb-6">
<h1 class="text-2xl font-bold text-slate-900 tracking-tight">使用指南</h1>
<p class="text-sm text-slate-500 mt-1">项目文档与配置说明</p>
</header>
<div class="bg-white rounded-lg border border-slate-200 shadow-sm p-6">
<div id="guide-content" class="markdown-body prose max-w-none text-slate-700">
<div class="text-center py-8 text-slate-500">
<svg class="w-8 h-8 mx-auto mb-2 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
</svg>
<p>加载中...</p>
</div>
</div>
</div>
</div>
</main>
</div>
<script src="/editor/dashboard-app.js"></script>
<script>
//页面加载完成后初始化
document.addEventListener('DOMContentLoaded', () => {
//默认显示运维概览
switchTab('dashboard');
});
</script>
</body>
</html>