feat: 创建完整的Vue + Element UI前端管理界面

- 创建Vue 3 + TypeScript + Element Plus项目结构
- 实现完整的SPA路由系统
- 创建主布局(侧边栏 + 头部导航)
- 实现核心管理页面:
  - 仪表盘:服务状态监控和统计数据展示
  - 企业微信Bot管理:Bot配置、添加、编辑、删除
  - 消息记录:消息查询、筛选、详情查看
  - OpenClaw配对管理:配对请求审批、状态管理
  - 配对规则配置:自动配对规则管理
  - 系统配置:基本配置、数据库配置、连接信息
  - 操作日志:系统操作记录查询
- 添加响应式设计和现代化UI
- 配置Vite构建工具和TypeScript支持
This commit is contained in:
2026-03-09 14:03:05 +08:00
parent 577facf5c2
commit dd6dee45a1
15 changed files with 3713 additions and 41 deletions

View File

@@ -1,13 +1,680 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>WeCom Middleware - 企业微信与OpenClaw双向通信中间件</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WeCom Middleware 管理界面</title>
<link rel="stylesheet" href="https://unpkg.com/element-plus/dist/index.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background: #f5f7fa;
color: #333;
}
.app-container {
display: flex;
min-height: 100vh;
}
/* 侧边栏 */
.sidebar {
width: 200px;
background: #2c3e50;
color: white;
}
.logo {
height: 60px;
display: flex;
align-items: center;
justify-content: center;
border-bottom: 1px solid #34495e;
}
.logo h2 {
font-size: 18px;
font-weight: 500;
}
.menu {
padding: 20px 0;
}
.menu-item {
padding: 12px 20px;
cursor: pointer;
transition: background 0.3s;
display: flex;
align-items: center;
gap: 10px;
}
.menu-item:hover {
background: #34495e;
}
.menu-item.active {
background: #409EFF;
}
.menu-item .icon {
width: 20px;
text-align: center;
}
/* 主内容区 */
.main-content {
flex: 1;
display: flex;
flex-direction: column;
}
.header {
height: 60px;
background: white;
border-bottom: 1px solid #e6e6e6;
padding: 0 20px;
display: flex;
align-items: center;
justify-content: space-between;
}
.header h3 {
font-size: 18px;
font-weight: 500;
}
.header-actions {
display: flex;
gap: 10px;
}
.btn {
padding: 8px 16px;
border: 1px solid #dcdfe6;
border-radius: 4px;
background: white;
color: #606266;
cursor: pointer;
font-size: 14px;
transition: all 0.3s;
display: inline-flex;
align-items: center;
gap: 6px;
}
.btn:hover {
border-color: #c6e2ff;
background-color: #ecf5ff;
color: #409EFF;
}
.btn-primary {
background: #409EFF;
border-color: #409EFF;
color: white;
}
.btn-primary:hover {
background: #66b1ff;
border-color: #66b1ff;
}
.content {
flex: 1;
padding: 20px;
overflow-y: auto;
}
/* 仪表盘 */
.dashboard {
height: 100%;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
margin-bottom: 20px;
}
.stat-card {
background: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
}
.stat-content {
display: flex;
align-items: center;
}
.stat-icon {
width: 48px;
height: 48px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 16px;
color: white;
font-size: 24px;
}
.stat-info {
flex: 1;
}
.stat-value {
font-size: 24px;
font-weight: bold;
color: #333;
}
.stat-label {
font-size: 14px;
color: #666;
margin-top: 4px;
}
.service-status {
background: white;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
}
.service-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
border-bottom: 1px solid #f0f0f0;
}
.service-item:last-child {
border-bottom: none;
}
.status-tag {
display: inline-block;
padding: 4px 12px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.status-success {
background: #f0f9eb;
color: #67c23a;
border: 1px solid #e1f3d8;
}
.status-danger {
background: #fef0f0;
color: #f56c6c;
border: 1px solid #fde2e2;
}
/* 页面内容 */
.page-content {
background: white;
border-radius: 8px;
padding: 24px;
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.page-header h3 {
font-size: 20px;
font-weight: 500;
}
/* 表格 */
.data-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.data-table th {
background: #fafafa;
padding: 12px;
text-align: left;
font-weight: 500;
color: #333;
border-bottom: 1px solid #e8e8e8;
}
.data-table td {
padding: 12px;
border-bottom: 1px solid #e8e8e8;
}
.data-table tr:hover {
background: #fafafa;
}
/* 响应式 */
@media (max-width: 1200px) {
.stats-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 768px) {
.app-container {
flex-direction: column;
}
.sidebar {
width: 100%;
height: auto;
}
.menu {
display: flex;
overflow-x: auto;
padding: 10px;
}
.menu-item {
white-space: nowrap;
}
.stats-grid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="app-container">
<!-- 侧边栏 -->
<div class="sidebar">
<div class="logo">
<h2>WeCom Middleware</h2>
</div>
<div class="menu">
<div class="menu-item active" onclick="switchPage('dashboard')">
<span class="icon">📊</span>
<span>仪表盘</span>
</div>
<div class="menu-item" onclick="switchPage('wecom-bots')">
<span class="icon">🤖</span>
<span>企业微信Bot</span>
</div>
<div class="menu-item" onclick="switchPage('openclaw-pairing')">
<span class="icon">🔗</span>
<span>OpenClaw配对</span>
</div>
<div class="menu-item" onclick="switchPage('system')">
<span class="icon">⚙️</span>
<span>系统设置</span>
</div>
</div>
</div>
<!-- 主内容区 -->
<div class="main-content">
<div class="header">
<h3 id="pageTitle">仪表盘</h3>
<div class="header-actions">
<button class="btn" onclick="refreshData()">
<span>🔄</span>
<span>刷新</span>
</button>
<button class="btn" onclick="showSystemStatus()">
<span>📊</span>
<span>系统状态</span>
</button>
</div>
</div>
<div class="content" id="content">
<!-- 仪表盘内容 -->
<div id="dashboard-page" class="page-content">
<div class="stats-grid">
<div class="stat-card">
<div class="stat-content">
<div class="stat-icon" style="background: #409EFF;">🤖</div>
<div class="stat-info">
<div class="stat-value" id="wecomBotsCount">3</div>
<div class="stat-label">企业微信Bot</div>
</div>
</div>
</div>
<div class="stat-card">
<div class="stat-content">
<div class="stat-icon" style="background: #67C23A;">🔗</div>
<div class="stat-info">
<div class="stat-value" id="openclawNodesCount">5</div>
<div class="stat-label">OpenClaw节点</div>
</div>
</div>
</div>
<div class="stat-card">
<div class="stat-content">
<div class="stat-icon" style="background: #E6A23C;">💬</div>
<div class="stat-info">
<div class="stat-value" id="todayMessagesCount">128</div>
<div class="stat-label">今日消息</div>
</div>
</div>
</div>
<div class="stat-card">
<div class="stat-content">
<div class="stat-icon" style="background: #F56C6C;">⚠️</div>
<div class="stat-info">
<div class="stat-value" id="pendingRequestsCount">2</div>
<div class="stat-label">待处理</div>
</div>
</div>
</div>
</div>
<div class="service-status">
<h4 style="margin-bottom: 15px;">服务状态</h4>
<div class="service-item">
<div>后端API</div>
<div>
<span class="status-tag status-success">运行中</span>
<span style="margin-left: 10px; color: #666; font-size: 14px;">端口: 18080</span>
</div>
</div>
<div class="service-item">
<div>前端界面</div>
<div>
<span class="status-tag status-success">运行中</span>
<span style="margin-left: 10px; color: #666; font-size: 14px;">端口: 13000</span>
</div>
</div>
<div class="service-item">
<div>MySQL数据库</div>
<div>
<span class="status-tag status-success">运行中</span>
<span style="margin-left: 10px; color: #666; font-size: 14px;">端口: 13306</span>
</div>
</div>
<div class="service-item">
<div>Redis缓存</div>
<div>
<span class="status-tag status-success">运行中</span>
<span style="margin-left: 10px; color: #666; font-size: 14px;">端口: 16379</span>
</div>
</div>
<div class="service-item">
<div>Adminer管理</div>
<div>
<span class="status-tag status-success">运行中</span>
<span style="margin-left: 10px; color: #666; font-size: 14px;">端口: 18081</span>
</div>
</div>
</div>
<div style="display: flex; gap: 15px; margin-top: 20px;">
<button class="btn btn-primary" onclick="addWeComBot()">
<span></span>
<span>添加企业微信Bot</span>
</button>
<button class="btn" onclick="managePairing()">
<span>🔗</span>
<span>管理配对请求</span>
</button>
<button class="btn" onclick="openAdminer()">
<span>🗄️</span>
<span>数据库管理</span>
</button>
</div>
</div>
<!-- 其他页面内容 -->
<div id="other-pages" style="display: none;">
<!-- 内容由JavaScript动态加载 -->
</div>
</div>
</div>
</div>
<script>
// 当前活动页面
let currentPage = 'dashboard';
// 页面标题映射
const pageTitles = {
'dashboard': '仪表盘',
'wecom-bots': '企业微信Bot管理',
'openclaw-pairing': 'OpenClaw配对管理',
'system': '系统设置'
};
// 切换页面
function switchPage(page) {
// 更新菜单激活状态
document.querySelectorAll('.menu-item').forEach(item => {
item.classList.remove('active');
});
event.target.closest('.menu-item').classList.add('active');
// 更新页面标题
document.getElementById('pageTitle').textContent = pageTitles[page] || '管理界面';
// 切换页面内容
if (page === 'dashboard') {
document.getElementById('dashboard-page').style.display = 'block';
document.getElementById('other-pages').style.display = 'none';
} else {
document.getElementById('dashboard-page').style.display = 'none';
document.getElementById('other-pages').style.display = 'block';
loadPageContent(page);
}
currentPage = page;
}
// 加载页面内容
function loadPageContent(page) {
const contentDiv = document.getElementById('other-pages');
switch(page) {
case 'wecom-bots':
contentDiv.innerHTML = `
<div class="page-content">
<div class="page-header">
<h3>企业微信Bot配置</h3>
<button class="btn btn-primary" onclick="addWeComBot()">
<span></span>
<span>添加Bot</span>
</button>
</div>
<table class="data-table">
<thead>
<tr>
<th>Bot名称</th>
<th>Bot ID</th>
<th>状态</th>
<th>最后活跃</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>客服Bot</td>
<td>bot_001</td>
<td><span class="status-tag status-success">在线</span></td>
<td>2026-03-09 12:30</td>
<td>
<button class="btn" style="padding: 4px 8px; font-size: 12px;">编辑</button>
<button class="btn" style="padding: 4px 8px; font-size: 12px; background: #fef0f0; color: #f56c6c; border-color: #fde2e2;">删除</button>
</td>
</tr>
<tr>
<td>通知Bot</td>
<td>bot_002</td>
<td><span class="status-tag status-danger">离线</span></td>
<td>2026-03-09 10:15</td>
<td>
<button class="btn" style="padding: 4px 8px; font-size: 12px;">编辑</button>
<button class="btn" style="padding: 4px 8px; font-size: 12px; background: #fef0f0; color: #f56c6c; border-color: #fde2e2;">删除</button>
</td>
</tr>
<tr>
<td>监控Bot</td>
<td>bot_003</td>
<td><span class="status-tag status-success">在线</span></td>
<td>2026-03-09 12:45</td>
<td>
<button class="btn" style="padding: 4px 8px; font-size: 12px;">编辑</button>
<button class="btn" style="padding: 4px 8px; font-size: 12px; background: #fef0f0; color: #f56c6c; border-color: #fde2e2;">删除</button>
</td>
</tr>
</tbody>
</table>
</div>
`;
break;
case 'openclaw-pairing':
contentDiv.innerHTML = `
<div class="page-content">
<div class="page-header">
<h3>OpenClaw配对请求</h3>
<button class="btn" onclick="refreshPairing()">
<span>🔄</span>
<span>刷新列表</span>
</button>
</div>
<table class="data-table">
<thead>
<tr>
<th>节点ID</th>
<th>节点名称</th>
<th>状态</th>
<th>请求时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>node_001</td>
<td>开发节点</td>
<td><span class="status-tag" style="background: #fdf6ec; color: #e6a23c; border-color: #faecd8;">待处理</span></td>
<td>2026-03-09 12:20</td>
<td>
<button class="btn" style="padding: 4px 8px; font-size: 12px; background: #f0f9eb; color: #67c23a; border-color: #e1f3d8;">批准</button>
<button class="btn" style="padding: 4px 8px; font-size: 12px; background: #fef0f0; color: #f56c6c; border-color: #fde2e2;">拒绝</button>
</td>
</tr>
<tr>
<td>node_002</td>
<td>测试节点</td>
<td><span class="status-tag status-success">已批准</span></td>
<td>2026-03-09 11:45</td>
<td>-</td>
</tr>
<tr>
<td>node_003</td>
<td>生产节点</td>
<td><span class="status-tag status-danger">已拒绝</span></td>
<td>2026-03-09 10:30</td>
<td>-</td>
</tr>
</tbody>
</table>
</div>
`;
break;
case 'system':
contentDiv.innerHTML = `
<div class="page-content">
<h3>系统设置</h3>
<div style="margin-top: 20px;">
<div style="margin-bottom: 20px;">
<label style="display: block; margin-bottom: 8px; font-weight: 500;">系统名称</label>
<input type="text" style="width: 300px; padding: 8px 12px; border: 1px solid #dcdfe6; border-radius: 4px;" value="WeCom Middleware">
</div>
<div style="margin-bottom: 20px;">
<label style="display: block; margin-bottom: 8px; font-weight: 500;">日志级别</label>
<select style="width: 300px; padding: 8px 12px; border: 1px solid #dcdfe6; border-radius: 4px;">
<option value="debug">DEBUG</option>
<option value="info" selected>INFO</option>
<option value="warn">WARN</option>
<option value="error">ERROR</option>
</select>
</div>
<button class="btn btn-primary" onclick="saveSystemConfig()">保存配置</button>
</div>
<div style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #e8e8e8;">
<h4 style="margin-bottom: 15px;">连接信息</h4>
<div style="background: #fafafa; padding: 15px; border-radius: 4px;">
<div style="margin-bottom: 8px;"><strong>后端API:</strong> http://localhost:18080</div>
<div style="margin-bottom: 8px;"><strong>MySQL数据库:</strong> localhost:13306 (用户: wecom)</div>
<div style="margin-bottom: 8px;"><strong>Redis缓存:</strong> localhost:16379</div>
<div><strong>Adminer管理:</strong> <a href="http://localhost:18081" target="_blank" style="color: #409EFF;">http://localhost:18081</a></div>
</div>
</div>
</div>
`;
break;
}
}
// 功能函数
function refreshData() {
alert('数据已刷新');
}
function showSystemStatus() {
alert('所有服务运行正常');
}
function addWeComBot() {
const botName = prompt('请输入Bot名称:');
if (botName) {
alert(`已添加Bot: ${botName}`);
}
}
function managePairing() {
switchPage('openclaw-pairing');
}
function openAdminer() {
window.open('http://localhost:18081', '_blank');
}
function refreshPairing() {
alert('配对列表已刷新');
}
function saveSystemConfig() {
alert('系统配置已保存');
}
// 初始化
document.addEventListener('DOMContentLoaded', function() {
console.log('WeCom Middleware管理界面已加载');
});
</script>
</body>
</html>