ForcePilot/web/src/components/dashboard/AgentStatsComponent.vue
Wenjie Zhang e592c75662 style(dashboard): 更新图表调色盘和样式优化
- 修改 base.css 中的图表调色盘颜色,提升视觉效果
- 在多个组件中引入统一的颜色工具函数,确保颜色一致性
- 优化仪表板组件的样式,提升用户体验和可读性
- 移除冗余的颜色定义,简化代码结构
2025-10-07 13:04:14 +08:00

356 lines
8.5 KiB
Vue

<template>
<a-card title="AI智能体分析" :loading="loading" class="dashboard-card">
<!-- 智能体概览 -->
<div class="stats-overview">
<a-row :gutter="16">
<a-col :span="8">
<a-statistic
title="智能体总数"
:value="agentStats?.total_agents || 0"
:value-style="{ color: 'var(--chart-info)' }"
suffix="个"
/>
</a-col>
<a-col :span="8">
<a-statistic
title="总对话数"
:value="totalConversations"
:value-style="{ color: 'var(--chart-secondary)' }"
suffix="次"
/>
</a-col>
<a-col :span="8">
<a-statistic
title="工具调用总数"
:value="totalToolUsage"
:value-style="{ color: 'var(--chart-warning)' }"
suffix="次"
/>
</a-col>
</a-row>
</div>
<a-divider />
<!-- 图表区域 -->
<a-row :gutter="24">
<!-- 对话数和工具调用数分布 -->
<a-col :span="24">
<div class="chart-container">
<h4>智能体对话数与工具调用数分布</h4>
<div ref="conversationToolChartRef" class="chart"></div>
</div>
</a-col>
</a-row>
<!-- 表现排行榜 -->
<a-divider />
<div class="top-performers">
<h4>表现最佳智能体 TOP 5</h4>
<a-table
:columns="performerColumns"
:data-source="topPerformers"
size="small"
:pagination="false"
>
<template #bodyCell="{ column, record, index }">
<template v-if="column.key === 'rank'">
<div class="rank-display">
<span v-if="index < 3" class="rank-medal">
{{ index === 0 ? '🥇' : index === 1 ? '🥈' : '🥉' }}
</span>
<span v-else class="rank-number">{{ index + 1 }}</span>
</div>
</template>
<template v-if="column.key === 'agent_id'">
<a-tag color="blue">{{ record.agent_id }}</a-tag>
</template>
<template v-if="column.key === 'score'">
<a-progress
:percent="record.score"
size="small"
:stroke-color="getScoreColor(record.score)"
:show-info="true"
:format="(percent) => `${percent}分`"
/>
</template>
<template v-if="column.key === 'satisfaction_rate'">
<a-statistic
:value="record.satisfaction_rate"
suffix="%"
:value-style="{
color: record.satisfaction_rate >= 80 ? 'var(--chart-success)' :
record.satisfaction_rate >= 60 ? 'var(--chart-warning)' : 'var(--chart-error)',
fontSize: '14px'
}"
/>
</template>
<template v-if="column.key === 'conversation_count'">
<span class="metric-value">{{ record.conversation_count }}</span>
</template>
</template>
</a-table>
</div>
</a-card>
</template>
<script setup>
import { ref, onMounted, watch, nextTick, computed } from 'vue'
import * as echarts from 'echarts'
import { getColorByIndex, getChartColor } from '@/utils/chartColors'
// Props
const props = defineProps({
agentStats: {
type: Object,
default: () => ({})
},
loading: {
type: Boolean,
default: false
}
})
// Chart refs
const conversationToolChartRef = ref(null)
let conversationToolChart = null
// 表格列定义
const performerColumns = [
{
title: '排名',
key: 'rank',
width: '80px',
align: 'center'
},
{
title: '智能体ID',
key: 'agent_id',
width: '25%'
},
{
title: '综合评分',
key: 'score',
width: '20%'
},
{
title: '满意度',
key: 'satisfaction_rate',
width: '20%',
align: 'center'
},
{
title: '对话数',
key: 'conversation_count',
width: '15%',
align: 'center'
}
]
// 计算属性
const totalConversations = computed(() => {
const conversationCounts = props.agentStats?.agent_conversation_counts || []
return conversationCounts.reduce((sum, item) => sum + item.conversation_count, 0)
})
const totalToolUsage = computed(() => {
const toolUsage = props.agentStats?.agent_tool_usage || []
return toolUsage.reduce((sum, item) => sum + item.tool_usage_count, 0)
})
const topPerformers = computed(() => {
return props.agentStats?.top_performing_agents || []
})
// 颜色辅助函数
const getScoreColor = (score) => {
if (score >= 80) return getChartColor('primary')
if (score >= 60) return getChartColor('warning')
return getChartColor('accent')
}
// 初始化对话数和工具调用数合并图表
const initConversationToolChart = () => {
if (!conversationToolChartRef.value ||
(!props.agentStats?.agent_conversation_counts?.length &&
!props.agentStats?.agent_tool_usage?.length)) return
conversationToolChart = echarts.init(conversationToolChartRef.value)
const conversationData = props.agentStats.agent_conversation_counts || []
const toolData = props.agentStats.agent_tool_usage || []
// 获取所有智能体ID
const allAgentIds = [...new Set([
...conversationData.map(item => item.agent_id),
...toolData.map(item => item.agent_id)
])]
const option = {
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(255, 255, 255, 0.95)',
borderColor: '#e8e8e8',
borderWidth: 1,
textStyle: {
color: '#666'
}
},
legend: {
data: ['对话数', '工具调用数'],
right: '0%',
top: '0%',
orient: 'horizontal',
textStyle: {
color: '#666'
}
},
grid: {
left: '3%',
right: '15%',
bottom: '3%',
top: '10%',
containLabel: true
},
xAxis: {
type: 'category',
data: allAgentIds,
axisLine: {
lineStyle: {
color: '#e8e8e8'
}
},
axisLabel: {
color: '#666',
interval: 0,
rotate: 45
}
},
yAxis: {
type: 'value',
axisLine: {
lineStyle: {
color: '#e8e8e8'
}
},
axisLabel: {
color: '#666'
},
splitLine: {
lineStyle: {
color: '#f0f0f0'
}
}
},
series: [
{
name: '对话数',
type: 'bar',
data: allAgentIds.map(agentId => {
const item = conversationData.find(d => d.agent_id === agentId)
return item ? item.conversation_count : 0
}),
itemStyle: {
color: getChartColor('primary'),
borderRadius: [4, 4, 0, 0]
},
emphasis: {
itemStyle: {
color: getChartColor('primary'),
shadowBlur: 10,
shadowColor: 'rgba(2, 142, 160, 0.3)'
}
}
},
{
name: '工具调用数',
type: 'bar',
data: allAgentIds.map(agentId => {
const item = toolData.find(d => d.agent_id === agentId)
return item ? item.tool_usage_count : 0
}),
itemStyle: {
color: getChartColor('accent'),
borderRadius: [4, 4, 0, 0]
},
emphasis: {
itemStyle: {
color: getChartColor('primary'),
shadowBlur: 10,
shadowColor: 'rgba(2, 142, 160, 0.3)'
}
}
}
]
}
conversationToolChart.setOption(option)
}
// 更新图表
const updateCharts = () => {
nextTick(() => {
initConversationToolChart()
})
}
// 监听数据变化
watch(() => props.agentStats, () => {
updateCharts()
}, { deep: true })
// 窗口大小变化时重新调整图表
const handleResize = () => {
if (conversationToolChart) conversationToolChart.resize()
}
onMounted(() => {
updateCharts()
window.addEventListener('resize', handleResize)
})
// 组件卸载时清理
const cleanup = () => {
window.removeEventListener('resize', handleResize)
if (conversationToolChart) {
conversationToolChart.dispose()
conversationToolChart = null
}
}
// 导出清理函数供父组件调用
defineExpose({
cleanup
})
</script>
<style scoped lang="less">
// AgentStatsComponent 特有的样式
.top-performers, .metrics-comparison {
h4 {
margin-bottom: 16px;
font-weight: 600;
color: var(--gray-1000);
font-size: 16px;
}
h5 {
margin-bottom: 12px;
color: var(--gray-600);
font-weight: 500;
font-size: 13px;
text-transform: uppercase;
letter-spacing: 0.05em;
}
}
:deep(.ant-progress-bg) {
transition: all 0.3s ease;
}
</style>