仪表盘功能完善

This commit is contained in:
2025-10-31 14:33:57 +08:00
parent 778f05daa5
commit bbe79ecffb
8 changed files with 416 additions and 20 deletions
+39
View File
@@ -41,12 +41,29 @@ export interface RecentActivity {
extraData: Record<string, any>
}
export interface UserGrowthTrend {
date: string
newUsers: number
totalUsers: number
}
export interface RecentLogin {
userId: string
username: string
nickname: string
avatar: string
loginTime: string
timeDescription: string
}
export interface DashboardStats {
userStats: UserStats
contentStats: ContentStats
aiServiceStats: AiServiceStats
systemStats: SystemStats
recentActivities: RecentActivity[]
userGrowthTrends: UserGrowthTrend[]
recentLogins: RecentLogin[]
updateTime: string
}
@@ -98,4 +115,26 @@ export function getSystemStats() {
url: '/admin/dashboard/system-stats',
method: 'get'
})
}
/**
* 获取用户增长趋势数据
*/
export function getUserGrowthTrends(days: number = 7) {
return request<UserGrowthTrend[]>({
url: '/admin/dashboard/user-growth-trends',
method: 'get',
params: { days }
})
}
/**
* 获取最近登录用户
*/
export function getRecentLogins(limit: number = 10) {
return request<RecentLogin[]>({
url: '/admin/dashboard/recent-logins',
method: 'get',
params: { limit }
})
}
+132 -20
View File
@@ -211,9 +211,25 @@
<template #header>
<span>最近登录</span>
</template>
<el-table :data="recentLogins" style="width: 100%">
<el-table-column prop="username" label="用户" />
<el-table-column prop="time" label="时间" />
<el-table :data="recentLogins" style="width: 100%" size="small">
<el-table-column label="用户" min-width="120">
<template #default="{ row }">
<div class="user-info">
<el-avatar
:size="32"
:src="row.avatar"
:alt="row.nickname || row.username"
>
{{ (row.nickname || row.username)?.charAt(0) }}
</el-avatar>
<div class="user-name">
<div class="nickname">{{ row.nickname || row.username }}</div>
<div class="username" v-if="row.nickname">@{{ row.username }}</div>
</div>
</div>
</template>
</el-table-column>
<el-table-column prop="timeDescription" label="时间" width="80" />
</el-table>
</el-card>
</el-col>
@@ -230,7 +246,7 @@ import { ref, reactive, onMounted } from 'vue'
import { User, UserFilled, TrendCharts, ChatDotRound, Setting, CircleCheck, CircleClose, Star } from '@element-plus/icons-vue'
import * as echarts from 'echarts'
import { countEnabledConfigs, countDisabledConfigs, countDefaultConfigs } from '@/api/aiconfig'
import { getDashboardStats, type DashboardStats } from '@/api/dashboard'
import { getDashboardStats, getUserGrowthTrends, getRecentLogins, type DashboardStats, type UserGrowthTrend, type RecentLogin } from '@/api/dashboard'
import AiConfigQuickActions from '@/components/AiConfigQuickActions.vue'
import { ElMessage } from 'element-plus'
@@ -276,12 +292,8 @@ const aiStats = reactive({
default: 0
})
const recentLogins = ref([
{ username: '张三', time: '2分钟前' },
{ username: '李四', time: '5分钟前' },
{ username: '王五', time: '10分钟前' },
{ username: '赵六', time: '15分钟前' }
])
const recentLogins = ref<RecentLogin[]>([])
const userGrowthTrends = ref<UserGrowthTrend[]>([])
const loading = ref(false)
@@ -298,11 +310,36 @@ const fetchDashboardData = async () => {
const statsRes = await getDashboardStats()
if (statsRes.data) {
Object.assign(dashboardStats, statsRes.data)
// 如果响应中包含增长趋势和最近登录数据,直接使用
if (statsRes.data.userGrowthTrends) {
userGrowthTrends.value = statsRes.data.userGrowthTrends
}
if (statsRes.data.recentLogins) {
recentLogins.value = statsRes.data.recentLogins
}
}
// 如果主接口没有返回这些数据,单独获取
if (!userGrowthTrends.value.length) {
const trendsRes = await getUserGrowthTrends(7)
if (trendsRes.data) {
userGrowthTrends.value = trendsRes.data
}
}
if (!recentLogins.value.length) {
const loginsRes = await getRecentLogins(10)
if (loginsRes.data) {
recentLogins.value = loginsRes.data
}
}
// 获取AI配置统计(保持原有逻辑)
await fetchAiStats()
// 更新图表数据
updateUserChart()
ElMessage.success('数据加载成功')
} catch (error) {
console.error('获取仪表盘数据失败:', error)
@@ -329,40 +366,87 @@ const fetchAiStats = async () => {
}
}
let userChart: any = null
const initUserChart = () => {
if (!userChartRef.value) return
const chart = echarts.init(userChartRef.value)
userChart = echarts.init(userChartRef.value)
// 初始化空图表
updateUserChart()
window.addEventListener('resize', () => {
userChart?.resize()
})
}
const updateUserChart = () => {
if (!userChart || !userGrowthTrends.value.length) return
// 处理日期标签,显示月-日格式
const dates = userGrowthTrends.value.map(trend => {
const date = new Date(trend.date)
return `${date.getMonth() + 1}-${date.getDate()}`
})
const newUsersData = userGrowthTrends.value.map(trend => trend.newUsers)
const option = {
tooltip: {
trigger: 'axis'
trigger: 'axis',
formatter: (params: any) => {
const dataIndex = params[0].dataIndex
const trend = userGrowthTrends.value[dataIndex]
return `
<div>
<div>日期: ${trend.date}</div>
<div>新增用户: ${trend.newUsers}</div>
<div>累计用户: ${trend.totalUsers}</div>
</div>
`
}
},
xAxis: {
type: 'category',
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
data: dates,
axisLabel: {
fontSize: 12
}
},
yAxis: {
type: 'value'
type: 'value',
axisLabel: {
fontSize: 12
}
},
series: [
{
name: '新增用户',
type: 'line',
data: [12, 23, 18, 29, 35, 42, 38],
data: newUsersData,
smooth: true,
itemStyle: {
color: '#409eff'
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(64, 158, 255, 0.3)' },
{ offset: 1, color: 'rgba(64, 158, 255, 0.1)' }
]
}
}
}
]
}
chart.setOption(option)
window.addEventListener('resize', () => {
chart.resize()
})
userChart.setOption(option)
}
</script>
@@ -429,5 +513,33 @@ const initUserChart = () => {
.chart-container {
height: 300px;
}
.user-info {
display: flex;
align-items: center;
gap: 10px;
.user-name {
flex: 1;
min-width: 0;
.nickname {
font-size: 14px;
font-weight: 500;
color: #333;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.username {
font-size: 12px;
color: #999;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
}
</style>