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.
 
 
 

461 lines
19 KiB

@{
ViewData["Title"] = "测试客户端配置";
var testConfig = ViewBag.TestConfig as LTEMvcApp.Models.ClientConfig;
// 只保留不含 EVENT 的日志层
var allLayers = LTEMvcApp.Models.LogLayerTypes.AllLayers.Where(l => l != "EVENT").ToArray();
var layerConfigs = testConfig?.Logs?.Layers ?? new Dictionary<string, LTEMvcApp.Models.LogLayerConfig>();
}
<style>
/* 页面整体布局 */
.config-container {
max-height: calc(100vh - 280px);
display: flex;
flex-direction: column;
overflow: hidden;
}
/* 表单容器 */
.config-form {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
padding: 0.75rem;
}
/* 自定义滚动条 */
.config-form::-webkit-scrollbar {
width: 6px;
}
.config-form::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 3px;
}
.config-form::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 3px;
}
.config-form::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
/* 表单样式 */
.form-group {
margin-bottom: 0.75rem;
}
.form-group label {
font-weight: 600;
color: #495057;
margin-bottom: 0.25rem;
font-size: 0.9rem;
}
.form-control {
border-radius: 0.375rem;
border: 1px solid #ced4da;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
padding: 0.375rem 0.75rem;
}
.form-control:focus {
border-color: #80bdff;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
/* 表格样式 */
.table-responsive {
border-radius: 0.5rem;
overflow: hidden;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
.table {
margin-bottom: 0;
flex: 1;
font-size: 0.85rem;
}
.table th {
background-color: #343a40;
color: white;
font-weight: 600;
border: none;
position: sticky;
top: 0;
z-index: 10;
padding: 0.5rem;
}
.table td {
vertical-align: middle;
border-color: #dee2e6;
padding: 0.5rem;
}
/* 按钮样式 */
.btn {
border-radius: 0.375rem;
font-weight: 500;
transition: all 0.2s;
padding: 0.375rem 0.75rem;
font-size: 0.9rem;
}
.btn:hover {
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.form-check {
margin-bottom: 0.5rem;
}
.form-check-input:checked {
background-color: #007bff;
border-color: #007bff;
}
.section-title {
color: #495057;
font-weight: 600;
margin-bottom: 0.5rem;
padding-bottom: 0.25rem;
border-bottom: 2px solid #e9ecef;
font-size: 1rem;
}
/* 按钮区域 */
.button-area {
flex-shrink: 0;
background: white;
border-top: 1px solid #dee2e6;
padding: 0.5rem 0.75rem;
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
/* 响应式调整 */
@@media (max-width: 768px) {
.config-container {
max-height: calc(100vh - 240px);
}
.button-area {
padding: 0.4rem 0.6rem;
}
.config-form {
padding: 0.6rem;
}
}
@@media (max-width: 576px) {
.config-container {
max-height: calc(100vh - 220px);
}
.button-area {
padding: 0.3rem 0.5rem;
}
.config-form {
padding: 0.5rem;
}
.btn {
font-size: 0.8rem;
padding: 0.3rem 0.6rem;
}
}
/* 日志层配置容器 */
.logs-config-section {
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
}
.logs-config-content {
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
}
</style>
<div class="container">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">
<i class="fas fa-cog"></i> 测试客户端配置
</h3>
</div>
<div class="card-body p-0">
<div class="config-container">
<form id="testClientConfigForm" class="config-form">
<!-- 基本配置 -->
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="name">客户端名称</label>
<input type="text" class="form-control" id="name" name="name" value="@testConfig?.Name" required>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="address">服务器地址</label>
<input type="text" class="form-control" id="address" name="address" value="@testConfig?.Address" placeholder="192.168.13.12:9001">
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="password">密码</label>
<input type="password" class="form-control" id="password" name="password" value="@testConfig?.Password">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="reconnectDelay">重连延迟 (毫秒)</label>
<input type="number" class="form-control" id="reconnectDelay" name="reconnectDelay" value="@testConfig?.ReconnectDelay">
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-check form-check-inline">
<input type="checkbox" class="form-check-input" id="enabled" name="enabled" @(testConfig?.Enabled == true ? "checked" : "")>
<label class="form-check-label" for="enabled">启用</label>
</div>
<div class="form-check form-check-inline">
<input type="checkbox" class="form-check-input" id="ssl" name="ssl" @(testConfig?.Ssl == true ? "checked" : "")>
<label class="form-check-label" for="ssl">使用SSL</label>
</div>
<div class="form-check form-check-inline">
<input type="checkbox" class="form-check-input" id="readonly" name="readonly" @(testConfig?.Readonly == true ? "checked" : "")>
<label class="form-check-label" for="readonly">只读模式</label>
</div>
</div>
</div>
<!-- 日志层配置 -->
<div class="row mt-4 logs-config-section">
<div class="col-md-12">
<h5 class="section-title">
<i class="fas fa-layer-group"></i> 日志层配置
</h5>
<div class="logs-config-content">
<div class="table-responsive">
<table class="table table-bordered table-sm">
<thead class="table-dark">
<tr>
<th>日志层</th>
<th>过滤器</th>
<th>级别</th>
<th>最大大小</th>
<th>包含负载</th>
</tr>
</thead>
<tbody>
@foreach (var layer in allLayers)
{
var config = layerConfigs[layer];
<tr>
<td><strong>@layer</strong></td>
<td>
<select class="form-control form-control-sm" name="layers[@layer][filter]">
@foreach (var logLevel in LTEMvcApp.Models.LogLayerTypes.LogLevels)
{
if (logLevel == config.Filter)
{
<option value="@logLevel" selected>@logLevel.ToUpper()</option>
}
else
{
<option value="@logLevel">@logLevel.ToUpper()</option>
}
}
</select>
</td>
<td>
<select class="form-control form-control-sm" name="layers[@layer][level]">
@foreach (var logLevel in LTEMvcApp.Models.LogLayerTypes.LogLevels)
{
if (logLevel == config.Level)
{
<option value="@logLevel" selected>@logLevel.ToUpper()</option>
}
else
{
<option value="@logLevel">@logLevel.ToUpper()</option>
}
}
</select>
</td>
<td>
<input type="number" class="form-control form-control-sm" name="layers[@layer][max_size]" value="@config.MaxSize" min="1" max="1000">
</td>
<td>
<div class="form-check">
<input type="checkbox" class="form-check-input" name="layers[@layer][payload]" @(config.Payload ? "checked" : "")>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
</div>
</form>
<!-- 操作按钮 -->
<div class="button-area">
<button type="submit" class="btn btn-primary" form="testClientConfigForm">
<i class="fas fa-save"></i> 保存配置
</button>
<button type="button" class="btn btn-success" onclick="startTestClient()">
<i class="fas fa-play"></i> 启动测试客户端
</button>
<button type="button" class="btn btn-danger" onclick="stopTestClient()">
<i class="fas fa-stop"></i> 停止测试客户端
</button>
<a href="@Url.Action("ClientMessages", "Home", new { clientName = testConfig?.Name })" class="btn btn-info">
<i class="fas fa-list"></i> 查看消息队列
</a>
<button type="button" class="btn btn-secondary" onclick="resetToDefaults()">
<i class="fas fa-undo"></i> 重置为默认值
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@section Scripts {
<script>
$(document).ready(function() {
$('#testClientConfigForm').on('submit', function(e) {
e.preventDefault();
saveTestClientConfig();
});
});
function saveTestClientConfig() {
var formData = {
name: $('#name').val(),
address: $('#address').val(),
password: $('#password').val(),
reconnectDelay: parseInt($('#reconnectDelay').val()) || 15000,
enabled: $('#enabled').is(':checked'),
ssl: $('#ssl').is(':checked'),
readonly: $('#readonly').is(':checked'),
logs: {
layers: {}
}
};
// 构建日志层配置
var layers = @Html.Raw(System.Text.Json.JsonSerializer.Serialize(allLayers));
layers.forEach(function(layer) {
var level = $(`select[name="layers[${layer}][level]"]`).val();
var filter = $(`select[name="layers[${layer}][filter]"]`).val();
var maxSize = parseInt($(`input[name="layers[${layer}][max_size]"]`).val()) || 1;
var payload = $(`input[name="layers[${layer}][payload]"]`).is(':checked');
formData.logs.layers[layer] = {
level: level,
maxSize: maxSize,
payload: payload,
filter: filter
};
});
// 添加其他日志配置
// formData.logs.signal = ...; // 如果需要的话
$.ajax({
url: '/api/websocket/test-client-config',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(formData),
success: function(response) {
alert('配置保存成功!');
location.reload();
},
error: function(xhr) {
alert('保存配置失败:' + xhr.responseText);
}
});
}
function startTestClient() {
$.ajax({
url: '/api/websocket/test-client/start',
type: 'POST',
success: function(response) {
alert('测试客户端启动成功!');
},
error: function(xhr) {
alert('启动测试客户端失败:' + xhr.responseText);
}
});
}
function stopTestClient() {
$.ajax({
url: '/api/websocket/test-client/stop',
type: 'POST',
success: function(response) {
alert('测试客户端停止成功!');
},
error: function(xhr) {
alert('停止测试客户端失败:' + xhr.responseText);
}
});
}
function resetToDefaults() {
if (confirm('确定要重置为默认配置吗?')) {
// 重置为默认值
var defaultLevels = {
'PHY': 'info', 'MAC': 'info', 'RLC': 'info', 'PDCP': 'warn',
'RRC': 'debug', 'NAS': 'debug', 'S1AP': 'debug', 'NGAP': 'debug',
'GTPU': 'info', 'X2AP': 'debug', 'XnAP': 'info', 'M2AP': 'info'
};
Object.keys(defaultLevels).forEach(function(layer) {
$(`select[name="layers[${layer}][level]"]`).val(defaultLevels[layer]);
$(`select[name="layers[${layer}][filter]"]`).val('warn');
$(`input[name="layers[${layer}][max_size]"]`).val(1);
$(`input[name="layers[${layer}][payload]"]`).prop('checked', false);
});
// 设置一些层的 payload 为 true
['PHY', 'MAC', 'RRC', 'NAS'].forEach(function(layer) {
$(`input[name="layers[${layer}][payload]"]`).prop('checked', true);
});
alert('已重置为默认配置!');
}
}
</script>
}