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> result = new CommonPage<>();