From 2c73d0c552ad23b1a832c722250b05571b5147d7 Mon Sep 17 00:00:00 2001
From: cxytw <123@123.com>
Date: Tue, 30 Dec 2025 17:27:55 +0800
Subject: [PATCH] =?UTF-8?q?12=E6=9C=8830=E5=8F=B7=E7=AE=A1=E7=90=86?=
=?UTF-8?q?=E7=AB=AF=E7=95=8C=E9=9D=A2=E4=BC=98=E5=8C=96=EF=BC=8C=E5=89=8D?=
=?UTF-8?q?=E5=90=8E=E7=AB=AF=E9=85=8D=E7=BD=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Zhibo/admin/sql/fix_home_menu_v2.sql | 47 +++++++++++++++++++
Zhibo/admin/src/router/index.js | 15 ++++++
Zhibo/admin/src/store/modules/user.js | 30 ++++++++++--
Zhibo/admin/src/views/monitor/users/index.vue | 16 ++++++-
.../admin/controller/MonitorController.java | 38 ++++++++++++---
5 files changed, 136 insertions(+), 10 deletions(-)
create mode 100644 Zhibo/admin/sql/fix_home_menu_v2.sql
diff --git a/Zhibo/admin/sql/fix_home_menu_v2.sql b/Zhibo/admin/sql/fix_home_menu_v2.sql
new file mode 100644
index 00000000..c80621c2
--- /dev/null
+++ b/Zhibo/admin/sql/fix_home_menu_v2.sql
@@ -0,0 +1,47 @@
+-- ============================================
+-- 首页菜单修复脚本 v2
+-- 用于确保侧边栏"首页"菜单能正确跳转到主页
+-- ============================================
+
+-- 1. 查看当前一级菜单(pid=0)
+SELECT id, pid, name, icon, component, menu_type, sort, is_show, is_delte
+FROM eb_system_menu
+WHERE pid = 0
+ORDER BY sort DESC, id ASC;
+
+-- 2. 查看是否已存在首页相关菜单
+SELECT * FROM eb_system_menu
+WHERE name IN ('首页', '控制台', '主页')
+ OR component IN ('/dashboard', '/home', '/home/index');
+
+-- ============================================
+-- 修复方案:更新现有首页菜单的component为/dashboard
+-- ============================================
+
+-- 方案A:如果首页菜单存在但component不正确,更新它
+UPDATE eb_system_menu
+SET component = '/dashboard'
+WHERE name IN ('首页', '控制台', '主页')
+ AND (component IS NULL OR component = '' OR component = '/home' OR component = '/home/index');
+
+-- 方案B:如果首页菜单不存在,插入新记录
+-- 注意:先执行上面的查询,确认首页菜单是否存在
+
+-- 检查是否需要插入(如果不存在首页菜单)
+-- 如果上面的查询结果为空,执行以下插入语句:
+
+-- 获取当前最大的菜单ID
+SELECT MAX(id) as max_id FROM eb_system_menu;
+
+-- 插入首页菜单(sort=300 确保排在最前面)
+-- 注意:请根据实际情况修改id值,确保不与现有记录冲突
+/*
+INSERT INTO `eb_system_menu` (`id`, `pid`, `name`, `icon`, `perms`, `component`, `menu_type`, `sort`, `is_show`, `is_delte`, `create_time`, `update_time`)
+VALUES (700, 0, '首页', 'el-icon-s-home', 'admin:dashboard:index', '/dashboard', 'C', 300, 1, 0, NOW(), NOW());
+*/
+
+-- 3. 验证修复结果
+SELECT id, pid, name, icon, component, menu_type, sort, is_show
+FROM eb_system_menu
+WHERE name IN ('首页', '控制台', '主页')
+ OR component = '/dashboard';
diff --git a/Zhibo/admin/src/router/index.js b/Zhibo/admin/src/router/index.js
index 74d64f19..1fc09e81 100644
--- a/Zhibo/admin/src/router/index.js
+++ b/Zhibo/admin/src/router/index.js
@@ -159,6 +159,21 @@ export const constantRoutes = [
},
],
},
+ // 首页路由别名 - 兼容数据库菜单配置的 /home 路径
+ {
+ path: '/home',
+ component: Layout,
+ redirect: '/home/index',
+ hidden: true,
+ children: [
+ {
+ path: 'index',
+ component: () => import('@/views/dashboard/index'),
+ name: 'Home',
+ meta: { title: '首页', icon: 'el-icon-s-home', isAffix: true },
+ },
+ ],
+ },
{
path: '/setting/uploadPicture',
component: () => import('@/components/uploadPicture/index.vue'),
diff --git a/Zhibo/admin/src/store/modules/user.js b/Zhibo/admin/src/store/modules/user.js
index 9b305912..efb1ebce 100644
--- a/Zhibo/admin/src/store/modules/user.js
+++ b/Zhibo/admin/src/store/modules/user.js
@@ -253,7 +253,15 @@ function replaceChildListWithChildren(data) {
const children = replaceChildListWithChildren(item.childList);
// 创建一个新的对象,将 childList 替换为 children
const title = item.name;
- const path = item.component;
+ let path = item.component;
+
+ // 特殊处理:如果是首页相关菜单,确保路径正确
+ if (item.name === '首页' || item.name === '控制台' || item.name === '主页') {
+ if (!path || path === '' || path === '/home' || path === '/home/index') {
+ path = '/dashboard';
+ }
+ }
+
return {
...item,
children,
@@ -265,8 +273,24 @@ function replaceChildListWithChildren(data) {
component: undefined,
};
}
- // 如果不存在 childList 字段,直接返回原对象
- return item;
+
+ // 如果不存在 childList 字段,处理叶子节点
+ let path = item.component;
+
+ // 特殊处理:如果是首页相关菜单,确保路径正确
+ if (item.name === '首页' || item.name === '控制台' || item.name === '主页') {
+ if (!path || path === '' || path === '/home' || path === '/home/index') {
+ path = '/dashboard';
+ }
+ }
+
+ return {
+ ...item,
+ title: item.name,
+ path: path,
+ name: undefined,
+ component: undefined,
+ };
});
}
diff --git a/Zhibo/admin/src/views/monitor/users/index.vue b/Zhibo/admin/src/views/monitor/users/index.vue
index 42dc7c3c..21d85655 100644
--- a/Zhibo/admin/src/views/monitor/users/index.vue
+++ b/Zhibo/admin/src/views/monitor/users/index.vue
@@ -8,11 +8,19 @@
+
+
+
+
+
+
+
搜索
+ 重置
@@ -80,7 +88,7 @@ export default {
loading: false,
tableData: [],
total: 0,
- queryForm: { keyword: '', page: 1, limit: 20 }
+ queryForm: { keyword: '', status: '', page: 1, limit: 20 }
};
},
mounted() {
@@ -95,6 +103,7 @@ export default {
this.total = res.total || 0;
} catch (error) {
console.error('获取在线用户失败:', error);
+ this.$message.error('获取用户列表失败');
} finally {
this.loading = false;
}
@@ -103,6 +112,10 @@ export default {
this.queryForm.page = 1;
this.getList();
},
+ handleReset() {
+ this.queryForm = { keyword: '', status: '', page: 1, limit: 20 };
+ this.getList();
+ },
handleSizeChange(val) {
this.queryForm.limit = val;
this.getList();
@@ -129,6 +142,7 @@ export default {
.header-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
.page-title { font-size: 18px; color: #303133; margin: 0; }
.selWidth { width: 200px; }
+.selWidth120 { width: 120px; }
.mb20 { margin-bottom: 20px; }
.mt20 { margin-top: 20px; }
.user-info { display: flex; align-items: center; gap: 12px; }
diff --git a/Zhibo/zhibo-h/crmeb-admin/src/main/java/com/zbkj/admin/controller/MonitorController.java b/Zhibo/zhibo-h/crmeb-admin/src/main/java/com/zbkj/admin/controller/MonitorController.java
index 37899ffc..a9d176fb 100644
--- a/Zhibo/zhibo-h/crmeb-admin/src/main/java/com/zbkj/admin/controller/MonitorController.java
+++ b/Zhibo/zhibo-h/crmeb-admin/src/main/java/com/zbkj/admin/controller/MonitorController.java
@@ -96,27 +96,39 @@ public class MonitorController {
/**
* 在线用户列表
+ * 显示所有用户,按最后登录时间排序,标记在线状态(5分钟内活跃为在线)
*/
@ApiOperation(value = "在线用户列表")
@GetMapping("/users")
public CommonResult>> getOnlineUsers(
@RequestParam(value = "keyword", required = false) String keyword,
+ @RequestParam(value = "status", required = false) String status,
@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "limit", defaultValue = "20") Integer limit) {
- // 获取最近活跃的用户(5分钟内有登录记录)
+ // 获取用户列表,按最后登录时间排序
StringBuilder sql = new StringBuilder();
sql.append("SELECT u.uid as id, u.nickname, u.avatar, u.phone, ");
sql.append("u.last_login_time, u.create_time, ");
+ sql.append("CASE WHEN u.last_login_time >= DATE_SUB(NOW(), INTERVAL 5 MINUTE) THEN 1 ELSE 0 END as is_online, ");
sql.append("TIMESTAMPDIFF(MINUTE, u.last_login_time, NOW()) as inactive_minutes ");
sql.append("FROM eb_user u ");
sql.append("WHERE u.status = 1 ");
- sql.append("AND u.last_login_time >= DATE_SUB(NOW(), INTERVAL 30 MINUTE) ");
StringBuilder countSql = new StringBuilder();
countSql.append("SELECT COUNT(*) FROM eb_user u ");
countSql.append("WHERE u.status = 1 ");
- countSql.append("AND u.last_login_time >= DATE_SUB(NOW(), INTERVAL 30 MINUTE) ");
+
+ // 根据状态筛选:online=仅在线,offline=仅离线,空=全部
+ if ("online".equals(status)) {
+ String condition = " AND u.last_login_time >= DATE_SUB(NOW(), INTERVAL 5 MINUTE) ";
+ sql.append(condition);
+ countSql.append(condition);
+ } else if ("offline".equals(status)) {
+ String condition = " AND (u.last_login_time IS NULL OR u.last_login_time < DATE_SUB(NOW(), INTERVAL 5 MINUTE)) ";
+ sql.append(condition);
+ countSql.append(condition);
+ }
if (keyword != null && !keyword.isEmpty()) {
String condition = " AND (u.nickname LIKE '%" + keyword + "%' OR u.phone LIKE '%" + keyword + "%') ";
@@ -124,7 +136,8 @@ public class MonitorController {
countSql.append(condition);
}
- sql.append(" ORDER BY u.last_login_time DESC ");
+ // 优先显示在线用户,然后按最后登录时间排序
+ sql.append(" ORDER BY is_online DESC, u.last_login_time DESC ");
Long total = jdbcTemplate.queryForObject(countSql.toString(), Long.class);
@@ -135,10 +148,23 @@ public class MonitorController {
// 添加在线状态
list.forEach(item -> {
- Integer inactiveMinutes = (Integer) item.get("inactive_minutes");
- item.put("isOnline", inactiveMinutes != null && inactiveMinutes < 5);
+ Object inactiveObj = item.get("inactive_minutes");
+ Long inactiveMinutes = null;
+ if (inactiveObj instanceof Long) {
+ inactiveMinutes = (Long) inactiveObj;
+ } else if (inactiveObj instanceof Integer) {
+ inactiveMinutes = ((Integer) inactiveObj).longValue();
+ }
+ // 5分钟内活跃视为在线
+ boolean isOnline = inactiveMinutes != null && inactiveMinutes < 5;
+ item.put("isOnline", isOnline);
item.put("lastLoginTime", item.get("last_login_time"));
item.put("createTime", item.get("create_time"));
+ // 移除不需要的字段
+ item.remove("is_online");
+ item.remove("inactive_minutes");
+ item.remove("last_login_time");
+ item.remove("create_time");
});
CommonPage