|
|
<template> |
|
|
<div class="glass-strong mb-8 rounded-3xl p-6"> |
|
|
<div class="mb-6 flex flex-col items-start justify-between gap-4 md:flex-row md:items-center"> |
|
|
<h2 class="flex items-center text-xl font-bold text-gray-800"> |
|
|
<i class="fas fa-chart-area mr-2 text-blue-500" /> |
|
|
使用趋势 |
|
|
</h2> |
|
|
|
|
|
<div class="flex items-center gap-3"> |
|
|
<el-radio-group v-model="granularity" size="small" @change="handleGranularityChange"> |
|
|
<el-radio-button label="day"> 按天 </el-radio-button> |
|
|
<el-radio-button label="hour"> 按小时 </el-radio-button> |
|
|
</el-radio-group> |
|
|
|
|
|
<el-select |
|
|
v-model="trendPeriod" |
|
|
size="small" |
|
|
style="width: 120px" |
|
|
@change="handlePeriodChange" |
|
|
> |
|
|
<el-option |
|
|
v-for="period in periodOptions" |
|
|
:key="period.days" |
|
|
:label="`最近${period.days}天`" |
|
|
:value="period.days" |
|
|
/> |
|
|
</el-select> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="relative" style="height: 300px"> |
|
|
<canvas ref="chartCanvas" /> |
|
|
</div> |
|
|
</div> |
|
|
</template> |
|
|
|
|
|
<script setup> |
|
|
import { ref, onMounted, onUnmounted, watch } from 'vue' |
|
|
import { Chart } from 'chart.js/auto' |
|
|
import { useDashboardStore } from '@/stores/dashboard' |
|
|
import { useChartConfig } from '@/composables/useChartConfig' |
|
|
|
|
|
const dashboardStore = useDashboardStore() |
|
|
const chartCanvas = ref(null) |
|
|
let chart = null |
|
|
|
|
|
const trendPeriod = ref(7) |
|
|
const granularity = ref('day') |
|
|
|
|
|
const periodOptions = [ |
|
|
{ days: 1, label: '24小时' }, |
|
|
{ days: 7, label: '7天' }, |
|
|
{ days: 30, label: '30天' } |
|
|
] |
|
|
|
|
|
const createChart = () => { |
|
|
if (!chartCanvas.value || !dashboardStore.trendData.length) return |
|
|
|
|
|
if (chart) { |
|
|
chart.destroy() |
|
|
} |
|
|
|
|
|
const { getGradient } = useChartConfig() |
|
|
const ctx = chartCanvas.value.getContext('2d') |
|
|
|
|
|
const labels = dashboardStore.trendData.map((item) => { |
|
|
if (granularity.value === 'hour') { |
|
|
// 小时粒度使用hour字段 |
|
|
const date = new Date(item.hour) |
|
|
const month = String(date.getMonth() + 1).padStart(2, '0') |
|
|
const day = String(date.getDate()).padStart(2, '0') |
|
|
const hour = String(date.getHours()).padStart(2, '0') |
|
|
return `${month}/${day} ${hour}:00` |
|
|
} |
|
|
return item.date |
|
|
}) |
|
|
|
|
|
chart = new Chart(ctx, { |
|
|
type: 'line', |
|
|
data: { |
|
|
labels, |
|
|
datasets: [ |
|
|
{ |
|
|
label: '请求次数', |
|
|
data: dashboardStore.trendData.map((item) => item.requests), |
|
|
borderColor: '#667eea', |
|
|
backgroundColor: getGradient(ctx, '#667eea', 0.1), |
|
|
yAxisID: 'y', |
|
|
tension: 0.4 |
|
|
}, |
|
|
{ |
|
|
label: 'Token使用量', |
|
|
data: dashboardStore.trendData.map((item) => item.tokens), |
|
|
borderColor: '#f093fb', |
|
|
backgroundColor: getGradient(ctx, '#f093fb', 0.1), |
|
|
yAxisID: 'y1', |
|
|
tension: 0.4 |
|
|
} |
|
|
] |
|
|
}, |
|
|
options: { |
|
|
responsive: true, |
|
|
maintainAspectRatio: false, |
|
|
interaction: { |
|
|
mode: 'index', |
|
|
intersect: false |
|
|
}, |
|
|
plugins: { |
|
|
legend: { |
|
|
position: 'top' |
|
|
}, |
|
|
tooltip: { |
|
|
mode: 'index', |
|
|
intersect: false |
|
|
} |
|
|
}, |
|
|
scales: { |
|
|
x: { |
|
|
display: true, |
|
|
grid: { |
|
|
display: false |
|
|
} |
|
|
}, |
|
|
y: { |
|
|
type: 'linear', |
|
|
display: true, |
|
|
position: 'left', |
|
|
title: { |
|
|
display: true, |
|
|
text: '请求次数' |
|
|
} |
|
|
}, |
|
|
y1: { |
|
|
type: 'linear', |
|
|
display: true, |
|
|
position: 'right', |
|
|
title: { |
|
|
display: true, |
|
|
text: 'Token使用量' |
|
|
}, |
|
|
grid: { |
|
|
drawOnChartArea: false |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
}) |
|
|
} |
|
|
|
|
|
const handlePeriodChange = async () => { |
|
|
await dashboardStore.loadUsageTrend(trendPeriod.value, granularity.value) |
|
|
createChart() |
|
|
} |
|
|
|
|
|
const handleGranularityChange = async () => { |
|
|
// 根据粒度调整时间范围 |
|
|
if (granularity.value === 'hour' && trendPeriod.value > 7) { |
|
|
trendPeriod.value = 1 |
|
|
} |
|
|
await dashboardStore.loadUsageTrend(trendPeriod.value, granularity.value) |
|
|
createChart() |
|
|
} |
|
|
|
|
|
watch( |
|
|
() => dashboardStore.trendData, |
|
|
() => { |
|
|
createChart() |
|
|
}, |
|
|
{ deep: true } |
|
|
) |
|
|
|
|
|
onMounted(() => { |
|
|
createChart() |
|
|
}) |
|
|
|
|
|
onUnmounted(() => { |
|
|
if (chart) { |
|
|
chart.destroy() |
|
|
} |
|
|
}) |
|
|
</script> |
|
|
|
|
|
<style scoped> |
|
|
|
|
|
</style> |
|
|
|