| New file |
| | |
| | | <!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; |
| | | } |
| | | |
| | | .stats-cards { |
| | | display: flex; |
| | | gap: 15px; |
| | | margin-bottom: 20px; |
| | | flex-wrap: wrap; |
| | | } |
| | | |
| | | .stat-card { |
| | | background: linear-gradient(135deg, #409eff, #66b1ff); |
| | | border-radius: 8px; |
| | | padding: 20px 30px; |
| | | color: #fff; |
| | | min-width: 200px; |
| | | flex: 1; |
| | | max-width: 300px; |
| | | } |
| | | |
| | | .stat-card .label { |
| | | font-size: 14px; |
| | | opacity: 0.9; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .stat-card .value { |
| | | font-size: 36px; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .header-section { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .header-section .title { |
| | | font-size: 16px; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | } |
| | | |
| | | .status-dot { |
| | | display: inline-block; |
| | | width: 8px; |
| | | height: 8px; |
| | | border-radius: 50%; |
| | | margin-right: 6px; |
| | | } |
| | | |
| | | .status-online { |
| | | background: #67c23a; |
| | | } |
| | | |
| | | .pagination-section { |
| | | margin-top: 20px; |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | } |
| | | |
| | | .auto-refresh-bar { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10px; |
| | | margin-bottom: 15px; |
| | | font-size: 13px; |
| | | color: #909399; |
| | | } |
| | | </style> |
| | | </head> |
| | | |
| | | <body> |
| | | <div id="app"> |
| | | <div class="app-container"> |
| | | <!-- 统计卡片 --> |
| | | <div class="stats-cards"> |
| | | <div class="stat-card"> |
| | | <div class="label">WebSocket连接总数</div> |
| | | <div class="value">{{ total }}</div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 自动刷新控制 --> |
| | | <div class="auto-refresh-bar"> |
| | | <el-switch v-model="autoRefresh" active-text="自动刷新" inactive-text="关闭"></el-switch> |
| | | <span>每5秒刷新一次</span> |
| | | <el-button size="mini" type="primary" icon="el-icon-refresh" @click="loadData" |
| | | :loading="loading">手动刷新</el-button> |
| | | </div> |
| | | |
| | | <!-- 表格区域 --> |
| | | <div class="header-section"> |
| | | <span class="title">连接列表</span> |
| | | <span style="color: #909399; font-size: 13px;">最后刷新: {{ lastRefreshTime || '-' }}</span> |
| | | </div> |
| | | |
| | | <el-table :data="pagedData" border stripe v-loading="loading" |
| | | :header-cell-style="{ background: '#f5f7fa', color: '#606266' }" style="width: 100%;"> |
| | | <el-table-column type="index" label="序号" width="60" align="center" |
| | | :index="indexMethod"></el-table-column> |
| | | <el-table-column prop="ip" label="客户端IP" min-width="140" align="center"></el-table-column> |
| | | <el-table-column prop="deviceName" label="设备名称" min-width="180"> |
| | | <template slot-scope="scope"> |
| | | {{ scope.row.deviceName || '-' }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="sessionId" label="会话ID" min-width="200" show-overflow-tooltip> |
| | | <template slot-scope="scope"> |
| | | {{ scope.row.sessionId || '-' }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="open" label="状态" width="100" align="center"> |
| | | <template slot-scope="scope"> |
| | | <span> |
| | | <span class="status-dot" :class="scope.row.open ? 'status-online' : ''"></span> |
| | | {{ scope.row.open ? '活跃' : '已关闭' }} |
| | | </span> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- 分页 --> |
| | | <div class="pagination-section" v-if="connections.length > pageSize"> |
| | | <el-pagination background layout="total, prev, pager, next" :total="connections.length" |
| | | :page-size="pageSize" :current-page="currentPage" @current-change="handlePageChange"> |
| | | </el-pagination> |
| | | </div> |
| | | |
| | | <!-- 空状态 --> |
| | | <div v-if="!loading && connections.length === 0" |
| | | style="text-align: center; padding: 60px; color: #909399;"> |
| | | <i class="el-icon-warning-outline" style="font-size: 48px; margin-bottom: 15px;"></i> |
| | | <div>暂无WebSocket连接</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <script src="../../static/vue/js/vue.min.js"></script> |
| | | <script src="../../static/vue/element/element.js"></script> |
| | | <script src="../../static/js/jquery/jquery-3.3.1.min.js"></script> |
| | | <script src="../../static/js/common.js"></script> |
| | | <script> |
| | | new Vue({ |
| | | el: '#app', |
| | | data: { |
| | | connections: [], |
| | | total: 0, |
| | | loading: false, |
| | | autoRefresh: true, |
| | | refreshTimer: null, |
| | | lastRefreshTime: '', |
| | | currentPage: 1, |
| | | pageSize: 10 |
| | | }, |
| | | |
| | | computed: { |
| | | pagedData() { |
| | | var start = (this.currentPage - 1) * this.pageSize; |
| | | return this.connections.slice(start, start + this.pageSize); |
| | | } |
| | | }, |
| | | |
| | | created() { |
| | | this.loadData(); |
| | | this.startAutoRefresh(); |
| | | }, |
| | | |
| | | watch: { |
| | | autoRefresh: function (val) { |
| | | if (val) { |
| | | this.startAutoRefresh(); |
| | | } else { |
| | | this.stopAutoRefresh(); |
| | | } |
| | | } |
| | | }, |
| | | |
| | | methods: { |
| | | getHeaders() { |
| | | return { 'token': localStorage.getItem('token') }; |
| | | }, |
| | | |
| | | loadData() { |
| | | this.loading = true; |
| | | var self = this; |
| | | $.ajax({ |
| | | url: baseUrl + '/tvDevice/tvWebSocket/status/auth', |
| | | headers: this.getHeaders(), |
| | | success: function (res) { |
| | | self.loading = false; |
| | | if (res.code === 200) { |
| | | self.connections = res.data.connections || []; |
| | | self.total = res.data.total || 0; |
| | | self.lastRefreshTime = self.formatTime(new Date()); |
| | | } else if (res.code === 403) { |
| | | top.location.href = baseUrl + '/'; |
| | | } else { |
| | | self.$message.error(res.msg || '加载失败'); |
| | | } |
| | | }, |
| | | error: function () { |
| | | self.loading = false; |
| | | self.$message.error('请求失败'); |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | startAutoRefresh() { |
| | | this.stopAutoRefresh(); |
| | | this.refreshTimer = setInterval(this.loadData, 5000); |
| | | }, |
| | | |
| | | stopAutoRefresh() { |
| | | if (this.refreshTimer) { |
| | | clearInterval(this.refreshTimer); |
| | | this.refreshTimer = null; |
| | | } |
| | | }, |
| | | |
| | | handlePageChange(page) { |
| | | this.currentPage = page; |
| | | }, |
| | | |
| | | indexMethod(index) { |
| | | return (this.currentPage - 1) * this.pageSize + index + 1; |
| | | }, |
| | | |
| | | formatTime(date) { |
| | | var h = String(date.getHours()).padStart(2, '0'); |
| | | var m = String(date.getMinutes()).padStart(2, '0'); |
| | | var s = String(date.getSeconds()).padStart(2, '0'); |
| | | return h + ':' + m + ':' + s; |
| | | } |
| | | }, |
| | | |
| | | beforeDestroy() { |
| | | this.stopAutoRefresh(); |
| | | } |
| | | }); |
| | | </script> |
| | | </body> |
| | | |
| | | </html> |