12月30号管理端界面优化,前后端配置
This commit is contained in:
parent
ce0e27f9e7
commit
2c73d0c552
47
Zhibo/admin/sql/fix_home_menu_v2.sql
Normal file
47
Zhibo/admin/sql/fix_home_menu_v2.sql
Normal 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';
|
||||||
|
|
@ -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',
|
path: '/setting/uploadPicture',
|
||||||
component: () => import('@/components/uploadPicture/index.vue'),
|
component: () => import('@/components/uploadPicture/index.vue'),
|
||||||
|
|
|
||||||
|
|
@ -253,7 +253,15 @@ function replaceChildListWithChildren(data) {
|
||||||
const children = replaceChildListWithChildren(item.childList);
|
const children = replaceChildListWithChildren(item.childList);
|
||||||
// 创建一个新的对象,将 childList 替换为 children
|
// 创建一个新的对象,将 childList 替换为 children
|
||||||
const title = item.name;
|
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 {
|
return {
|
||||||
...item,
|
...item,
|
||||||
children,
|
children,
|
||||||
|
|
@ -265,8 +273,24 @@ function replaceChildListWithChildren(data) {
|
||||||
component: undefined,
|
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,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,19 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-form :inline="true" :model="queryForm" size="small" class="mb20">
|
<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-form-item label="搜索">
|
||||||
<el-input v-model="queryForm.keyword" placeholder="昵称/手机号" clearable class="selWidth" />
|
<el-input v-model="queryForm.keyword" placeholder="昵称/手机号" clearable class="selWidth" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||||
|
<el-button @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
|
|
@ -80,7 +88,7 @@ export default {
|
||||||
loading: false,
|
loading: false,
|
||||||
tableData: [],
|
tableData: [],
|
||||||
total: 0,
|
total: 0,
|
||||||
queryForm: { keyword: '', page: 1, limit: 20 }
|
queryForm: { keyword: '', status: '', page: 1, limit: 20 }
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
@ -95,6 +103,7 @@ export default {
|
||||||
this.total = res.total || 0;
|
this.total = res.total || 0;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取在线用户失败:', error);
|
console.error('获取在线用户失败:', error);
|
||||||
|
this.$message.error('获取用户列表失败');
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
|
|
@ -103,6 +112,10 @@ export default {
|
||||||
this.queryForm.page = 1;
|
this.queryForm.page = 1;
|
||||||
this.getList();
|
this.getList();
|
||||||
},
|
},
|
||||||
|
handleReset() {
|
||||||
|
this.queryForm = { keyword: '', status: '', page: 1, limit: 20 };
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
handleSizeChange(val) {
|
handleSizeChange(val) {
|
||||||
this.queryForm.limit = val;
|
this.queryForm.limit = val;
|
||||||
this.getList();
|
this.getList();
|
||||||
|
|
@ -129,6 +142,7 @@ export default {
|
||||||
.header-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
|
.header-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
|
||||||
.page-title { font-size: 18px; color: #303133; margin: 0; }
|
.page-title { font-size: 18px; color: #303133; margin: 0; }
|
||||||
.selWidth { width: 200px; }
|
.selWidth { width: 200px; }
|
||||||
|
.selWidth120 { width: 120px; }
|
||||||
.mb20 { margin-bottom: 20px; }
|
.mb20 { margin-bottom: 20px; }
|
||||||
.mt20 { margin-top: 20px; }
|
.mt20 { margin-top: 20px; }
|
||||||
.user-info { display: flex; align-items: center; gap: 12px; }
|
.user-info { display: flex; align-items: center; gap: 12px; }
|
||||||
|
|
|
||||||
|
|
@ -96,27 +96,39 @@ public class MonitorController {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在线用户列表
|
* 在线用户列表
|
||||||
|
* 显示所有用户,按最后登录时间排序,标记在线状态(5分钟内活跃为在线)
|
||||||
*/
|
*/
|
||||||
@ApiOperation(value = "在线用户列表")
|
@ApiOperation(value = "在线用户列表")
|
||||||
@GetMapping("/users")
|
@GetMapping("/users")
|
||||||
public CommonResult<CommonPage<Map<String, Object>>> getOnlineUsers(
|
public CommonResult<CommonPage<Map<String, Object>>> getOnlineUsers(
|
||||||
@RequestParam(value = "keyword", required = false) String keyword,
|
@RequestParam(value = "keyword", required = false) String keyword,
|
||||||
|
@RequestParam(value = "status", required = false) String status,
|
||||||
@RequestParam(value = "page", defaultValue = "1") Integer page,
|
@RequestParam(value = "page", defaultValue = "1") Integer page,
|
||||||
@RequestParam(value = "limit", defaultValue = "20") Integer limit) {
|
@RequestParam(value = "limit", defaultValue = "20") Integer limit) {
|
||||||
|
|
||||||
// 获取最近活跃的用户(5分钟内有登录记录)
|
// 获取用户列表,按最后登录时间排序
|
||||||
StringBuilder sql = new StringBuilder();
|
StringBuilder sql = new StringBuilder();
|
||||||
sql.append("SELECT u.uid as id, u.nickname, u.avatar, u.phone, ");
|
sql.append("SELECT u.uid as id, u.nickname, u.avatar, u.phone, ");
|
||||||
sql.append("u.last_login_time, u.create_time, ");
|
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("TIMESTAMPDIFF(MINUTE, u.last_login_time, NOW()) as inactive_minutes ");
|
||||||
sql.append("FROM eb_user u ");
|
sql.append("FROM eb_user u ");
|
||||||
sql.append("WHERE u.status = 1 ");
|
sql.append("WHERE u.status = 1 ");
|
||||||
sql.append("AND u.last_login_time >= DATE_SUB(NOW(), INTERVAL 30 MINUTE) ");
|
|
||||||
|
|
||||||
StringBuilder countSql = new StringBuilder();
|
StringBuilder countSql = new StringBuilder();
|
||||||
countSql.append("SELECT COUNT(*) FROM eb_user u ");
|
countSql.append("SELECT COUNT(*) FROM eb_user u ");
|
||||||
countSql.append("WHERE u.status = 1 ");
|
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()) {
|
if (keyword != null && !keyword.isEmpty()) {
|
||||||
String condition = " AND (u.nickname LIKE '%" + keyword + "%' OR u.phone LIKE '%" + keyword + "%') ";
|
String condition = " AND (u.nickname LIKE '%" + keyword + "%' OR u.phone LIKE '%" + keyword + "%') ";
|
||||||
|
|
@ -124,7 +136,8 @@ public class MonitorController {
|
||||||
countSql.append(condition);
|
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);
|
Long total = jdbcTemplate.queryForObject(countSql.toString(), Long.class);
|
||||||
|
|
||||||
|
|
@ -135,10 +148,23 @@ public class MonitorController {
|
||||||
|
|
||||||
// 添加在线状态
|
// 添加在线状态
|
||||||
list.forEach(item -> {
|
list.forEach(item -> {
|
||||||
Integer inactiveMinutes = (Integer) item.get("inactive_minutes");
|
Object inactiveObj = item.get("inactive_minutes");
|
||||||
item.put("isOnline", inactiveMinutes != null && inactiveMinutes < 5);
|
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("lastLoginTime", item.get("last_login_time"));
|
||||||
item.put("createTime", item.get("create_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<>();
|
CommonPage<Map<String, Object>> result = new CommonPage<>();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user