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.
 
 
 

193 lines
8.3 KiB

@{
ViewData["Title"] = "客户端消息队列";
var clientName = ViewBag.ClientName as string ?? "TestClient";
}
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">客户端消息队列 - @clientName</h3>
<div class="card-tools">
<span id="connection-status" class="badge badge-secondary">正在连接...</span>
<a href="@Url.Action("TestClientConfig", "Home")" class="btn btn-info btn-sm ml-2">
<i class="fas fa-cog"></i> 配置
</a>
</div>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5 class="card-title">
<i class="fas fa-paper-plane text-success"></i> 发送消息
<span class="badge badge-primary" id="sentCount">0</span>
</h5>
</div>
<div class="card-body" style="max-height: 600px; overflow-y: auto;">
<div id="sentMessages">
<div class="text-muted text-center">
<i class="fas fa-spinner fa-spin"></i> 正在建立与服务器的连接...
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5 class="card-title">
<i class="fas fa-download text-info"></i> 接收消息
<span class="badge badge-info" id="receivedCount">0</span>
</h5>
</div>
<div class="card-body" style="max-height: 600px; overflow-y: auto;">
<div id="receivedMessages">
<div class="text-muted text-center">
<i class="fas fa-spinner fa-spin"></i> 正在建立与服务器的连接...
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@section Scripts {
<script>
const clientName = '@clientName';
const MAX_MESSAGES = 500; // 每个列表最多显示500条消息
$(document).ready(function() {
initializeEventSource();
});
function initializeEventSource() {
$('#sentMessages').html('<div class="text-muted text-center"><i class="fas fa-spinner fa-spin"></i> 正在建立与服务器的连接...</div>');
$('#receivedMessages').html('<div class="text-muted text-center"><i class="fas fa-spinner fa-spin"></i> 正在建立与服务器的连接...</div>');
const source = new EventSource(`/api/websocket/clients/${encodeURIComponent(clientName)}/messages/stream`);
const statusBadge = $('#connection-status');
source.addEventListener('open', function(e) {
console.log("SSE connection opened.");
statusBadge.removeClass('badge-secondary badge-danger').addClass('badge-success').text('已连接');
// 清空等待消息
$('#sentMessages').empty();
$('#receivedMessages').empty();
});
source.addEventListener('update', function(e) {
const data = JSON.parse(e.data);
updateMessageList(data.type, data.messages, data.totalCount);
});
source.addEventListener('error', function(e) {
statusBadge.removeClass('badge-success').addClass('badge-danger').text('连接断开');
if (e.target.readyState === EventSource.CLOSED) {
console.error("SSE connection closed.");
} else {
console.error("SSE error:", e);
}
// EventSource 会自动尝试重连
});
}
function updateMessageList(type, newMessages, totalCount) {
if (!newMessages || newMessages.length === 0) {
$(`#${type}Count`).text(totalCount);
return;
}
const container = $(`#${type}Messages`);
const fragment = $(document.createDocumentFragment());
// 移除 "暂无消息" 或 "正在连接" 的提示
if (container.children().length === 1 && !container.children().first().hasClass('card')) {
container.empty();
}
let currentIndex = totalCount - newMessages.length;
newMessages.forEach(function(message) {
const card = createMessageCard(message, currentIndex, type);
fragment.append(card);
currentIndex++;
});
container.append(fragment);
// 限制DOM节点数量
const messageCards = container.children('.card');
if (messageCards.length > MAX_MESSAGES) {
messageCards.slice(0, messageCards.length - MAX_MESSAGES).remove();
}
$(`#${type}Count`).text(totalCount);
// 高亮新添加的代码
container.find('pre code').not('.hljs').each(function() {
if (typeof hljs !== 'undefined') {
hljs.highlightElement(this);
}
});
}
function createMessageCard(message, index, type) {
const timestamp = new Date().toLocaleTimeString();
const messageType = type === 'sent' ? '发送' : '接收';
const bgClass = type === 'sent' ? 'border-success' : 'border-info';
const iconClass = type === 'sent' ? 'fas fa-paper-plane text-success' : 'fas fa-download text-info';
const cardHtml = `
<div class="card mb-2 ${bgClass}">
<div class="card-header py-2">
<small class="text-muted">
<i class="${iconClass}"></i> ${messageType} #${index + 1} - ${timestamp}
</small>
<button class="btn btn-sm btn-outline-secondary float-right py-0" onclick="toggleMessage(this)">
<i class="fas fa-chevron-down"></i>
</button>
</div>
<div class="card-body py-2" style="display: none;">
<pre><code class="json">${escapeHtml(formatJson(message))}</code></pre>
</div>
</div>
`;
return $(cardHtml);
}
function toggleMessage(button) {
$(button).closest('.card').find('.card-body').slideToggle('fast');
$(button).find('i').toggleClass('fa-chevron-down fa-chevron-up');
}
function formatJson(jsonString) {
try {
var obj = JSON.parse(jsonString);
return JSON.stringify(obj, null, 2);
} catch (e) {
return jsonString;
}
}
function escapeHtml(text) {
return text
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
</script>
<!-- 添加代码高亮支持 -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/github.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
}