仪表盘功能完善
This commit is contained in:
@@ -14,6 +14,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 管理员控制器
|
* 管理员控制器
|
||||||
@@ -143,4 +144,26 @@ public class AdminController {
|
|||||||
DashboardStatsResponse.SystemStats systemStats = dashboardService.getSystemStats();
|
DashboardStatsResponse.SystemStats systemStats = dashboardService.getSystemStats();
|
||||||
return Result.success("获取成功", systemStats);
|
return Result.success("获取成功", systemStats);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户增长趋势数据
|
||||||
|
*/
|
||||||
|
@Operation(summary = "获取用户增长趋势数据", description = "获取指定天数的用户增长趋势数据")
|
||||||
|
@GetMapping("/dashboard/user-growth-trends")
|
||||||
|
public Result<List<DashboardStatsResponse.UserGrowthTrend>> getUserGrowthTrends(
|
||||||
|
@RequestParam(defaultValue = "7") int days) {
|
||||||
|
List<DashboardStatsResponse.UserGrowthTrend> trends = dashboardService.getUserGrowthTrends(days);
|
||||||
|
return Result.success("获取成功", trends);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取最近登录用户
|
||||||
|
*/
|
||||||
|
@Operation(summary = "获取最近登录用户", description = "获取最近登录的用户列表")
|
||||||
|
@GetMapping("/dashboard/recent-logins")
|
||||||
|
public Result<List<DashboardStatsResponse.RecentLogin>> getRecentLogins(
|
||||||
|
@RequestParam(defaultValue = "10") int limit) {
|
||||||
|
List<DashboardStatsResponse.RecentLogin> recentLogins = dashboardService.getRecentLogins(limit);
|
||||||
|
return Result.success("获取成功", recentLogins);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,12 @@ public class DashboardStatsResponse {
|
|||||||
@Schema(description = "数据更新时间")
|
@Schema(description = "数据更新时间")
|
||||||
private LocalDateTime updateTime;
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
@Schema(description = "用户增长趋势数据")
|
||||||
|
private List<UserGrowthTrend> userGrowthTrends;
|
||||||
|
|
||||||
|
@Schema(description = "最近登录用户")
|
||||||
|
private List<RecentLogin> recentLogins;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户统计
|
* 用户统计
|
||||||
*/
|
*/
|
||||||
@@ -165,4 +171,51 @@ public class DashboardStatsResponse {
|
|||||||
@Schema(description = "额外数据")
|
@Schema(description = "额外数据")
|
||||||
private Map<String, Object> extraData;
|
private Map<String, Object> extraData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户增长趋势
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Schema(description = "用户增长趋势")
|
||||||
|
public static class UserGrowthTrend {
|
||||||
|
@Schema(description = "日期")
|
||||||
|
private String date;
|
||||||
|
|
||||||
|
@Schema(description = "新增用户数")
|
||||||
|
private Long newUsers;
|
||||||
|
|
||||||
|
@Schema(description = "累计用户数")
|
||||||
|
private Long totalUsers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最近登录用户
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Schema(description = "最近登录用户")
|
||||||
|
public static class RecentLogin {
|
||||||
|
@Schema(description = "用户ID")
|
||||||
|
private String userId;
|
||||||
|
|
||||||
|
@Schema(description = "用户名")
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@Schema(description = "昵称")
|
||||||
|
private String nickname;
|
||||||
|
|
||||||
|
@Schema(description = "头像")
|
||||||
|
private String avatar;
|
||||||
|
|
||||||
|
@Schema(description = "登录时间")
|
||||||
|
private LocalDateTime loginTime;
|
||||||
|
|
||||||
|
@Schema(description = "登录时间描述")
|
||||||
|
private String timeDescription;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.emotion.service;
|
package com.emotion.service;
|
||||||
|
|
||||||
import com.emotion.dto.response.DashboardStatsResponse;
|
import com.emotion.dto.response.DashboardStatsResponse;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 仪表盘服务接口
|
* 仪表盘服务接口
|
||||||
@@ -44,4 +45,20 @@ public interface DashboardService {
|
|||||||
* @return 系统统计数据
|
* @return 系统统计数据
|
||||||
*/
|
*/
|
||||||
DashboardStatsResponse.SystemStats getSystemStats();
|
DashboardStatsResponse.SystemStats getSystemStats();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户增长趋势数据
|
||||||
|
*
|
||||||
|
* @param days 天数,默认7天
|
||||||
|
* @return 用户增长趋势数据
|
||||||
|
*/
|
||||||
|
List<DashboardStatsResponse.UserGrowthTrend> getUserGrowthTrends(int days);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取最近登录用户
|
||||||
|
*
|
||||||
|
* @param limit 限制数量,默认10个
|
||||||
|
* @return 最近登录用户列表
|
||||||
|
*/
|
||||||
|
List<DashboardStatsResponse.RecentLogin> getRecentLogins(int limit);
|
||||||
}
|
}
|
||||||
@@ -73,6 +73,8 @@ public class DashboardServiceImpl implements DashboardService {
|
|||||||
.aiServiceStats(getAiServiceStats())
|
.aiServiceStats(getAiServiceStats())
|
||||||
.systemStats(getSystemStats())
|
.systemStats(getSystemStats())
|
||||||
.recentActivities(getRecentActivities())
|
.recentActivities(getRecentActivities())
|
||||||
|
.userGrowthTrends(getUserGrowthTrends(7))
|
||||||
|
.recentLogins(getRecentLogins(10))
|
||||||
.updateTime(LocalDateTime.now())
|
.updateTime(LocalDateTime.now())
|
||||||
.build();
|
.build();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -385,7 +387,110 @@ public class DashboardServiceImpl implements DashboardService {
|
|||||||
.uptime("未知")
|
.uptime("未知")
|
||||||
.build())
|
.build())
|
||||||
.recentActivities(new ArrayList<>())
|
.recentActivities(new ArrayList<>())
|
||||||
|
.userGrowthTrends(new ArrayList<>())
|
||||||
|
.recentLogins(new ArrayList<>())
|
||||||
.updateTime(LocalDateTime.now())
|
.updateTime(LocalDateTime.now())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<DashboardStatsResponse.UserGrowthTrend> getUserGrowthTrends(int days) {
|
||||||
|
List<DashboardStatsResponse.UserGrowthTrend> trends = new ArrayList<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
LocalDate endDate = LocalDate.now();
|
||||||
|
LocalDate startDate = endDate.minusDays(days - 1);
|
||||||
|
|
||||||
|
// 获取指定日期范围内每天的用户注册数据
|
||||||
|
for (int i = 0; i < days; i++) {
|
||||||
|
LocalDate currentDate = startDate.plusDays(i);
|
||||||
|
LocalDateTime dayStart = currentDate.atStartOfDay();
|
||||||
|
LocalDateTime dayEnd = dayStart.plusDays(1);
|
||||||
|
|
||||||
|
// 统计当天新增用户数
|
||||||
|
Long newUsers = userService.count(new LambdaQueryWrapper<User>()
|
||||||
|
.eq(User::getIsDeleted, 0)
|
||||||
|
.ge(User::getCreateTime, dayStart)
|
||||||
|
.lt(User::getCreateTime, dayEnd));
|
||||||
|
|
||||||
|
// 统计截止到当天的总用户数
|
||||||
|
Long totalUsers = userService.count(new LambdaQueryWrapper<User>()
|
||||||
|
.eq(User::getIsDeleted, 0)
|
||||||
|
.lt(User::getCreateTime, dayEnd));
|
||||||
|
|
||||||
|
trends.add(DashboardStatsResponse.UserGrowthTrend.builder()
|
||||||
|
.date(currentDate.toString())
|
||||||
|
.newUsers(newUsers)
|
||||||
|
.totalUsers(totalUsers)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取用户增长趋势数据失败", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return trends;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<DashboardStatsResponse.RecentLogin> getRecentLogins(int limit) {
|
||||||
|
List<DashboardStatsResponse.RecentLogin> recentLogins = new ArrayList<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取最近活跃的用户(按最后活跃时间排序)
|
||||||
|
List<User> recentUsers = userService.list(new LambdaQueryWrapper<User>()
|
||||||
|
.eq(User::getIsDeleted, 0)
|
||||||
|
.isNotNull(User::getLastActiveTime)
|
||||||
|
.orderByDesc(User::getLastActiveTime)
|
||||||
|
.last("LIMIT " + limit));
|
||||||
|
|
||||||
|
for (User user : recentUsers) {
|
||||||
|
String timeDescription = formatTimeDescription(user.getLastActiveTime());
|
||||||
|
|
||||||
|
recentLogins.add(DashboardStatsResponse.RecentLogin.builder()
|
||||||
|
.userId(user.getId())
|
||||||
|
.username(user.getUsername())
|
||||||
|
.nickname(user.getNickname())
|
||||||
|
.avatar(user.getAvatar())
|
||||||
|
.loginTime(user.getLastActiveTime())
|
||||||
|
.timeDescription(timeDescription)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取最近登录用户失败", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return recentLogins;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化时间描述
|
||||||
|
*/
|
||||||
|
private String formatTimeDescription(LocalDateTime dateTime) {
|
||||||
|
if (dateTime == null) {
|
||||||
|
return "未知";
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
long minutes = java.time.Duration.between(dateTime, now).toMinutes();
|
||||||
|
|
||||||
|
if (minutes < 1) {
|
||||||
|
return "刚刚";
|
||||||
|
} else if (minutes < 60) {
|
||||||
|
return minutes + "分钟前";
|
||||||
|
} else if (minutes < 1440) { // 24小时
|
||||||
|
long hours = minutes / 60;
|
||||||
|
return hours + "小时前";
|
||||||
|
} else {
|
||||||
|
long days = minutes / 1440;
|
||||||
|
if (days == 1) {
|
||||||
|
return "昨天";
|
||||||
|
} else if (days < 7) {
|
||||||
|
return days + "天前";
|
||||||
|
} else {
|
||||||
|
return dateTime.toLocalDate().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -85,6 +85,8 @@ public class DashboardServiceTest {
|
|||||||
System.out.println("奖励数量: " + systemStats.getRewardCount());
|
System.out.println("奖励数量: " + systemStats.getRewardCount());
|
||||||
System.out.println("系统运行时间: " + systemStats.getUptime());
|
System.out.println("系统运行时间: " + systemStats.getUptime());
|
||||||
System.out.println("最近活动数量: " + stats.getRecentActivities().size());
|
System.out.println("最近活动数量: " + stats.getRecentActivities().size());
|
||||||
|
System.out.println("用户增长趋势数量: " + (stats.getUserGrowthTrends() != null ? stats.getUserGrowthTrends().size() : 0));
|
||||||
|
System.out.println("最近登录用户数量: " + (stats.getRecentLogins() != null ? stats.getRecentLogins().size() : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -137,4 +139,48 @@ public class DashboardServiceTest {
|
|||||||
assertTrue(systemStats.getRewardCount() >= 0, "奖励数量应大于等于0");
|
assertTrue(systemStats.getRewardCount() >= 0, "奖励数量应大于等于0");
|
||||||
assertNotNull(systemStats.getUptime(), "系统运行时间不应为空");
|
assertNotNull(systemStats.getUptime(), "系统运行时间不应为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetUserGrowthTrends() {
|
||||||
|
// 测试获取用户增长趋势数据
|
||||||
|
var trends = dashboardService.getUserGrowthTrends(7);
|
||||||
|
|
||||||
|
assertNotNull(trends, "用户增长趋势数据不应为空");
|
||||||
|
assertTrue(trends.size() <= 7, "趋势数据数量应不超过7天");
|
||||||
|
|
||||||
|
for (var trend : trends) {
|
||||||
|
assertNotNull(trend.getDate(), "日期不应为空");
|
||||||
|
assertTrue(trend.getNewUsers() >= 0, "新增用户数应大于等于0");
|
||||||
|
assertTrue(trend.getTotalUsers() >= 0, "总用户数应大于等于0");
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("=== 用户增长趋势测试结果 ===");
|
||||||
|
for (var trend : trends) {
|
||||||
|
System.out.println(String.format("日期: %s, 新增: %d, 总计: %d",
|
||||||
|
trend.getDate(), trend.getNewUsers(), trend.getTotalUsers()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetRecentLogins() {
|
||||||
|
// 测试获取最近登录用户
|
||||||
|
var recentLogins = dashboardService.getRecentLogins(10);
|
||||||
|
|
||||||
|
assertNotNull(recentLogins, "最近登录用户数据不应为空");
|
||||||
|
assertTrue(recentLogins.size() <= 10, "最近登录用户数量应不超过10个");
|
||||||
|
|
||||||
|
for (var login : recentLogins) {
|
||||||
|
assertNotNull(login.getUserId(), "用户ID不应为空");
|
||||||
|
assertNotNull(login.getUsername(), "用户名不应为空");
|
||||||
|
assertNotNull(login.getTimeDescription(), "时间描述不应为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("=== 最近登录用户测试结果 ===");
|
||||||
|
for (var login : recentLogins) {
|
||||||
|
System.out.println(String.format("用户: %s (%s), 时间: %s",
|
||||||
|
login.getNickname() != null ? login.getNickname() : login.getUsername(),
|
||||||
|
login.getUsername(),
|
||||||
|
login.getTimeDescription()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -167,6 +167,7 @@ CREATE TABLE t_message (
|
|||||||
id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键
|
id VARCHAR(64) PRIMARY KEY COMMENT 'UUID主键', -- UUID主键
|
||||||
conversation_id VARCHAR(64) COMMENT '对话ID (关联t_conversation.id)', -- 对话ID (关联t_conversation.id)
|
conversation_id VARCHAR(64) COMMENT '对话ID (关联t_conversation.id)', -- 对话ID (关联t_conversation.id)
|
||||||
content TEXT COMMENT '消息内容', -- 消息内容
|
content TEXT COMMENT '消息内容', -- 消息内容
|
||||||
|
message_order BIGINT COMMENT '消息顺序',
|
||||||
type VARCHAR(50) DEFAULT 'text' COMMENT '消息类型', -- 消息类型
|
type VARCHAR(50) DEFAULT 'text' COMMENT '消息类型', -- 消息类型
|
||||||
sender VARCHAR(20) COMMENT '发送者: user-用户, assistant-AI助手', -- 发送者: user-用户, assistant-AI助手
|
sender VARCHAR(20) COMMENT '发送者: user-用户, assistant-AI助手', -- 发送者: user-用户, assistant-AI助手
|
||||||
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '消息时间戳', -- 消息时间戳
|
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '消息时间戳', -- 消息时间戳
|
||||||
|
|||||||
@@ -41,12 +41,29 @@ export interface RecentActivity {
|
|||||||
extraData: Record<string, any>
|
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 {
|
export interface DashboardStats {
|
||||||
userStats: UserStats
|
userStats: UserStats
|
||||||
contentStats: ContentStats
|
contentStats: ContentStats
|
||||||
aiServiceStats: AiServiceStats
|
aiServiceStats: AiServiceStats
|
||||||
systemStats: SystemStats
|
systemStats: SystemStats
|
||||||
recentActivities: RecentActivity[]
|
recentActivities: RecentActivity[]
|
||||||
|
userGrowthTrends: UserGrowthTrend[]
|
||||||
|
recentLogins: RecentLogin[]
|
||||||
updateTime: string
|
updateTime: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,4 +115,26 @@ export function getSystemStats() {
|
|||||||
url: '/admin/dashboard/system-stats',
|
url: '/admin/dashboard/system-stats',
|
||||||
method: 'get'
|
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 }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
@@ -211,9 +211,25 @@
|
|||||||
<template #header>
|
<template #header>
|
||||||
<span>最近登录</span>
|
<span>最近登录</span>
|
||||||
</template>
|
</template>
|
||||||
<el-table :data="recentLogins" style="width: 100%">
|
<el-table :data="recentLogins" style="width: 100%" size="small">
|
||||||
<el-table-column prop="username" label="用户" />
|
<el-table-column label="用户" min-width="120">
|
||||||
<el-table-column prop="time" label="时间" />
|
<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-table>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</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 { User, UserFilled, TrendCharts, ChatDotRound, Setting, CircleCheck, CircleClose, Star } from '@element-plus/icons-vue'
|
||||||
import * as echarts from 'echarts'
|
import * as echarts from 'echarts'
|
||||||
import { countEnabledConfigs, countDisabledConfigs, countDefaultConfigs } from '@/api/aiconfig'
|
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 AiConfigQuickActions from '@/components/AiConfigQuickActions.vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
@@ -276,12 +292,8 @@ const aiStats = reactive({
|
|||||||
default: 0
|
default: 0
|
||||||
})
|
})
|
||||||
|
|
||||||
const recentLogins = ref([
|
const recentLogins = ref<RecentLogin[]>([])
|
||||||
{ username: '张三', time: '2分钟前' },
|
const userGrowthTrends = ref<UserGrowthTrend[]>([])
|
||||||
{ username: '李四', time: '5分钟前' },
|
|
||||||
{ username: '王五', time: '10分钟前' },
|
|
||||||
{ username: '赵六', time: '15分钟前' }
|
|
||||||
])
|
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|
||||||
@@ -298,11 +310,36 @@ const fetchDashboardData = async () => {
|
|||||||
const statsRes = await getDashboardStats()
|
const statsRes = await getDashboardStats()
|
||||||
if (statsRes.data) {
|
if (statsRes.data) {
|
||||||
Object.assign(dashboardStats, 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配置统计(保持原有逻辑)
|
// 获取AI配置统计(保持原有逻辑)
|
||||||
await fetchAiStats()
|
await fetchAiStats()
|
||||||
|
|
||||||
|
// 更新图表数据
|
||||||
|
updateUserChart()
|
||||||
|
|
||||||
ElMessage.success('数据加载成功')
|
ElMessage.success('数据加载成功')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取仪表盘数据失败:', error)
|
console.error('获取仪表盘数据失败:', error)
|
||||||
@@ -329,40 +366,87 @@ const fetchAiStats = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let userChart: any = null
|
||||||
|
|
||||||
const initUserChart = () => {
|
const initUserChart = () => {
|
||||||
if (!userChartRef.value) return
|
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 = {
|
const option = {
|
||||||
tooltip: {
|
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: {
|
xAxis: {
|
||||||
type: 'category',
|
type: 'category',
|
||||||
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
|
data: dates,
|
||||||
|
axisLabel: {
|
||||||
|
fontSize: 12
|
||||||
|
}
|
||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
type: 'value'
|
type: 'value',
|
||||||
|
axisLabel: {
|
||||||
|
fontSize: 12
|
||||||
|
}
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: '新增用户',
|
name: '新增用户',
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: [12, 23, 18, 29, 35, 42, 38],
|
data: newUsersData,
|
||||||
smooth: true,
|
smooth: true,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: '#409eff'
|
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)
|
userChart.setOption(option)
|
||||||
|
|
||||||
window.addEventListener('resize', () => {
|
|
||||||
chart.resize()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -429,5 +513,33 @@ const initUserChart = () => {
|
|||||||
.chart-container {
|
.chart-container {
|
||||||
height: 300px;
|
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>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user