103 lines
5.4 KiB
Vue
103 lines
5.4 KiB
Vue
<script setup lang="ts">
|
||
import VChart from "vue-echarts";
|
||
import { use } from "echarts/core";
|
||
import { CanvasRenderer } from "echarts/renderers";
|
||
import { BarChart, GaugeChart, LineChart, PieChart } from "echarts/charts";
|
||
import { GridComponent, LegendComponent, TooltipComponent } from "echarts/components";
|
||
import { Activity, PauseCircle, PlayCircle } from "lucide-vue-next";
|
||
|
||
defineProps<{ ctx: any }>();
|
||
|
||
use([CanvasRenderer, LineChart, PieChart, BarChart, GaugeChart, GridComponent, TooltipComponent, LegendComponent]);
|
||
</script>
|
||
|
||
<template>
|
||
<section class="page-stack">
|
||
<div class="metric-grid">
|
||
<article class="metric"><span>反馈总数</span><strong>{{ ctx.kpis.feedbackTotal || 0 }}</strong><small>今日新增 {{ ctx.kpis.feedbackToday || 0 }}</small></article>
|
||
<article class="metric"><span>可见接口</span><strong>{{ ctx.kpis.sourceVisible || 0 }}</strong><small>接口总数 {{ ctx.kpis.sourceTotal || 0 }}</small></article>
|
||
<article class="metric"><span>版本日志</span><strong>{{ ctx.kpis.releaseNotices || 0 }}</strong><small>{{ ctx.latestNotice?.version || "暂无最新版本" }}</small></article>
|
||
<article class="metric"><span>邮件失败</span><strong>{{ ctx.kpis.mailFailed || 0 }}</strong><small>旧反馈兼容记录</small></article>
|
||
</div>
|
||
|
||
<div class="toolbar">
|
||
<button class="btn primary" @click="ctx.checkSources"><Activity :size="16" />立即心跳检测</button>
|
||
<button class="btn ghost" @click="ctx.toggleAutoRefresh">
|
||
<component :is="ctx.autoRefreshPaused ? PlayCircle : PauseCircle" :size="16" />
|
||
{{ ctx.autoRefreshPaused ? "恢复自动刷新" : "暂停自动刷新" }}
|
||
</button>
|
||
<span class="muted">每 15 秒自动刷新仪表盘数据。</span>
|
||
</div>
|
||
|
||
<section v-if="ctx.sourceCheckJobs.length" class="panel">
|
||
<div class="section-head"><h2>心跳检测任务</h2><span class="badge">{{ ctx.sourceCheckJobs[0].status }}</span></div>
|
||
<table>
|
||
<thead><tr><th>任务</th><th>进度</th><th>正常</th><th>重定向</th><th>降级</th><th>错误</th><th>开始时间</th></tr></thead>
|
||
<tbody>
|
||
<tr v-for="job in ctx.sourceCheckJobs.slice(0, 5)" :key="job.id">
|
||
<td class="mono">{{ job.id }}</td>
|
||
<td>{{ job.checked || 0 }} / {{ job.total || 0 }}</td>
|
||
<td>{{ job.stats?.ok || 0 }}</td>
|
||
<td>{{ job.stats?.redirected || 0 }}</td>
|
||
<td>{{ job.stats?.degraded || 0 }}</td>
|
||
<td>{{ job.stats?.error || 0 }}</td>
|
||
<td>{{ job.startedAt || "-" }}</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</section>
|
||
|
||
<section class="panel quick-panel">
|
||
<div class="section-head"><h2>功能总览</h2><span class="badge">{{ ctx.quickActions.length }} 个入口</span></div>
|
||
<div class="quick-grid">
|
||
<button v-for="item in ctx.quickActions" :key="item.path" @click="ctx.navigate(item.path)">
|
||
<component :is="item.icon" :size="18" />
|
||
<strong>{{ item.label }}</strong>
|
||
<span>{{ item.description }}</span>
|
||
</button>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="chart-grid">
|
||
<section class="panel chart-panel"><h2>接口心跳延迟</h2><VChart class="chart" :option="ctx.heartbeatOption" autoresize /></section>
|
||
<section class="panel chart-panel"><h2>接口健康分布</h2><VChart class="chart" :option="ctx.healthOption" autoresize /></section>
|
||
<section class="panel chart-panel"><h2>反馈状态分布</h2><VChart class="chart" :option="ctx.feedbackOption" autoresize /></section>
|
||
<section class="panel chart-panel"><h2>服务可用率</h2><VChart class="chart" :option="ctx.availabilityOption" autoresize /></section>
|
||
</div>
|
||
|
||
<section class="panel">
|
||
<div class="section-head"><h2>最近接口心跳</h2><span class="badge">{{ ctx.heartbeats.length }} 条</span></div>
|
||
<table>
|
||
<thead><tr><th>接口</th><th>状态</th><th>延迟</th><th>错误</th><th>时间</th></tr></thead>
|
||
<tbody>
|
||
<tr v-for="item in ctx.heartbeats.slice(0, 10)" :key="item.id">
|
||
<td>{{ item.name || item.sourceId }}</td>
|
||
<td><span :class="['badge', ctx.statusTone(item.status)]">{{ ctx.labelStatus(item.status) }}</span></td>
|
||
<td>{{ item.latencyMs || 0 }}ms</td>
|
||
<td class="hash">{{ item.error || "-" }}</td>
|
||
<td>{{ item.checkedAt || "-" }}</td>
|
||
</tr>
|
||
<tr v-if="ctx.heartbeats.length === 0"><td colspan="5">暂无心跳记录,点击“立即心跳检测”后会刷新。</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</section>
|
||
|
||
<section class="panel">
|
||
<div class="section-head"><h2>客户端调用上报</h2><span class="badge">{{ ctx.clientCalls.length }} 条</span></div>
|
||
<table>
|
||
<thead><tr><th>接口</th><th>状态</th><th>延迟</th><th>客户端</th><th>时间</th></tr></thead>
|
||
<tbody>
|
||
<tr v-for="item in ctx.clientCalls.slice(0, 8)" :key="item.id">
|
||
<td>{{ item.sourceId }}</td>
|
||
<td><span :class="['badge', ctx.statusTone(item.status)]">{{ ctx.labelStatus(item.status) }}</span></td>
|
||
<td>{{ item.latencyMs || 0 }}ms</td>
|
||
<td class="hash">{{ item.client || "-" }}</td>
|
||
<td>{{ item.createdAt || "-" }}</td>
|
||
</tr>
|
||
<tr v-if="ctx.clientCalls.length === 0"><td colspan="5">暂无客户端调用上报。</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</section>
|
||
</section>
|
||
</template>
|