refactor(frontend): 复用 TokenUsageTrend 组件优化用户 Dashboard 图表
用户 Dashboard 的 Token 使用趋势图表现在显示 Input/Output/Cache 三种类型, 并在 Tooltip 中显示 Actual 和 Standard 价格,与管理员页面保持一致。
This commit is contained in:
@@ -55,16 +55,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Token Usage Trend Chart -->
|
<!-- Token Usage Trend Chart -->
|
||||||
<div class="card relative overflow-hidden p-4">
|
<TokenUsageTrend :trend-data="trend" :loading="loading" />
|
||||||
<div v-if="loading" class="absolute inset-0 z-10 flex items-center justify-center bg-white/50 backdrop-blur-sm dark:bg-dark-800/50">
|
|
||||||
<LoadingSpinner size="md" />
|
|
||||||
</div>
|
|
||||||
<h3 class="mb-4 text-sm font-semibold text-gray-900 dark:text-white">{{ t('dashboard.tokenUsageTrend') }}</h3>
|
|
||||||
<div class="h-48">
|
|
||||||
<Line v-if="trendData" :data="trendData" :options="lineOptions" />
|
|
||||||
<div v-else class="flex h-full items-center justify-center text-sm text-gray-500 dark:text-gray-400">{{ t('dashboard.noDataAvailable') }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -75,7 +66,8 @@ import { useI18n } from 'vue-i18n'
|
|||||||
import LoadingSpinner from '@/components/common/LoadingSpinner.vue'
|
import LoadingSpinner from '@/components/common/LoadingSpinner.vue'
|
||||||
import DateRangePicker from '@/components/common/DateRangePicker.vue'
|
import DateRangePicker from '@/components/common/DateRangePicker.vue'
|
||||||
import Select from '@/components/common/Select.vue'
|
import Select from '@/components/common/Select.vue'
|
||||||
import { Line, Doughnut } from 'vue-chartjs'
|
import { Doughnut } from 'vue-chartjs'
|
||||||
|
import TokenUsageTrend from '@/components/charts/TokenUsageTrend.vue'
|
||||||
import type { TrendDataPoint, ModelStat } from '@/types'
|
import type { TrendDataPoint, ModelStat } from '@/types'
|
||||||
import { formatCostFixed as formatCost, formatNumberLocaleString as formatNumber, formatTokensK as formatTokens } from '@/utils/format'
|
import { formatCostFixed as formatCost, formatNumberLocaleString as formatNumber, formatTokensK as formatTokens } from '@/utils/format'
|
||||||
import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, ArcElement, Title, Tooltip, Legend, Filler } from 'chart.js'
|
import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, ArcElement, Title, Tooltip, Legend, Filler } from 'chart.js'
|
||||||
@@ -93,28 +85,6 @@ const modelData = computed(() => !props.models?.length ? null : {
|
|||||||
}]
|
}]
|
||||||
})
|
})
|
||||||
|
|
||||||
const trendData = computed(() => !props.trend?.length ? null : {
|
|
||||||
labels: props.trend.map((d: TrendDataPoint) => d.date),
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: t('dashboard.input'),
|
|
||||||
data: props.trend.map((d: TrendDataPoint) => d.input_tokens),
|
|
||||||
borderColor: '#3b82f6',
|
|
||||||
backgroundColor: 'rgba(59, 130, 246, 0.1)',
|
|
||||||
tension: 0.3,
|
|
||||||
fill: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('dashboard.output'),
|
|
||||||
data: props.trend.map((d: TrendDataPoint) => d.output_tokens),
|
|
||||||
borderColor: '#10b981',
|
|
||||||
backgroundColor: 'rgba(16, 185, 129, 0.1)',
|
|
||||||
tension: 0.3,
|
|
||||||
fill: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const doughnutOptions = {
|
const doughnutOptions = {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
@@ -127,25 +97,4 @@ const doughnutOptions = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const lineOptions = {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
plugins: {
|
|
||||||
legend: { display: true, position: 'top' as const },
|
|
||||||
tooltip: {
|
|
||||||
callbacks: {
|
|
||||||
label: (context: any) => `${context.dataset.label}: ${formatTokens(context.parsed.y)} tokens`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
beginAtZero: true,
|
|
||||||
ticks: {
|
|
||||||
callback: (value: any) => formatTokens(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user