<!DOCTYPE html>
|
<html lang="zh-CN">
|
|
<head>
|
<meta charset="utf-8">
|
<title>WebSocket调试</title>
|
<meta name="renderer" content="webkit">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
<link rel="stylesheet" href="../../static/vue/element/element.css">
|
<style>
|
* {
|
margin: 0;
|
padding: 0;
|
box-sizing: border-box;
|
}
|
|
body {
|
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;
|
background: #f5f7fa;
|
padding: 15px;
|
}
|
|
.app-container {
|
background: #fff;
|
border-radius: 8px;
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
padding: 20px;
|
}
|
|
.toolbar {
|
display: flex;
|
align-items: center;
|
gap: 10px;
|
margin-bottom: 15px;
|
flex-wrap: wrap;
|
}
|
|
.toolbar .el-input {
|
width: 400px;
|
}
|
|
.status-dot {
|
display: inline-block;
|
width: 10px;
|
height: 10px;
|
border-radius: 50%;
|
margin-right: 5px;
|
}
|
|
.status-dot.connected {
|
background: #67c23a;
|
}
|
|
.status-dot.disconnected {
|
background: #909399;
|
}
|
|
.log-panel {
|
border: 1px solid #dcdfe6;
|
border-radius: 4px;
|
height: calc(100vh - 220px);
|
overflow-y: auto;
|
padding: 10px;
|
background: #1e1e1e;
|
color: #d4d4d4;
|
font-family: Consolas, Monaco, 'Courier New', monospace;
|
font-size: 13px;
|
line-height: 1.6;
|
}
|
|
.log-item {
|
margin-bottom: 4px;
|
word-break: break-all;
|
white-space: pre-wrap;
|
}
|
|
.log-item .time {
|
color: #6a9955;
|
margin-right: 8px;
|
}
|
|
.log-item.system {
|
color: #569cd6;
|
}
|
|
.log-item.error {
|
color: #f44747;
|
}
|
|
.log-item.receive {
|
color: #dcdcaa;
|
}
|
|
.log-item .tag {
|
display: inline-block;
|
padding: 0 6px;
|
border-radius: 3px;
|
font-size: 12px;
|
margin-right: 6px;
|
}
|
|
.tag-connect {
|
background: #67c23a;
|
color: #fff;
|
}
|
|
.tag-close {
|
background: #909399;
|
color: #fff;
|
}
|
|
.tag-error {
|
background: #f56c6c;
|
color: #fff;
|
}
|
|
.tag-receive {
|
background: #e6a23c;
|
color: #fff;
|
}
|
|
.filter-bar {
|
display: flex;
|
align-items: center;
|
gap: 10px;
|
margin-bottom: 10px;
|
}
|
|
.stats {
|
color: #909399;
|
font-size: 13px;
|
margin-left: auto;
|
}
|
</style>
|
</head>
|
|
<body>
|
<div id="app" class="app-container">
|
<div class="toolbar">
|
<el-input v-model="wsUrl" placeholder="WebSocket地址" size="small" :disabled="connected">
|
<template slot="prepend">WS</template>
|
</el-input>
|
<el-button :type="connected ? 'danger' : 'primary'" size="small" @click="toggleConnection">
|
{{ connected ? '断开连接' : '连接' }}
|
</el-button>
|
<el-button size="small" @click="clearLogs">清空日志</el-button>
|
<el-button size="small" @click="autoScroll = !autoScroll">
|
{{ autoScroll ? '自动滚动:开' : '自动滚动:关' }}
|
</el-button>
|
<span>
|
<span class="status-dot" :class="connected ? 'connected' : 'disconnected'"></span>
|
{{ connected ? '已连接' : '未连接' }}
|
</span>
|
</div>
|
|
<div class="filter-bar">
|
<el-checkbox-group v-model="filters" size="small">
|
<el-checkbox-button label="system">系统</el-checkbox-button>
|
<el-checkbox-button label="receive">接收</el-checkbox-button>
|
<el-checkbox-button label="error">错误</el-checkbox-button>
|
</el-checkbox-group>
|
<el-input v-model="searchKeyword" placeholder="搜索消息内容" size="small" style="width: 250px;"
|
clearable></el-input>
|
<span class="stats">共 {{ filteredLogs.length }} 条</span>
|
</div>
|
|
<div class="log-panel" ref="logPanel">
|
<div v-for="(log, index) in filteredLogs" :key="index" class="log-item" :class="log.type">
|
<span class="time">{{ log.time }}</span>
|
<span v-if="log.type === 'system'" class="tag tag-connect">系统</span>
|
<span v-else-if="log.type === 'error'" class="tag tag-error">错误</span>
|
<span v-else-if="log.type === 'receive'" class="tag tag-receive">{{ log.url || '接收' }}</span>
|
<span>{{ log.content }}</span>
|
</div>
|
<div v-if="filteredLogs.length === 0" style="color: #606266; text-align: center; padding: 40px;">
|
点击"连接"开始接收WebSocket消息
|
</div>
|
</div>
|
</div>
|
|
<script src="../../static/vue/js/vue.min.js"></script>
|
<script src="../../static/vue/element/element.js"></script>
|
<script>
|
new Vue({
|
el: '#app',
|
data: {
|
wsUrl: '',
|
ws: null,
|
connected: false,
|
autoScroll: true,
|
logs: [],
|
filters: ['system', 'receive', 'error'],
|
searchKeyword: '',
|
msgCount: {}
|
},
|
created: function () {
|
this.wsUrl = this.getDefaultUrl();
|
},
|
computed: {
|
filteredLogs: function () {
|
var self = this;
|
return this.logs.filter(function (log) {
|
var typeMatch = self.filters.indexOf(log.type) >= 0;
|
var searchMatch = !self.searchKeyword ||
|
log.content.toLowerCase().indexOf(self.searchKeyword.toLowerCase()) >= 0 ||
|
(log.url && log.url.toLowerCase().indexOf(self.searchKeyword.toLowerCase()) >= 0);
|
return typeMatch && searchMatch;
|
});
|
}
|
},
|
methods: {
|
getDefaultUrl: function () {
|
var protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
return protocol + '//' + window.location.host + '/monitor/tv/socket';
|
},
|
toggleConnection: function () {
|
if (this.connected) {
|
this.disconnect();
|
} else {
|
this.connect();
|
}
|
},
|
connect: function () {
|
var self = this;
|
try {
|
this.ws = new WebSocket(this.wsUrl);
|
} catch (e) {
|
this.addLog('error', '连接失败: ' + e.message);
|
return;
|
}
|
|
this.ws.onopen = function () {
|
self.connected = true;
|
self.addLog('system', '连接已建立');
|
};
|
|
this.ws.onmessage = function (event) {
|
var url = '';
|
var content = event.data;
|
try {
|
var msg = JSON.parse(event.data);
|
url = msg.url || '';
|
if (msg.data) {
|
try {
|
var parsed = JSON.parse(msg.data);
|
content = JSON.stringify(parsed, null, 2);
|
} catch (e) {
|
content = msg.data;
|
}
|
}
|
} catch (e) {
|
// 非JSON消息,直接显示
|
}
|
|
if (url) {
|
self.msgCount[url] = (self.msgCount[url] || 0) + 1;
|
}
|
|
self.addLog('receive', content, url);
|
};
|
|
this.ws.onclose = function () {
|
self.connected = false;
|
self.addLog('system', '连接已关闭');
|
};
|
|
this.ws.onerror = function () {
|
self.addLog('error', '连接发生错误');
|
};
|
},
|
disconnect: function () {
|
if (this.ws) {
|
this.ws.close();
|
this.ws = null;
|
}
|
},
|
addLog: function (type, content, url) {
|
var now = new Date();
|
var time = [
|
String(now.getHours()).padStart(2, '0'),
|
String(now.getMinutes()).padStart(2, '0'),
|
String(now.getSeconds()).padStart(2, '0')
|
].join(':') + '.' + String(now.getMilliseconds()).padStart(3, '0');
|
|
this.logs.push({
|
time: time,
|
type: type,
|
content: content,
|
url: url
|
});
|
|
// 限制最大日志条数
|
if (this.logs.length > 2000) {
|
this.logs.splice(0, this.logs.length - 1500);
|
}
|
|
if (this.autoScroll) {
|
this.$nextTick(function () {
|
var panel = this.$refs.logPanel;
|
if (panel) {
|
panel.scrollTop = panel.scrollHeight;
|
}
|
});
|
}
|
},
|
clearLogs: function () {
|
this.logs = [];
|
this.msgCount = {};
|
}
|
},
|
beforeDestroy: function () {
|
this.disconnect();
|
}
|
});
|
</script>
|
</body>
|
|
</html>
|