fix: 优化token图表,增加全屏显示
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import { PieChart } from '@element-plus/icons-vue';
|
||||
import { FullScreen, PieChart } from '@element-plus/icons-vue';
|
||||
import { useElementSize } from '@vueuse/core';
|
||||
import { BarChart, PieChart as EPieChart, LineChart } from 'echarts/charts';
|
||||
import {
|
||||
GraphicComponent,
|
||||
@@ -8,7 +9,6 @@ import {
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
} from 'echarts/components';
|
||||
// 按需引入 ECharts
|
||||
import * as echarts from 'echarts/core';
|
||||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
import { getLast7DaysTokenUsage, getModelTokenUsage } from '@/api';
|
||||
@@ -34,48 +34,20 @@ let lineChartInstance: any = null;
|
||||
let pieChartInstance: any = null;
|
||||
let barChartInstance: any = null;
|
||||
|
||||
// 全屏状态
|
||||
const isFullscreen = ref(false);
|
||||
const usageStatisticsRef = ref<HTMLElement>();
|
||||
|
||||
// 容器尺寸监听
|
||||
const pieContainerSize = useElementSize(pieChart);
|
||||
const barContainerSize = useElementSize(barChart);
|
||||
|
||||
// 数据
|
||||
const dateRange = ref([
|
||||
new Date(new Date().setDate(new Date().getDate() - 30)),
|
||||
new Date(),
|
||||
]);
|
||||
const loading = ref(false);
|
||||
const totalTokens = ref(0);
|
||||
const usageData = ref<any[]>([]);
|
||||
const modelUsageData = ref<any[]>([]);
|
||||
|
||||
// 日期选择器选项
|
||||
const pickerOptions = {
|
||||
disabledDate(time: Date) {
|
||||
return time > new Date();
|
||||
},
|
||||
shortcuts: [{
|
||||
text: '最近一周',
|
||||
onClick(picker: any) {
|
||||
const end = new Date();
|
||||
const start = new Date();
|
||||
start.setDate(start.getDate() - 7);
|
||||
picker.$emit('pick', [start, end]);
|
||||
},
|
||||
}, {
|
||||
text: '最近一个月',
|
||||
onClick(picker: any) {
|
||||
const end = new Date();
|
||||
const start = new Date();
|
||||
start.setMonth(start.getMonth() - 1);
|
||||
picker.$emit('pick', [start, end]);
|
||||
},
|
||||
}, {
|
||||
text: '最近三个月',
|
||||
onClick(picker: any) {
|
||||
const end = new Date();
|
||||
const start = new Date();
|
||||
start.setMonth(start.getMonth() - 3);
|
||||
picker.$emit('pick', [start, end]);
|
||||
},
|
||||
}],
|
||||
};
|
||||
|
||||
// 获取用量数据
|
||||
async function fetchUsageData() {
|
||||
loading.value = true;
|
||||
@@ -149,7 +121,7 @@ function updateLineChart() {
|
||||
type: 'category',
|
||||
data: dates,
|
||||
axisLabel: {
|
||||
rotate: 45,
|
||||
// rotate: 45,
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
@@ -182,7 +154,7 @@ function updateLineChart() {
|
||||
lineChartInstance.setOption(option);
|
||||
}
|
||||
|
||||
// 更新饼图
|
||||
// 更新饼图 - 动态适应数据量
|
||||
function updatePieChart() {
|
||||
if (!pieChartInstance || modelUsageData.value.length === 0)
|
||||
return;
|
||||
@@ -192,15 +164,31 @@ function updatePieChart() {
|
||||
value: item.tokens,
|
||||
}));
|
||||
|
||||
const isManyItems = data.length > 8;
|
||||
const isSmallContainer = pieContainerSize.width.value < 600;
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b}: {c} tokens ({d}%)',
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
right: 10,
|
||||
top: 'center',
|
||||
orient: isManyItems ? 'vertical' : 'horizontal',
|
||||
right: isManyItems ? 10 : 'auto',
|
||||
bottom: isManyItems ? 0 : 10,
|
||||
type: isManyItems ? 'scroll' : 'plain',
|
||||
pageIconColor: '#3a4de9',
|
||||
pageIconInactiveColor: '#ccc',
|
||||
pageTextStyle: { color: '#333' },
|
||||
itemGap: isSmallContainer ? 5 : 10,
|
||||
itemWidth: isSmallContainer ? 15 : 25,
|
||||
itemHeight: isSmallContainer ? 10 : 14,
|
||||
textStyle: {
|
||||
fontSize: isSmallContainer ? 10 : 12,
|
||||
},
|
||||
formatter(name: string) {
|
||||
return name.length > 15 ? `${name.substring(0, 12)}...` : name;
|
||||
},
|
||||
data: data.map(item => item.name),
|
||||
},
|
||||
series: [
|
||||
@@ -208,6 +196,7 @@ function updatePieChart() {
|
||||
name: '模型用量',
|
||||
type: 'pie',
|
||||
radius: ['50%', '70%'],
|
||||
center: isManyItems ? ['40%', '50%'] : ['50%', '50%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
@@ -236,7 +225,7 @@ function updatePieChart() {
|
||||
pieChartInstance.setOption(option);
|
||||
}
|
||||
|
||||
// 更新柱状图
|
||||
// 更新柱状图 - 动态适应数据量
|
||||
function updateBarChart() {
|
||||
if (!barChartInstance || modelUsageData.value.length === 0)
|
||||
return;
|
||||
@@ -244,6 +233,9 @@ function updateBarChart() {
|
||||
const models = modelUsageData.value.map(item => item.model);
|
||||
const tokens = modelUsageData.value.map(item => item.tokens);
|
||||
|
||||
const isManyItems = models.length > 10;
|
||||
const isSmallContainer = barContainerSize.width.value < 600;
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
@@ -256,6 +248,7 @@ function updateBarChart() {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
top: '3%',
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
@@ -267,12 +260,20 @@ function updateBarChart() {
|
||||
data: models,
|
||||
axisLabel: {
|
||||
interval: 0,
|
||||
fontSize: Math.max(10, 14 - Math.floor(models.length / 10)),
|
||||
formatter(value: string) {
|
||||
if (isSmallContainer && value.length > 10) {
|
||||
return `${value.substring(0, 8)}...`;
|
||||
}
|
||||
return value;
|
||||
},
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '用量',
|
||||
type: 'bar',
|
||||
barWidth: isManyItems ? '60%' : '80%',
|
||||
data: tokens,
|
||||
itemStyle: {
|
||||
color(params: any) {
|
||||
@@ -311,6 +312,20 @@ function resizeCharts() {
|
||||
barChartInstance?.resize();
|
||||
}
|
||||
|
||||
// 切换全屏
|
||||
function toggleFullscreen() {
|
||||
isFullscreen.value = !isFullscreen.value;
|
||||
nextTick(() => {
|
||||
resizeCharts();
|
||||
});
|
||||
}
|
||||
|
||||
// 监听容器大小变化
|
||||
watch([pieContainerSize.width, barContainerSize.width], () => {
|
||||
updatePieChart();
|
||||
updateBarChart();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
initCharts();
|
||||
fetchUsageData();
|
||||
@@ -325,12 +340,23 @@ onBeforeUnmount(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="usage-statistics">
|
||||
<div
|
||||
ref="usageStatisticsRef"
|
||||
class="usage-statistics"
|
||||
:class="{ 'fullscreen-mode': isFullscreen }"
|
||||
>
|
||||
<div class="header">
|
||||
<h2>
|
||||
<el-icon><PieChart /></el-icon>
|
||||
Token用量统计
|
||||
</h2>
|
||||
<el-button
|
||||
:icon="FullScreen"
|
||||
circle
|
||||
plain
|
||||
size="small"
|
||||
@click="toggleFullscreen"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<el-card v-loading="loading" class="chart-card">
|
||||
@@ -354,7 +380,7 @@ onBeforeUnmount(() => {
|
||||
</div>
|
||||
</template>
|
||||
<div class="chart-container">
|
||||
<div ref="pieChart" class="chart" style="height: 400px;" />
|
||||
<div ref="pieChart" class="chart" style="height: 450px;" />
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
@@ -365,16 +391,29 @@ onBeforeUnmount(() => {
|
||||
</div>
|
||||
</template>
|
||||
<div class="chart-container">
|
||||
<div ref="barChart" class="chart" style="height: 400px;" />
|
||||
<div ref="barChart" class="chart" style="height: 500px;" />
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 样式保持不变 */
|
||||
.usage-statistics {
|
||||
padding: 20px;
|
||||
position: relative;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.usage-statistics.fullscreen-mode {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 2000;
|
||||
background: #fff;
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.header {
|
||||
@@ -421,6 +460,19 @@ onBeforeUnmount(() => {
|
||||
.header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.chart-card {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.chart {
|
||||
height: 350px !important;
|
||||
}
|
||||
|
||||
.usage-statistics.fullscreen-mode {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user