| | |
| | | <body> |
| | | <div id="app" v-cloak class="page-shell"> |
| | | <div class="page-head"> |
| | | <div class="page-title">设备网络分析</div> |
| | | <div class="page-meta">包大小 {{ formatPacketSize(samplingConfig.packetSize) }},{{ samplingConfigText }}</div> |
| | | <div class="page-title">{{ i18n('devicePingLog.title', '设备网络分析') }}</div> |
| | | <div class="page-meta">{{ i18n('devicePingLog.pageMeta', '包大小 {0},{1}', [formatPacketSize(samplingConfig.packetSize), samplingConfigText]) }}</div> |
| | | </div> |
| | | |
| | | <section class="summary-grid"> |
| | | <div class="summary-card"> |
| | | <div class="summary-label">设备总数</div> |
| | | <div class="summary-value">{{ overviewSummary.totalDevices }}</div> |
| | | <div class="summary-sub">已配置 IP 的设备</div> |
| | | <div class="summary-label">{{ i18n('devicePingLog.summary.totalDevices', '设备总数') }}</div> |
| | | <div class="summary-value">{{ formatNumber(overviewSummary.totalDevices) }}</div> |
| | | <div class="summary-sub">{{ i18n('devicePingLog.summary.totalDevicesHint', '已配置 IP 的设备') }}</div> |
| | | </div> |
| | | <div class="summary-card"> |
| | | <div class="summary-label">正常</div> |
| | | <div class="summary-value">{{ overviewSummary.okDevices }}</div> |
| | | <div class="summary-sub">最近样本状态 OK</div> |
| | | <div class="summary-label">{{ i18n('devicePingLog.summary.ok', '正常') }}</div> |
| | | <div class="summary-value">{{ formatNumber(overviewSummary.okDevices) }}</div> |
| | | <div class="summary-sub">{{ i18n('devicePingLog.summary.okHint', '最近样本状态 OK') }}</div> |
| | | </div> |
| | | <div class="summary-card"> |
| | | <div class="summary-label">波动</div> |
| | | <div class="summary-value">{{ overviewSummary.unstableDevices }}</div> |
| | | <div class="summary-sub">部分探测成功</div> |
| | | <div class="summary-label">{{ i18n('devicePingLog.summary.unstable', '波动') }}</div> |
| | | <div class="summary-value">{{ formatNumber(overviewSummary.unstableDevices) }}</div> |
| | | <div class="summary-sub">{{ i18n('devicePingLog.summary.unstableHint', '部分探测成功') }}</div> |
| | | </div> |
| | | <div class="summary-card"> |
| | | <div class="summary-label">超时/异常</div> |
| | | <div class="summary-value">{{ overviewSummary.offlineDevices }}</div> |
| | | <div class="summary-sub">最近样本不可达</div> |
| | | <div class="summary-label">{{ i18n('devicePingLog.summary.offline', '超时/异常') }}</div> |
| | | <div class="summary-value">{{ formatNumber(overviewSummary.offlineDevices) }}</div> |
| | | <div class="summary-sub">{{ i18n('devicePingLog.summary.offlineHint', '最近样本不可达') }}</div> |
| | | </div> |
| | | <div class="summary-card"> |
| | | <div class="summary-label">暂无数据</div> |
| | | <div class="summary-value">{{ overviewSummary.noDataDevices }}</div> |
| | | <div class="summary-sub">还没有落盘样本</div> |
| | | <div class="summary-label">{{ i18n('devicePingLog.summary.noData', '暂无数据') }}</div> |
| | | <div class="summary-value">{{ formatNumber(overviewSummary.noDataDevices) }}</div> |
| | | <div class="summary-sub">{{ i18n('devicePingLog.summary.noDataHint', '还没有落盘样本') }}</div> |
| | | </div> |
| | | <div class="summary-card"> |
| | | <div class="summary-label">整体平均延迟</div> |
| | | <div class="summary-label">{{ i18n('devicePingLog.summary.avgLatency', '整体平均延迟') }}</div> |
| | | <div class="summary-value">{{ formatLatency(overviewSummary.avgLatencyMs) }}</div> |
| | | <div class="summary-sub">峰值 {{ formatLatency(overviewSummary.maxLatencyMs) }}</div> |
| | | <div class="summary-sub">{{ i18n('devicePingLog.summary.peakLatency', '峰值 {0}', [formatLatency(overviewSummary.maxLatencyMs)]) }}</div> |
| | | </div> |
| | | </section> |
| | | |
| | | <section class="panel"> |
| | | <div class="panel-head"> |
| | | <div class="panel-title">设备总览</div> |
| | | <el-button size="small" :loading="overviewLoading" @click="loadOverview">刷新</el-button> |
| | | <div class="panel-title">{{ i18n('devicePingLog.overviewTitle', '设备总览') }}</div> |
| | | <el-button size="small" :loading="overviewLoading" @click="loadOverview">{{ i18n('devicePingLog.refresh', '刷新') }}</el-button> |
| | | </div> |
| | | <div class="panel-body"> |
| | | <div class="toolbar"> |
| | | <el-form :inline="true" size="small" :model="overviewFilters" style="margin-bottom: -18px;"> |
| | | <el-form-item label="设备类型"> |
| | | <el-form-item :label="i18n('devicePingLog.filter.deviceType', '设备类型')"> |
| | | <el-select v-model="overviewFilters.deviceType" clearable style="width: 120px;"> |
| | | <el-option label="全部" value=""></el-option> |
| | | <el-option :label="i18n('devicePingLog.filter.all', '全部')" value=""></el-option> |
| | | <el-option label="Crn" value="Crn"></el-option> |
| | | <el-option label="DualCrn" value="DualCrn"></el-option> |
| | | <el-option label="Devp" value="Devp"></el-option> |
| | | <el-option label="Rgv" value="Rgv"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="关键字"> |
| | | <el-input v-model.trim="overviewFilters.keyword" clearable style="width: 220px;" placeholder="设备号 / IP"></el-input> |
| | | <el-form-item :label="i18n('devicePingLog.filter.keyword', '关键字')"> |
| | | <el-input v-model.trim="overviewFilters.keyword" clearable style="width: 220px;" :placeholder="i18n('devicePingLog.filter.keywordPlaceholder', '设备号 / IP')"></el-input> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div class="toolbar-right"> |
| | | <span class="page-meta">总览设备 {{ filteredOverviewRows.length }} 台</span> |
| | | <span class="page-meta">{{ i18n('devicePingLog.overviewCount', '总览设备 {0} 台', [formatNumber(filteredOverviewRows.length)]) }}</span> |
| | | </div> |
| | | </div> |
| | | |
| | | <el-table :data="filteredOverviewRows" border stripe size="mini" v-loading="overviewLoading" style="width: 100%;"> |
| | | <el-table-column label="设备" min-width="170"> |
| | | <el-table-column :label="i18n('devicePingLog.column.device', '设备')" min-width="170"> |
| | | <template slot-scope="scope"> |
| | | {{ scope.row.deviceType }}-{{ scope.row.deviceNo }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="ip" label="IP" min-width="150"></el-table-column> |
| | | <el-table-column label="状态" width="110" align="center"> |
| | | <el-table-column :label="i18n('devicePingLog.column.status', '状态')" width="110" align="center"> |
| | | <template slot-scope="scope"> |
| | | <span class="status-chip" :class="statusClass(scope.row.status)">{{ scope.row.statusText }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="成功率" width="90" align="right"> |
| | | <el-table-column :label="i18n('devicePingLog.column.successRate', '成功率')" width="90" align="right"> |
| | | <template slot-scope="scope"> |
| | | {{ formatPercent(scope.row.successRate) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="平均" width="90" align="right"> |
| | | <el-table-column :label="i18n('devicePingLog.column.avg', '平均')" width="90" align="right"> |
| | | <template slot-scope="scope"> |
| | | {{ formatLatency(scope.row.avgLatencyMs) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="最小" width="90" align="right"> |
| | | <el-table-column :label="i18n('devicePingLog.column.min', '最小')" width="90" align="right"> |
| | | <template slot-scope="scope"> |
| | | {{ formatLatency(scope.row.minLatencyMs) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="最大" width="90" align="right"> |
| | | <el-table-column :label="i18n('devicePingLog.column.max', '最大')" width="90" align="right"> |
| | | <template slot-scope="scope"> |
| | | {{ formatLatency(scope.row.maxLatencyMs) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="latestTimeLabel" label="更新时间" width="170"></el-table-column> |
| | | <el-table-column prop="message" label="说明" min-width="200" show-overflow-tooltip></el-table-column> |
| | | <el-table-column label="操作" width="110" align="center" fixed="right"> |
| | | <el-table-column prop="latestTimeLabel" :label="i18n('devicePingLog.column.updateTime', '更新时间')" width="170"></el-table-column> |
| | | <el-table-column prop="message" :label="i18n('devicePingLog.column.message', '说明')" min-width="200" show-overflow-tooltip></el-table-column> |
| | | <el-table-column :label="i18n('devicePingLog.column.action', '操作')" width="110" align="center" fixed="right"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="mini" type="primary" plain @click="openDetail(scope.row)">查看详情</el-button> |
| | | <el-button size="mini" type="primary" plain @click="openDetail(scope.row)">{{ i18n('devicePingLog.viewDetail', '查看详情') }}</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | |
| | | |
| | | <section class="panel"> |
| | | <div class="panel-head"> |
| | | <div class="panel-title">设备详情</div> |
| | | <div class="panel-title">{{ i18n('devicePingLog.detailTitle', '设备详情') }}</div> |
| | | </div> |
| | | <div class="panel-body"> |
| | | <div v-if="!currentDevice" class="empty-shell">从上方设备总览选择一台设备查看秒级明细</div> |
| | | <div v-if="!currentDevice" class="empty-shell">{{ i18n('devicePingLog.detailEmpty', '从上方设备总览选择一台设备查看秒级明细') }}</div> |
| | | <div v-else class="detail-shell"> |
| | | <div class="detail-toolbar"> |
| | | <div class="detail-selected">{{ currentDevice.label }}</div> |
| | | <div class="toolbar-right"> |
| | | <el-form :inline="true" size="small" :model="detailFilters" style="margin-bottom: -18px;"> |
| | | <el-form-item label="时间范围"> |
| | | <el-form-item :label="i18n('devicePingLog.filter.timeRange', '时间范围')"> |
| | | <el-date-picker |
| | | v-model="detailFilters.range" |
| | | type="datetimerange" |
| | | unlink-panels |
| | | range-separator="至" |
| | | start-placeholder="开始" |
| | | end-placeholder="结束" |
| | | :range-separator="i18n('devicePingLog.filter.rangeSeparator', '至')" |
| | | :start-placeholder="i18n('devicePingLog.filter.start', '开始')" |
| | | :end-placeholder="i18n('devicePingLog.filter.end', '结束')" |
| | | style="width: 360px;"> |
| | | </el-date-picker> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button @click="setQuickRange(30)">30 分钟</el-button> |
| | | <el-button @click="setQuickRange(30)">{{ i18n('devicePingLog.quickRange.30m', '30 分钟') }}</el-button> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button @click="setQuickRange(60)">1 小时</el-button> |
| | | <el-button @click="setQuickRange(60)">{{ i18n('devicePingLog.quickRange.1h', '1 小时') }}</el-button> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button @click="setQuickRange(360)">6 小时</el-button> |
| | | <el-button @click="setQuickRange(360)">{{ i18n('devicePingLog.quickRange.6h', '6 小时') }}</el-button> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" :loading="detailLoading" @click="queryTrend">查询</el-button> |
| | | <el-button type="primary" :loading="detailLoading" @click="queryTrend">{{ i18n('devicePingLog.query', '查询') }}</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | |
| | | |
| | | <div class="detail-summary-grid"> |
| | | <div class="detail-card"> |
| | | <div class="detail-card-label">状态</div> |
| | | <div class="detail-card-label">{{ i18n('devicePingLog.detail.status', '状态') }}</div> |
| | | <div class="detail-card-value">{{ detailSummary.latestStatus || '--' }}</div> |
| | | </div> |
| | | <div class="detail-card"> |
| | | <div class="detail-card-label">包大小</div> |
| | | <div class="detail-card-label">{{ i18n('devicePingLog.detail.packetSize', '包大小') }}</div> |
| | | <div class="detail-card-value">{{ formatPacketSize(detailSummary.packetSize) }}</div> |
| | | </div> |
| | | <div class="detail-card"> |
| | | <div class="detail-card-label">成功率</div> |
| | | <div class="detail-card-label">{{ i18n('devicePingLog.detail.successRate', '成功率') }}</div> |
| | | <div class="detail-card-value">{{ formatPercent(detailSummary.successRate) }}</div> |
| | | </div> |
| | | <div class="detail-card"> |
| | | <div class="detail-card-label">平均</div> |
| | | <div class="detail-card-label">{{ i18n('devicePingLog.detail.avg', '平均') }}</div> |
| | | <div class="detail-card-value">{{ formatLatency(detailSummary.avgLatencyMs) }}</div> |
| | | </div> |
| | | <div class="detail-card"> |
| | | <div class="detail-card-label">最小</div> |
| | | <div class="detail-card-label">{{ i18n('devicePingLog.detail.min', '最小') }}</div> |
| | | <div class="detail-card-value">{{ formatLatency(detailSummary.minLatencyMs) }}</div> |
| | | </div> |
| | | <div class="detail-card"> |
| | | <div class="detail-card-label">最大</div> |
| | | <div class="detail-card-label">{{ i18n('devicePingLog.detail.max', '最大') }}</div> |
| | | <div class="detail-card-value">{{ formatLatency(detailSummary.maxLatencyMs) }}</div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="chart-grid"> |
| | | <div class="chart-card"> |
| | | <div class="chart-title">延迟</div> |
| | | <div v-if="!series.length && !detailLoading" class="empty-shell">当前范围暂无秒级样本</div> |
| | | <div class="chart-title">{{ i18n('devicePingLog.chart.latencyTitle', '延迟') }}</div> |
| | | <div v-if="!series.length && !detailLoading" class="empty-shell">{{ i18n('devicePingLog.chart.empty', '当前范围暂无秒级样本') }}</div> |
| | | <div v-show="series.length || detailLoading" ref="latencyChart" class="chart-box"></div> |
| | | </div> |
| | | <div class="chart-card"> |
| | | <div class="chart-title">成功率 / 失败次数</div> |
| | | <div v-if="!series.length && !detailLoading" class="empty-shell">当前范围暂无秒级样本</div> |
| | | <div class="chart-title">{{ i18n('devicePingLog.chart.availabilityTitle', '成功率 / 失败次数') }}</div> |
| | | <div v-if="!series.length && !detailLoading" class="empty-shell">{{ i18n('devicePingLog.chart.empty', '当前范围暂无秒级样本') }}</div> |
| | | <div v-show="series.length || detailLoading" ref="availabilityChart" class="chart-box"></div> |
| | | </div> |
| | | </div> |
| | | |
| | | <el-table :data="alerts" border stripe size="mini" style="width: 100%;"> |
| | | <el-table-column prop="timeLabel" label="时间" width="180"></el-table-column> |
| | | <el-table-column prop="status" label="状态" width="110"></el-table-column> |
| | | <el-table-column prop="timeLabel" :label="i18n('devicePingLog.alertColumn.time', '时间')" width="180"></el-table-column> |
| | | <el-table-column prop="status" :label="i18n('devicePingLog.alertColumn.status', '状态')" width="110"></el-table-column> |
| | | <el-table-column prop="ip" label="IP" width="150"></el-table-column> |
| | | <el-table-column prop="message" label="说明" min-width="240" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="message" :label="i18n('devicePingLog.alertColumn.message', '说明')" min-width="240" show-overflow-tooltip></el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | |
| | | <script type="text/javascript" src="../../static/vue/js/vue.min.js"></script> |
| | | <script type="text/javascript" src="../../static/vue/element/element.js"></script> |
| | | <script type="text/javascript" src="../../static/js/echarts/echarts.min.js"></script> |
| | | <script type="text/javascript" src="../../static/js/devicePingLog/devicePingLog.js?v=20260316_device_ping_packet_size_detail" charset="utf-8"></script> |
| | | <script type="text/javascript" src="../../static/js/devicePingLog/devicePingLog.js?v=20260317_device_ping_i18n" charset="utf-8"></script> |
| | | </body> |
| | | </html> |