Files
abot/plugins/stats_dashboard/templates/index.html

386 lines
15 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}首页概览 - 机器人统计看板{% endblock %}
{% block content %}
<!-- 首页概览 -->
<div>
<el-row :gutter="20">
<el-col :span="4">
<el-card shadow="hover" class="stats-card">
<div slot="header">
<span>总调用次数</span>
</div>
<div style="font-size: 24px; text-align: center;">
{% raw %}{{ totalCalls }}{% endraw %}
</div>
</el-card>
</el-col>
<el-col :span="4">
<el-card shadow="hover" class="stats-card">
<div slot="header">
<span>成功率</span>
</div>
<div style="font-size: 24px; text-align: center;">
{% raw %}{{ successRate.toFixed(2) }}{% endraw %}%
</div>
</el-card>
</el-col>
<el-col :span="4">
<el-card shadow="hover" class="stats-card">
<div slot="header">
<span>活跃用户数</span>
</div>
<div style="font-size: 24px; text-align: center;">
{% raw %}{{ activeUsers }}{% endraw %}
</div>
</el-card>
</el-col>
<el-col :span="4">
<el-card shadow="hover" class="stats-card">
<div slot="header">
<span>活跃群组数</span>
</div>
<div style="font-size: 24px; text-align: center;">
{% raw %}{{ activeGroups }}{% endraw %}
</div>
</el-card>
</el-col>
<el-col :span="4">
<el-card shadow="hover" class="stats-card">
<div slot="header">
<span>平均响应时间</span>
</div>
<div style="font-size: 24px; text-align: center;">
{% raw %}{{ avgResponseTime.toFixed(2) }}{% endraw %} ms
</div>
</el-card>
</el-col>
</el-row>
<!-- 添加热门用户、群组和插件 -->
<el-row :gutter="20" style="margin-top: 20px;">
<el-col :span="8">
<el-card shadow="hover">
<div slot="header">
<span>热门用户</span>
</div>
<el-table :data="topUsers" style="width: 100%">
<!-- 修改将用户ID改为用户信息使用固定像素宽度 -->
<el-table-column label="用户信息" min-width="180">
<template slot-scope="scope">
{% raw %}{{ scope.row.user_name || scope.row.user_id }} ({{ scope.row.user_id }}){% endraw %}
</template>
</el-table-column>
<el-table-column prop="total_calls" label="调用次数" width="80" align="center"></el-table-column>
</el-table>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="hover">
<div slot="header">
<span>热门群组</span>
</div>
<el-table :data="topGroups" style="width: 100%">
<!-- 修改将群组ID改为群组信息使用固定像素宽度 -->
<el-table-column label="群组信息" min-width="180">
<template slot-scope="scope">
{% raw %}{{ scope.row.group_name || scope.row.group_id }} ({{ scope.row.group_id }}){% endraw %}
</template>
</el-table-column>
<el-table-column prop="total_calls" label="调用次数" width="80" align="center"></el-table-column>
</el-table>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="hover">
<div slot="header">
<span>热门插件</span>
</div>
<el-table :data="topPlugins" style="width: 100%">
<el-table-column prop="plugin_name" label="插件名称" min-width="180"></el-table-column>
<el-table-column prop="total_calls" label="调用次数" width="80" align="center"></el-table-column>
</el-table>
</el-card>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<div class="chart-container">
<h3>插件使用排行</h3>
<canvas id="pluginChart" width="400" height="200"></canvas>
</div>
</el-col>
<el-col :span="12">
<div class="chart-container">
<h3>成功率分析</h3>
<canvas id="successRateChart" width="400" height="200"></canvas>
</div>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<div class="chart-container">
<h3>使用趋势</h3>
<canvas id="trendChart" width="800" height="200"></canvas>
</div>
</el-col>
</el-row>
</div>
{% endblock %}
{% block scripts %}
<script>
new Vue({
el: '#app',
mixins: [baseApp],
data() {
return {
totalCalls: 0,
successRate: 0,
activeUsers: 0,
activeGroups: 0,
avgResponseTime: 0, // 确保这里初始化为数字
topUsers: [],
topGroups: [],
topPlugins: [],
pluginStats: [],
charts: {} // 添加charts对象
}
},
mounted() {
this.currentView = '1';
this.loadData();
},
methods: {
loadData() {
const days = parseInt(this.timeRange);
this.loadDashboardSummary(days);
this.loadPluginStats(days);
this.loadPluginTrend(days);
},
loadDashboardSummary(days) {
axios.get(`/api/dashboard_summary?days=${days}`)
.then(response => {
if (response.data.success) {
const data = response.data.data;
this.totalCalls = parseInt(data.total_calls) || 0;
this.successRate = parseFloat(data.success_rate) || 0;
this.activeUsers = data.active_users || 0;
this.activeGroups = data.active_groups || 0;
this.avgResponseTime = parseFloat(data.avg_response_time) || 0;
this.topUsers = data.top_users || [];
this.topGroups = data.top_groups || [];
this.topPlugins = data.top_plugins || [];
}
})
.catch(error => {
console.error('加载仪表盘摘要数据出错:', error);
this.$message.error('加载仪表盘摘要数据出错');
});
},
loadPluginStats(days) {
axios.get(`/api/plugin_stats?days=${days}`)
.then(response => {
if (response.data.success) {
this.pluginStats = response.data.data || [];
this.$nextTick(() => {
this.renderPluginChart();
this.renderSuccessRateChart();
});
}
})
.catch(error => {
console.error('加载插件统计数据出错:', error);
this.$message.error('加载插件统计数据出错');
});
},
loadPluginTrend(days) {
axios.get(`/api/plugin_trend?days=${days}`)
.then(response => {
if (response.data.success) {
const trendData = response.data.data || [];
this.$nextTick(() => {
this.renderTrendChart(trendData);
});
}
})
.catch(error => {
console.error('加载插件趋势数据出错:', error);
this.$message.error('加载插件趋势数据出错');
});
},
renderPluginChart() {
const ctx = document.getElementById('pluginChart').getContext('2d');
// 销毁旧图表
if (this.charts.pluginChart) {
this.charts.pluginChart.destroy();
}
// 准备数据
const sortedData = [...this.pluginStats].sort((a, b) => b.total_calls - a.total_calls).slice(0, 10);
// 修改这里:将插件名称和指令组合作为标签
const labels = sortedData.map(item => `${item.plugin_name}(${item.command})`);
const data = sortedData.map(item => item.total_calls);
// 创建新图表
this.charts.pluginChart = new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: '调用次数',
data: data,
backgroundColor: 'rgba(54, 162, 235, 0.6)',
borderColor: 'rgba(54, 162, 235, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true
}
}
}
});
},
renderSuccessRateChart() {
const ctx = document.getElementById('successRateChart').getContext('2d');
// 销毁旧图表
if (this.charts.successRateChart) {
this.charts.successRateChart.destroy();
}
// 准备数据
const sortedData = [...this.pluginStats]
.filter(item => item.total_calls > 0)
.sort((a, b) => (b.success_calls / b.total_calls) - (a.success_calls / a.total_calls))
.slice(0, 10);
// 修改这里:将插件名称和指令组合作为标签
const labels = sortedData.map(item => `${item.plugin_name}(${item.command})`);
const data = sortedData.map(item => (item.success_calls / item.total_calls) * 100);
// 创建新图表
this.charts.successRateChart = new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: '成功率(%)',
data: data,
backgroundColor: 'rgba(75, 192, 192, 0.6)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true,
max: 100
}
}
}
});
},
renderTrendChart(trendData) {
const ctx = document.getElementById('trendChart').getContext('2d');
// 销毁旧图表
if (this.charts && this.charts.trendChart) {
this.charts.trendChart.destroy();
}
// 确保charts对象存在
if (!this.charts) {
this.charts = {};
}
// 准备数据 - 修改字段名匹配和数据类型转换
const labels = trendData.map(item => item.date); // 使用date而不是stat_date
const totalData = trendData.map(item => parseInt(item.total_calls) || 0); // 确保转换为数字
const successData = trendData.map(item => parseInt(item.success_calls) || 0);
const errorData = trendData.map(item => parseInt(item.failed_calls) || 0); // 使用failed_calls而不是error_calls
console.log('处理后的趋势数据:', { labels, totalData, successData, errorData });
// 创建新图表
this.charts.trendChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [
{
label: '总调用',
data: totalData,
fill: false,
backgroundColor: 'rgba(54, 162, 235, 0.6)',
borderColor: 'rgba(54, 162, 235, 1)',
tension: 0.1
},
{
label: '成功调用',
data: successData,
fill: false,
backgroundColor: 'rgba(75, 192, 192, 0.6)',
borderColor: 'rgba(75, 192, 192, 1)',
tension: 0.1
},
{
label: '失败调用',
data: errorData,
fill: false,
backgroundColor: 'rgba(255, 99, 132, 0.6)',
borderColor: 'rgba(255, 99, 132, 1)',
tension: 0.1
}
]
},
options: {
responsive: true,
maintainAspectRatio: true,
scales: {
y: {
beginAtZero: true
}
}
}
});
}
}
});
</script>
{% endblock %}
{% block styles %}
<style>
.stats-card {
margin-bottom: 15px;
height: 120px;
}
.chart-container {
margin-bottom: 20px;
padding: 10px;
background-color: #fff;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.chart-container h3 {
margin-top: 0;
margin-bottom: 10px;
font-size: 16px;
color: #606266;
}
</style>
{% endblock %}