feat: 日志中心 + GRE协议支持 + 流量监控 + 部署脚本
- 新增日志中心功能 (api/logapi.go + Logs.vue) - 新增GRE协议服务支持 (service/gre/) - 新增流量监控组件 (TrafficChart.vue) - 新增L2TP/IPSec测试用例 - 新增日志轮转服务 (logrotate.go) - 新增部署脚本 (scripts/install.sh, uninstall.sh, service文件) - 更新i18n国际化文件 - 优化后端API和前端路由
This commit is contained in:
1 parent
7de1f70905
commit
a14d9e62e6
25 files changed
+2285
-25
No files matched your search
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<div>
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">
|
||||
<h4 style="margin:0">{{ $t('tunnels.trafficTrend') }}</h4>
|
||||
<el-radio-group v-model="hours" size="small" @change="load">
|
||||
<el-radio-button :value="1">1h</el-radio-button>
|
||||
<el-radio-button :value="6">6h</el-radio-button>
|
||||
<el-radio-button :value="24">24h</el-radio-button>
|
||||
<el-radio-button :value="168">7d</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div ref="chartRef" style="height:220px" v-loading="loading"></div>
|
||||
<el-empty v-if="!loading && samples.length === 0" :description="$t('tunnels.noTrafficData')" :image-size="60" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, onMounted, onBeforeUnmount, nextTick } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
import api from '../api'
|
||||
import { fmtBytes } from '../utils/format'
|
||||
|
||||
const props = defineProps({ tunnelId: { type: Number, required: true } })
|
||||
|
||||
const chartRef = ref(null)
|
||||
const loading = ref(false)
|
||||
const hours = ref(24)
|
||||
const samples = ref([])
|
||||
let chart = null
|
||||
|
||||
function granularity(h) {
|
||||
if (h <= 1) return 'minute'
|
||||
if (h <= 168) return 'hour'
|
||||
return 'day'
|
||||
}
|
||||
|
||||
async function load() {
|
||||
if (!props.tunnelId) return
|
||||
loading.value = true
|
||||
try {
|
||||
const { data } = await api.get(`/tunnels/${props.tunnelId}/traffic/history`, {
|
||||
params: { hours: hours.value, granularity: granularity(hours.value) }
|
||||
})
|
||||
samples.value = data.samples || []
|
||||
renderChart()
|
||||
} catch (e) {
|
||||
samples.value = []
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function renderChart() {
|
||||
if (!chartRef.value || samples.value.length === 0) return
|
||||
if (!chart) chart = echarts.init(chartRef.value)
|
||||
const times = samples.value.map(s => {
|
||||
const d = new Date(s.time)
|
||||
return hours.value <= 24 ? d.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
|
||||
: d.toLocaleDateString('zh-CN', { month: 'numeric', day: 'numeric', hour: '2-digit', minute: '2-digit' })
|
||||
})
|
||||
chart.setOption({
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter(params) {
|
||||
const t = params[0].axisValue
|
||||
const rx = params[0] ? fmtBytes(params[0].value) : '0'
|
||||
const tx = params[1] ? fmtBytes(params[1].value) : '0'
|
||||
return `${t}<br/>↓ RX: ${rx}<br/>↑ TX: ${tx}`
|
||||
}
|
||||
},
|
||||
legend: { data: ['RX', 'TX'], right: 0, top: 0, textStyle: { fontSize: 11 } },
|
||||
grid: { left: 50, right: 16, top: 30, bottom: 24 },
|
||||
xAxis: { type: 'category', data: times, axisLabel: { fontSize: 10 } },
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLabel: { fontSize: 10, formatter: v => fmtBytes(v) }
|
||||
},
|
||||
series: [
|
||||
{ name: 'RX', type: 'line', smooth: true, areaStyle: { opacity: 0.15 }, data: samples.value.map(s => s.delta_rx), lineStyle: { width: 2 } },
|
||||
{ name: 'TX', type: 'line', smooth: true, areaStyle: { opacity: 0.15 }, data: samples.value.map(s => s.delta_tx), lineStyle: { width: 2 } }
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
let resizeHandler = null
|
||||
onMounted(() => {
|
||||
load()
|
||||
resizeHandler = () => chart?.resize()
|
||||
window.addEventListener('resize', resizeHandler)
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', resizeHandler)
|
||||
chart?.dispose()
|
||||
})
|
||||
|
||||
watch(() => props.tunnelId, () => { hours.value = 24; load() })
|
||||
</script>
|
||||
Reference in new issue
Block a user