12月30号管理端界面优化,前后端配置

This commit is contained in:
cxytw 2025-12-30 17:27:55 +08:00
parent ce0e27f9e7
commit 2c73d0c552
5 changed files with 136 additions and 10 deletions

View File

@ -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';

View File

@ -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'),

View File

@ -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,
};
});
}

View File

@ -8,11 +8,19 @@
</div>
<el-form :inline="true" :model="queryForm" size="small" class="mb20">
<el-form-item label="状态">
<el-select v-model="queryForm.status" placeholder="全部" clearable class="selWidth120">
<el-option label="全部用户" value="" />
<el-option label="仅在线" value="online" />
<el-option label="仅离线" value="offline" />
</el-select>
</el-form-item>
<el-form-item label="搜索">
<el-input v-model="queryForm.keyword" placeholder="昵称/手机号" clearable class="selWidth" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
@ -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; }

View File

@ -96,27 +96,39 @@ public class MonitorController {
/**
* 在线用户列表
* 显示所有用户按最后登录时间排序标记在线状态5分钟内活跃为在线
*/
@ApiOperation(value = "在线用户列表")
@GetMapping("/users")
public CommonResult<CommonPage<Map<String, Object>>> 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<Map<String, Object>> result = new CommonPage<>();