322 lines
13 KiB
HTML
322 lines
13 KiB
HTML
<div class="space-y-6">
|
||
<!-- 页面标题和操作栏 -->
|
||
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
|
||
<h1 class="text-2xl font-semibold text-gray-800">机器人管理</h1>
|
||
<a href="/admin/robots/new" class="inline-flex items-center justify-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-gray-800 hover:bg-gray-700 transition-colors">
|
||
<i class="fas fa-plus mr-2"></i> 创建机器人
|
||
</a>
|
||
</div>
|
||
|
||
<!-- 搜索过滤栏 -->
|
||
<div class="clean-card p-4">
|
||
<div class="flex flex-col md:flex-row md:items-center gap-4">
|
||
<div class="flex-1">
|
||
<div class="relative rounded-md">
|
||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||
<i class="fas fa-search text-gray-400"></i>
|
||
</div>
|
||
<input id="robot-search" type="text" placeholder="搜索机器人..." class="block w-full pl-10 pr-3 py-2 border border-gray-200 rounded-md focus:ring-1 focus:ring-gray-200 focus:border-gray-300">
|
||
</div>
|
||
</div>
|
||
<div class="flex flex-wrap gap-2">
|
||
<button data-filter="all" class="filter-btn px-4 py-2 rounded-md text-sm border bg-gray-800 text-white">
|
||
全部
|
||
</button>
|
||
<button data-filter="online" class="filter-btn px-4 py-2 rounded-md text-sm border border-gray-200 bg-white text-gray-700 hover:bg-gray-50">
|
||
在线
|
||
</button>
|
||
<button data-filter="offline" class="filter-btn px-4 py-2 rounded-md text-sm border border-gray-200 bg-white text-gray-700 hover:bg-gray-50">
|
||
离线
|
||
</button>
|
||
<button data-filter="error" class="filter-btn px-4 py-2 rounded-md text-sm border border-gray-200 bg-white text-gray-700 hover:bg-gray-50">
|
||
错误
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 统计信息 -->
|
||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||
<div class="clean-card px-6 py-4 flex items-center">
|
||
<div class="w-12 h-12 rounded-full bg-emerald-50 border border-emerald-100 flex items-center justify-center text-emerald-500 mr-4">
|
||
<i class="fas fa-check-circle text-xl"></i>
|
||
</div>
|
||
<div>
|
||
<p class="text-sm text-gray-600">在线机器人</p>
|
||
<p class="text-2xl font-semibold text-gray-800" id="online-count">{{.OnlineCount}}</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="clean-card px-6 py-4 flex items-center">
|
||
<div class="w-12 h-12 rounded-full bg-blue-50 border border-blue-100 flex items-center justify-center text-blue-500 mr-4">
|
||
<i class="fas fa-robot text-xl"></i>
|
||
</div>
|
||
<div>
|
||
<p class="text-sm text-gray-600">总机器人</p>
|
||
<p class="text-2xl font-semibold text-gray-800" id="filtered-count">{{len .Robots}}</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="clean-card px-6 py-4 flex items-center">
|
||
<div class="w-12 h-12 rounded-full bg-amber-50 border border-amber-100 flex items-center justify-center text-amber-500 mr-4">
|
||
<i class="fas fa-power-off text-xl"></i>
|
||
</div>
|
||
<div>
|
||
<p class="text-sm text-gray-600">离线机器人</p>
|
||
<p class="text-2xl font-semibold text-gray-800" id="offline-count">{{.OfflineCount}}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 机器人卡片列表 -->
|
||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||
{{if .Robots}}
|
||
{{range .Robots}}
|
||
<div class="robot-card bg-white rounded-lg shadow-sm overflow-hidden border border-gray-50 transition-all duration-300 hover:shadow-md">
|
||
<!-- 状态指示条 -->
|
||
<div class="h-1 {{if eq .Status "online"}}bg-green-500{{else}}bg-gray-300{{end}} w-full"></div>
|
||
|
||
<!-- 卡片内容 -->
|
||
<div class="p-6">
|
||
<div class="flex items-center">
|
||
<div class="w-10 h-10 rounded-full {{if eq .Status "online"}}bg-green-100 text-green-600{{else}}bg-gray-100 text-gray-500{{end}} flex items-center justify-center mr-4">
|
||
<i class="fas fa-robot text-lg"></i>
|
||
</div>
|
||
<div>
|
||
<h3 class="text-lg font-medium text-gray-800 robot-name">{{.Nickname}}</h3>
|
||
<div class="flex items-center mt-1">
|
||
<span class="inline-flex items-center px-2 py-0.5 text-xs font-medium rounded-full {{if eq .Status "online"}}bg-green-100 text-green-800{{else}}bg-gray-100 text-gray-700{{end}}">
|
||
{{if eq .Status "online"}}在线{{else}}离线{{end}}
|
||
</span>
|
||
{{if .WechatID}}
|
||
<span class="ml-2 text-xs text-gray-500">{{.WechatID}}</span>
|
||
{{end}}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 机器人信息 -->
|
||
<div class="mt-4 border-t border-gray-50 pt-4">
|
||
<div class="flex justify-between items-center text-sm text-gray-500 mb-2">
|
||
<span>容器ID</span>
|
||
<span class="font-mono text-xs truncate max-w-[120px]">{{.ContainerID}}</span>
|
||
</div>
|
||
<div class="flex justify-between items-center text-sm text-gray-500">
|
||
<span>创建时间</span>
|
||
<span>{{.CreatedAt.Format "01-02 15:04"}}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 操作区域 -->
|
||
<div class="bg-gray-50 px-6 py-3 flex justify-between items-center">
|
||
<span class="text-xs text-gray-500">{{if eq .Status "online"}}运行中{{else}}已停止{{end}}</span>
|
||
<div class="flex space-x-2">
|
||
<a href="/admin/robots/{{.ID}}" class="p-1.5 rounded-md text-gray-600 hover:bg-gray-100 hover:text-gray-900" title="查看详情">
|
||
<i class="fas fa-eye"></i>
|
||
</a>
|
||
|
||
{{if eq .Status "offline"}}
|
||
<a href="/admin/robots/{{.ID}}/login" class="p-1.5 rounded-md text-blue-600 hover:bg-blue-50 hover:text-blue-700" title="登录微信">
|
||
<i class="fas fa-sign-in-alt"></i>
|
||
</a>
|
||
{{else}}
|
||
<form method="POST" action="/admin/robots/{{.ID}}/logout" class="inline">
|
||
<button type="submit" class="p-1.5 rounded-md text-yellow-600 hover:bg-yellow-50 hover:text-yellow-700" title="登出微信">
|
||
<i class="fas fa-sign-out-alt"></i>
|
||
</button>
|
||
</form>
|
||
{{end}}
|
||
|
||
<a href="#" class="p-1.5 rounded-md text-red-600 hover:bg-red-50 hover:text-red-700 delete-robot" data-id="{{.ID}}" data-robot-name="{{.Nickname}}" title="删除">
|
||
<i class="fas fa-trash"></i>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{{end}}
|
||
{{else}}
|
||
<div class="col-span-1 md:col-span-2 lg:col-span-3 bg-white rounded-lg shadow-sm p-12 text-center">
|
||
<div class="mx-auto w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mb-4">
|
||
<i class="fas fa-robot text-3xl text-gray-400"></i>
|
||
</div>
|
||
<h3 class="text-lg font-medium text-gray-700 mb-2">还没有机器人</h3>
|
||
<p class="text-gray-500 mb-6">创建您的第一个微信机器人,开启自动化之旅</p>
|
||
<a href="/admin/robots/new" class="btn btn-primary">
|
||
<i class="fas fa-plus mr-2"></i> 新建机器人
|
||
</a>
|
||
</div>
|
||
{{end}}
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// 计算不同状态机器人数量
|
||
let onlineCount = 0;
|
||
let offlineCount = 0;
|
||
let errorCount = 0;
|
||
|
||
document.querySelectorAll('.robot-card').forEach(card => {
|
||
const status = card.dataset.status;
|
||
if (status === 'online') {
|
||
onlineCount++;
|
||
} else if (status === 'error') {
|
||
errorCount++;
|
||
} else {
|
||
offlineCount++;
|
||
}
|
||
});
|
||
|
||
// 更新统计数字
|
||
document.getElementById('online-count').textContent = onlineCount;
|
||
document.getElementById('offline-count').textContent = offlineCount;
|
||
document.getElementById('error-count').textContent = errorCount;
|
||
|
||
// 删除机器人确认
|
||
document.querySelectorAll('.delete-robot').forEach(btn => {
|
||
btn.addEventListener('click', async function(e) {
|
||
e.preventDefault();
|
||
const robotId = this.dataset.id;
|
||
const robotName = this.dataset.robotName || '此机器人';
|
||
|
||
if (window.confirm(`确定要删除 ${robotName} 吗?此操作不可恢复!`)) {
|
||
// 使用fetch API发送DELETE请求
|
||
fetch(`/admin/robots/${robotId}`, { method: 'DELETE' })
|
||
.then(response => {
|
||
if (response.ok) {
|
||
window.location.reload();
|
||
} else {
|
||
throw new Error('删除失败');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
alert('删除失败: ' + error.message);
|
||
});
|
||
}
|
||
});
|
||
});
|
||
});
|
||
</script>
|
||
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// 删除机器人确认
|
||
document.querySelectorAll('.delete-robot').forEach(btn => {
|
||
btn.addEventListener('click', async function(e) {
|
||
e.preventDefault();
|
||
const robotId = this.dataset.id;
|
||
const robotName = this.dataset.robotName || '此机器人';
|
||
|
||
// 使用美观的确认对话框代替原生的window.confirm
|
||
if (typeof confirmDialog === 'function') {
|
||
// 使用confirmDialog函数
|
||
const confirmed = await confirmDialog(
|
||
`确定要删除 ${robotName} 吗?此操作不可恢复!`,
|
||
{ type: 'danger', title: '确认删除' }
|
||
);
|
||
|
||
if (confirmed) {
|
||
// 显示加载状态
|
||
this.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
|
||
this.disabled = true;
|
||
|
||
try {
|
||
// 使用fetch API发送DELETE请求
|
||
const response = await fetch(`/admin/robots/${robotId}`, {
|
||
method: 'DELETE',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
}
|
||
});
|
||
|
||
if (response.ok) {
|
||
// 删除成功,刷新页面
|
||
window.location.reload();
|
||
} else {
|
||
// 读取错误信息
|
||
const errorData = await response.json();
|
||
throw new Error(errorData.message || '删除失败');
|
||
}
|
||
} catch (error) {
|
||
console.error('Error:', error);
|
||
alert('删除失败: ' + error.message);
|
||
|
||
// 恢复按钮状态
|
||
this.innerHTML = '<i class="fas fa-trash"></i>';
|
||
this.disabled = false;
|
||
}
|
||
}
|
||
} else {
|
||
// 降级方案:使用原生confirm
|
||
if (window.confirm(`确定要删除 ${robotName} 吗?此操作不可恢复!`)) {
|
||
// 使用fetch API发送DELETE请求
|
||
fetch(`/admin/robots/${robotId}`, { method: 'DELETE' })
|
||
.then(response => {
|
||
if (response.ok) {
|
||
window.location.reload();
|
||
} else {
|
||
throw new Error('删除失败');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
alert('删除失败: ' + error.message);
|
||
});
|
||
}
|
||
}
|
||
});
|
||
});
|
||
});
|
||
</script>
|
||
|
||
<style>
|
||
.robot-card {
|
||
box-shadow: 0 2px 10px rgba(0,0,0,0.08); /* 保留基础阴影 */
|
||
position: relative;
|
||
margin: 3px 0;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
||
}
|
||
|
||
.robot-card:hover {
|
||
box-shadow: 0 8px 30px rgba(0,0,0,0.12); /* 保留悬停时的阴影效果 */
|
||
z-index: 10;
|
||
transform: translateY(-3px) scale(1.01); /* 保留上移和放大效果 */
|
||
/* 移除了原来的边框线:outline: 1px solid rgba(99, 102, 241, 0.3); */
|
||
}
|
||
|
||
/* 移除左侧边框装饰线 */
|
||
.robot-card:before {
|
||
display: none; /* 完全移除左侧的灰色条 */
|
||
}
|
||
|
||
/* 移除悬停时的左侧边框装饰线 */
|
||
.robot-card:hover:before {
|
||
display: none;
|
||
}
|
||
|
||
/* 同样移除基于状态的左侧边框装饰线 */
|
||
.robot-card:has(.bg-green-100):hover:before,
|
||
.robot-card:has(.bg-gray-100):hover:before {
|
||
display: none;
|
||
}
|
||
|
||
/* 保留其他有用的样式 */
|
||
.robot-card a, .robot-card button {
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.robot-card:hover a, .robot-card:hover button {
|
||
transform: scale(1.15);
|
||
filter: drop-shadow(0 1px 2px rgba(0,0,0,0.2));
|
||
}
|
||
|
||
/* 移除每个卡片的间隔装饰线 */
|
||
.robot-card td:first-child:before {
|
||
display: none;
|
||
}
|
||
</style>
|