You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

491 lines
20 KiB

@{
ViewData["Title"] = "主页";
var clients = ViewBag.Clients as List<dynamic>;
var ipGroups = ViewBag.IpGroups as List<dynamic>;
}
<style>
.client-table {
margin-bottom: 0;
}
.client-table th {
background-color: #f8f9fa !important;
color: #212529 !important;
border-top: none;
font-weight: 600;
}
.client-table td {
vertical-align: middle;
}
.project-actions {
display: flex;
gap: 0.5rem;
justify-content: flex-end;
flex-wrap: wrap;
}
.project-actions .btn {
margin: 0;
white-space: nowrap;
}
.badge {
font-size: 0.75rem;
padding: 0.375rem 0.75rem;
}
.badge-red, .badge.badge-red {
background-color: #ffd6d6 !important;
color: #111 !important;
font-weight: bold !important;
font-size: 1.1rem !important;
letter-spacing: 1px !important;
box-shadow: none !important;
padding: 0.375rem 0.75rem !important;
border-radius: 0.375rem !important;
display: inline-block !important;
text-shadow: none !important;
}
/* 页面容器 */
.card-body {
max-height: calc(100vh - 280px);
overflow-y: auto;
overflow-x: hidden;
}
.table-responsive {
border-radius: 0.5rem;
overflow: hidden;
}
/* 自定义滚动条样式 */
.card-body::-webkit-scrollbar {
width: 6px;
}
.card-body::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 3px;
}
.card-body::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 3px;
}
.card-body::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
.card-header {
background-color: #f8f9fa;
border-bottom: 1px solid #dee2e6;
}
.card-title {
margin-bottom: 0;
color: #495057;
}
.card-title i {
margin-right: 0.5rem;
}
/* 响应式调整 */
@@media (max-width: 768px) {
.card-body {
max-height: calc(100vh - 240px);
}
}
@@media (max-width: 576px) {
.card-body {
max-height: calc(100vh - 220px);
}
}
.status-dot {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 50%;
margin-right: 6px;
vertical-align: middle;
}
.status-running {
background: #28a745;
}
.status-stopped {
background: #dc3545;
}
.status-idle {
background: #adb5bd;
}
.status-text {
font-weight: bold;
color: #333;
vertical-align: middle;
}
/* IP分组表格样式 */
.ip-group-table {
margin-top: 2rem;
}
.ip-group-table .form-control-sm {
height: 30px;
font-size: 0.875rem;
padding: 0.25rem 0.5rem;
}
.ip-group-table .badge-info {
background-color: #17a2b8;
color: white;
font-size: 0.75rem;
padding: 0.375rem 0.75rem;
}
.ip-group-table .card-header {
background-color: #e3f2fd;
border-bottom: 1px solid #bbdefb;
}
.ip-group-table .card-title {
color: #1976d2;
}
.ip-group-table .card-title i {
color: #1976d2;
}
</style>
<div class="container">
<!-- IP分组管理表格 -->
<div class="row">
<div class="col-12">
<div class="card ip-group-table">
<div class="card-header">
<h3 class="card-title">
<i class="fas fa-network-wired"></i>AgentService
</h3>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-striped projects client-table">
<thead>
<tr>
<th style="width: 25%">AgentIP</th>
<th style="width: 25%">AgentPort</th>
<th style="width: 10%">Key</th>
<th style="width: 10%">状态</th>
<th style="width: 16%" class="text-center">操作</th>
</tr>
</thead>
<tbody>
@if (ipGroups != null)
{
foreach (var group in ipGroups)
{
<tr>
<td>@group.Ip</td>
<td>@group.Port</td>
<td>
<input type="text" class="form-control form-control-sm"
value="@group.Key"
placeholder="输入Key"
onchange="updateGroupKey('@group.Ip', this.value)">
</td>
<td>
@if (group.State == "运行")
{
<span class="status-dot status-running"></span><span class="status-text">运行</span>
}
else if (group.State == "部分运行")
{
<span class="status-dot status-running"></span><span class="status-text">部分运行</span>
}
else
{
<span class="status-dot status-stopped"></span><span class="status-text">停止</span>
}
</td>
<td class="project-actions text-right">
<a class="btn btn-primary btn-sm" href="#" onclick="startIpGroup('@group.Ip')">
<i class="fas fa-play"></i> 启动网络
</a>
<a class="btn btn-danger btn-sm" href="#" onclick="stopIpGroup('@group.Ip')">
<i class="fas fa-stop"></i> 停止网络
</a>
</td>
</tr>
}
}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">
<i class="fas fa-server"></i>客户端管理
</h3>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-striped projects client-table">
<thead>
<tr>
<th style="width: 20%">客户端名称</th>
<th style="width: 25%">地址</th>
<th style="width: 10%">状态</th>
<th style="width: 8%" class="text-center">SSL</th>
<th style="width: 8%" class="text-center">启用</th>
<th style="width: 29%" class="text-center">操作</th>
</tr>
</thead>
<tbody>
@if (clients != null)
{
foreach (var client in clients)
{
var config = client.Config as LTEMvcApp.Models.ClientConfig;
var state = (LTEMvcApp.Models.ClientState)client.State;
<tr>
<td>@client.Config.Name</td>
<td>@client.Config.Address</td>
<td>
@if (state == LTEMvcApp.Models.ClientState.Connected)
{
<span class="status-dot status-running"></span>
<span class="status-text">运行</span>
}
else if (state == LTEMvcApp.Models.ClientState.Stop)
{
<span class="status-dot status-idle"></span>
<span class="status-text">未启动</span>
}
else
{
<span class="status-dot status-stopped"></span>
<span class="status-text">停止</span>
}
</td>
<td class="text-center">
@if (config.Ssl)
{
<i class="fas fa-check-circle text-success"></i>
}
else
{
<i class="fas fa-times-circle text-muted"></i>
}
</td>
<td class="text-center">
@if (config.Enabled)
{
<i class="fas fa-check-circle text-success"></i>
}
else
{
<i class="fas fa-times-circle text-muted"></i>
}
</td>
<td class="project-actions text-right">
<a class="btn btn-primary btn-sm @(state == LTEMvcApp.Models.ClientState.Connected ? "disabled" : "")" href="#" onclick="startTestClient('@config.Address')">
<i class="fas fa-play"></i> 启动
</a>
<a class="btn btn-danger btn-sm @(state != LTEMvcApp.Models.ClientState.Connected ? "disabled" : "")" href="#" onclick="stopTestClient('@config.Address')">
<i class="fas fa-stop"></i> 停止
</a>
<a class="btn btn-info btn-sm @(state != LTEMvcApp.Models.ClientState.Connected ? "disabled" : "")" href="@Url.Action("ClientMessages", "Home", new { address = config.Address })">
<i class="fas fa-list"></i> 消息
</a>
<a href="@Url.Action("TestClientConfig", "Home", new { address = config.Address })" class="btn btn-sm btn-warning @(state == LTEMvcApp.Models.ClientState.Connected ? "disabled" : "")">
<i class="fas fa-cog"></i> 配置
</a>
<a class="btn btn-sm btn-outline-danger @(state == LTEMvcApp.Models.ClientState.Connected ? "disabled" : "")" href="#" onclick="deleteTestClient('@config.Address')">
<i class="fas fa-trash"></i> 删除
</a>
</td>
</tr>
}
}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
@section Scripts {
<script>
function startTestClient(address) {
$.ajax({
url: '/api/testconfig/start',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({ address: address }),
success: function() {
alert('启动请求已发送!页面将在2秒后刷新。');
setTimeout(() => location.reload(), 2000);
},
error: function(xhr) {
alert('启动失败:' + xhr.responseText);
}
});
}
function stopTestClient(address) {
$.ajax({
url: '/api/testconfig/stop',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({ address: address }),
success: function() {
alert('停止请求已发送!页面将在2秒后刷新。');
setTimeout(() => location.reload(), 2000);
},
error: function(xhr) {
alert('停止失败:' + xhr.responseText);
}
});
}
function deleteTestClient(address) {
if (!confirm('确定要删除该测试客户端吗?')) return;
$.ajax({
url: '/api/testconfig/address/' + encodeURIComponent(address),
type: 'DELETE',
success: function() {
alert('删除成功!');
setTimeout(() => location.reload(), 1000);
},
error: function(xhr) {
alert('删除失败:' + xhr.responseText);
}
});
}
function updateGroupKey(ip, key) {
// 发送AJAX请求保存到服务器
$.ajax({
url: '/api/ipgroup/key',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({ ip: ip, key: key }),
success: function(response) {
console.log('IP组Key保存成功:', response);
// 可以显示一个小的成功提示
showToast('Key保存成功', 'success');
},
error: function(xhr) {
console.error('IP组Key保存失败:', xhr.responseText);
showToast('Key保存失败', 'error');
}
});
}
function startIpGroup(ip) {
// 获取当前行的端口信息
var row = $('tr').filter(function() {
return $(this).find('td:first').text().trim() === ip;
});
var port = row.find('td:eq(1)').text().trim(); // 第二列是端口
var keyInput = row.find('input[type="text"]');
var key = keyInput.val().trim();
if (!key) {
alert('请先填写网络Key!');
keyInput.focus();
return;
}
if (!confirm('确定要启动该网络吗?')) return;
$.ajax({
url: '/api/ipgroup/start',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({ ip: ip, port: port }),
success: function(response) {
showToast('网络启动成功', 'success');
setTimeout(() => location.reload(), 2000);
},
error: function(xhr) {
var errorMsg = '启动失败';
try {
var errorResponse = JSON.parse(xhr.responseText);
errorMsg = errorResponse.message || errorResponse;
} catch (e) {
errorMsg = xhr.responseText || '启动失败';
}
showToast(errorMsg, 'error');
}
});
}
function stopIpGroup(ip) {
// 获取当前行的端口信息
var row = $('tr').filter(function() {
return $(this).find('td:first').text().trim() === ip;
});
var port = row.find('td:eq(1)').text().trim(); // 第二列是端口
if (!confirm('确定要停止该网络吗?')) return;
$.ajax({
url: '/api/ipgroup/stop',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({ ip: ip, port: port }),
success: function(response) {
showToast('网络停止成功', 'success');
setTimeout(() => location.reload(), 2000);
},
error: function(xhr) {
var errorMsg = '停止失败';
try {
var errorResponse = JSON.parse(xhr.responseText);
errorMsg = errorResponse.message || errorResponse;
} catch (e) {
errorMsg = xhr.responseText || '停止失败';
}
showToast(errorMsg, 'error');
}
});
}
function showToast(message, type) {
// 简单的提示函数,可以替换为更美观的toast组件
var alertClass = type === 'success' ? 'alert-success' : 'alert-danger';
var alertHtml = '<div class="alert ' + alertClass + ' alert-dismissible fade show position-fixed" style="top: 20px; right: 20px; z-index: 9999;" role="alert">' +
message +
'<button type="button" class="btn-close" data-bs-dismiss="alert"></button>' +
'</div>';
$('body').append(alertHtml);
// 3秒后自动移除
setTimeout(function() {
$('.alert').fadeOut();
}, 3000);
}
</script>
}