😎调整在线用户列表为缓存模式(去掉存库模式)
This commit is contained in:
parent
511be92199
commit
12c8e240bc
@ -28,6 +28,5 @@ public class Startup : AppStartup
|
||||
/// <param name="componentContext"></param>
|
||||
public void LoadAppComponent(object application, IWebHostEnvironment env, ComponentContext componentContext)
|
||||
{
|
||||
WebApplication webApplication = application as WebApplication;
|
||||
}
|
||||
}
|
||||
@ -27,10 +27,10 @@
|
||||
<PackageReference Include="AspectCore.Extensions.Reflection" Version="2.4.0" />
|
||||
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
|
||||
<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.2" Aliases="BouncyCastleV2" />
|
||||
<PackageReference Include="Elastic.Clients.Elasticsearch" Version="9.1.3" />
|
||||
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.108" />
|
||||
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.108" />
|
||||
<PackageReference Include="Furion.Pure" Version="4.9.7.108" />
|
||||
<PackageReference Include="Elastic.Clients.Elasticsearch" Version="9.1.4" />
|
||||
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.109" />
|
||||
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.109" />
|
||||
<PackageReference Include="Furion.Pure" Version="4.9.7.109" />
|
||||
<PackageReference Include="Hardware.Info" Version="101.0.1.1" />
|
||||
<PackageReference Include="Hashids.net" Version="1.7.0" />
|
||||
<PackageReference Include="IPTools.China" Version="1.6.0" />
|
||||
@ -52,7 +52,7 @@
|
||||
<PackageReference Include="SixLabors.ImageSharp.Web" Version="3.2.0" />
|
||||
<PackageReference Include="SKIT.FlurlHttpClient.Wechat.Api" Version="3.11.0" />
|
||||
<PackageReference Include="SKIT.FlurlHttpClient.Wechat.TenpayV3" Version="3.13.0" />
|
||||
<PackageReference Include="SqlSugar.MongoDbCore" Version="5.1.4.247" />
|
||||
<PackageReference Include="SqlSugar.MongoDbCore" Version="5.1.4.248" />
|
||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.199" />
|
||||
<PackageReference Include="SSH.NET" Version="2025.0.0" />
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.6.7" />
|
||||
|
||||
@ -1,81 +0,0 @@
|
||||
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||
//
|
||||
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
namespace Admin.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// 系统在线用户表
|
||||
/// </summary>
|
||||
[SugarTable(null, "系统在线用户表")]
|
||||
[SysTable]
|
||||
public partial class SysOnlineUser : EntityTenantId
|
||||
{
|
||||
/// <summary>
|
||||
/// 连接Id
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "连接Id")]
|
||||
public string? ConnectionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户Id
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "用户Id")]
|
||||
public long UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 账号
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "账号", Length = 32)]
|
||||
[Required, MaxLength(32)]
|
||||
public virtual string UserName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 真实姓名
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "真实姓名", Length = 32)]
|
||||
[MaxLength(32)]
|
||||
public string? RealName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 连接时间
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "连接时间")]
|
||||
public DateTime? Time { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 连接IP
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "连接IP", Length = 256)]
|
||||
[MaxLength(256)]
|
||||
public string? Ip { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 浏览器
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "浏览器", Length = 128)]
|
||||
[MaxLength(128)]
|
||||
public string? Browser { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作系统
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "操作系统", Length = 128)]
|
||||
[MaxLength(128)]
|
||||
public string? Os { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 登录模式
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "登录模式", DefaultValue = "1")]
|
||||
public LoginModeEnum LoginMode { get; set; } = LoginModeEnum.PC;
|
||||
|
||||
/// <summary>
|
||||
/// 登录设备
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "登录设备", Length = 256)]
|
||||
[MaxLength(256)]
|
||||
public string? Device { get; set; }
|
||||
}
|
||||
@ -12,5 +12,5 @@ public class OnlineUserList
|
||||
|
||||
public bool Online { get; set; }
|
||||
|
||||
public List<SysOnlineUser> UserList { get; set; }
|
||||
public List<OnlineUser> UserList { get; set; }
|
||||
}
|
||||
@ -17,15 +17,12 @@ public class OnlineUserHub : Hub<IOnlineUserHub>
|
||||
{
|
||||
private const string GROUP_ONLINE = "GROUP_ONLINE_"; // 租户分组前缀
|
||||
|
||||
private readonly SqlSugarRepository<SysOnlineUser> _sysOnlineUerRep;
|
||||
private readonly SysMessageService _sysMessageService;
|
||||
private readonly IHubContext<OnlineUserHub, IOnlineUserHub> _onlineUserHubContext;
|
||||
|
||||
public OnlineUserHub(SqlSugarRepository<SysOnlineUser> sysOnlineUerRep,
|
||||
SysMessageService sysMessageService,
|
||||
public OnlineUserHub(SysMessageService sysMessageService,
|
||||
IHubContext<OnlineUserHub, IOnlineUserHub> onlineUserHubContext)
|
||||
{
|
||||
_sysOnlineUerRep = sysOnlineUerRep;
|
||||
_sysMessageService = sysMessageService;
|
||||
_onlineUserHubContext = onlineUserHubContext;
|
||||
}
|
||||
@ -48,7 +45,7 @@ public class OnlineUserHub : Hub<IOnlineUserHub>
|
||||
var device = httpContext.GetClientDeviceInfo().Trim();
|
||||
var ipAddress = httpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault();
|
||||
|
||||
var user = new SysOnlineUser
|
||||
var user = new OnlineUser
|
||||
{
|
||||
ConnectionId = Context.ConnectionId,
|
||||
UserId = userId,
|
||||
@ -62,15 +59,14 @@ public class OnlineUserHub : Hub<IOnlineUserHub>
|
||||
LoginMode = loginMode,
|
||||
Device = device
|
||||
};
|
||||
await _sysOnlineUerRep.InsertAsync(user);
|
||||
SysCacheService.HashAdd(CacheConst.KeyUserOnline, user.UserId + Context.ConnectionId + loginMode, user);
|
||||
SysCacheService.HashAdd(CacheConst.KeyUserOnline, Context.ConnectionId, user);
|
||||
|
||||
// 以租户Id进行分组
|
||||
var groupName = $"{GROUP_ONLINE}{user.TenantId}";
|
||||
await _onlineUserHubContext.Groups.AddToGroupAsync(Context.ConnectionId, groupName);
|
||||
|
||||
var userList = await _sysOnlineUerRep.AsQueryable().Filter("", true)
|
||||
.Where(u => u.TenantId == user.TenantId).Take(10).ToListAsync();
|
||||
// 更新在线用户列表
|
||||
var userList = SysCacheService.HashGetAll<OnlineUser>(CacheConst.KeyUserOnline).Where(u => u.Value.TenantId == tenantId).Select(u => u.Value).ToList();
|
||||
await _onlineUserHubContext.Clients.Groups(groupName).OnlineUserList(new OnlineUserList
|
||||
{
|
||||
RealName = user.RealName,
|
||||
@ -88,14 +84,13 @@ public class OnlineUserHub : Hub<IOnlineUserHub>
|
||||
{
|
||||
if (string.IsNullOrEmpty(Context.ConnectionId)) return;
|
||||
|
||||
var user = await _sysOnlineUerRep.AsQueryable().Filter("", true).FirstAsync(u => u.ConnectionId == Context.ConnectionId);
|
||||
var user = SysCacheService.HashGetOne<OnlineUser>(CacheConst.KeyUserOnline, Context.ConnectionId);
|
||||
if (user == null) return;
|
||||
|
||||
await _sysOnlineUerRep.DeleteByIdAsync(user.Id);
|
||||
SysCacheService.HashDel<SysOnlineUser>(CacheConst.KeyUserOnline, user.UserId + Context.ConnectionId + user.LoginMode);
|
||||
SysCacheService.HashDel<OnlineUser>(CacheConst.KeyUserOnline, Context.ConnectionId);
|
||||
|
||||
// 通知当前组用户变动
|
||||
var userList = await _sysOnlineUerRep.AsQueryable().Filter("", true).Where(u => u.TenantId == user.TenantId).Take(10).ToListAsync();
|
||||
// 更新在线用户列表
|
||||
var userList = SysCacheService.HashGetAll<OnlineUser>(CacheConst.KeyUserOnline).Where(u => u.Value.TenantId == user.TenantId).Select(u => u.Value).ToList();
|
||||
await _onlineUserHubContext.Clients.Groups($"{GROUP_ONLINE}{user.TenantId}").OnlineUserList(new OnlineUserList
|
||||
{
|
||||
RealName = user.RealName,
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||
//
|
||||
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
namespace Admin.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// 清理在线用户作业任务(每隔 60秒 执行)
|
||||
/// </summary>
|
||||
[JobDetail("job_onlineUser", Description = "清理在线用户", GroupName = "default", Concurrent = false)]
|
||||
[PeriodSeconds(60, TriggerId = "trigger_onlineUser", Description = "清理在线用户", RunOnStart = true)]
|
||||
public class OnlineUserJob(IServiceScopeFactory serviceScopeFactory) : IJob
|
||||
{
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory = serviceScopeFactory;
|
||||
|
||||
public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
|
||||
{
|
||||
using var serviceScope = _serviceScopeFactory.CreateScope();
|
||||
|
||||
var sysOnlineUserService = serviceScope.ServiceProvider.GetRequiredService<SysOnlineUserService>();
|
||||
await sysOnlineUserService.ClearOnline();
|
||||
|
||||
var originColor = Console.ForegroundColor;
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
var message = $"【定时任务】清理系统在线用户 {DateTime.Now}";
|
||||
Console.WriteLine(message);
|
||||
Log.Information(message);
|
||||
Console.ForegroundColor = originColor;
|
||||
}
|
||||
}
|
||||
@ -36,8 +36,7 @@ public class StartHostedService(IServiceScopeFactory serviceScopeFactory) : IHos
|
||||
Log.Information(message);
|
||||
|
||||
// 清理在线用户
|
||||
var db = serviceScope.ServiceProvider.GetRequiredService<ISqlSugarClient>().CopyNew();
|
||||
await db.Deleteable<SysOnlineUser>().ExecuteCommandAsync(cancellationToken);
|
||||
serviceScope.ServiceProvider.GetRequiredService<SysCacheService>().Remove(CacheConst.KeyUserOnline);
|
||||
message = $"【启动任务】清理系统在线用户 {DateTime.Now}";
|
||||
Console.WriteLine(message);
|
||||
Log.Information(message);
|
||||
|
||||
@ -103,7 +103,7 @@ public class SysCacheService : IDynamicApiController, ISingleton
|
||||
{
|
||||
var key = Key(cacheName, obs);
|
||||
// 使用分布式锁
|
||||
using (_cacheProvider.Cache.AcquireLock($@"lock:AdGetAsync:{cacheName}", 1000))
|
||||
using (_cacheProvider.Cache.AcquireLock($@"lock:AdGetAsync:{cacheName}", expiry != null ? (int)expiry.Value.TotalSeconds * 1000 : 1000))
|
||||
{
|
||||
var value = Get<T>(key);
|
||||
value ??= await ((dynamic)del).DynamicInvokeAsync(obs);
|
||||
|
||||
@ -43,7 +43,7 @@ public class SysMessageService : IDynamicApiController, ITransient
|
||||
[DisplayName("发送消息给某人")]
|
||||
public async Task SendUser(MessageInput input)
|
||||
{
|
||||
var hashKey = SysCacheService.HashGetAll<SysOnlineUser>(CacheConst.KeyUserOnline);
|
||||
var hashKey = SysCacheService.HashGetAll<OnlineUser>(CacheConst.KeyUserOnline);
|
||||
var sendUser = hashKey.Where(u => u.Value.UserId == input.SendUserId).Select(u => u.Value).FirstOrDefault();
|
||||
var receiveUsers = hashKey.Where(u => input.ReceiveUserIds.Any(a => a == u.Value.UserId)).Select(u => u.Value).ToList();
|
||||
foreach (var receiveUser in receiveUsers)
|
||||
|
||||
@ -0,0 +1,68 @@
|
||||
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||
//
|
||||
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
namespace Admin.NET.Core.Service;
|
||||
|
||||
/// <summary>
|
||||
/// 在线用户
|
||||
/// </summary>
|
||||
public class OnlineUser
|
||||
{
|
||||
/// <summary>
|
||||
/// 连接Id
|
||||
/// </summary>
|
||||
public string ConnectionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 租户Id
|
||||
/// </summary>
|
||||
public long TenantId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户Id
|
||||
/// </summary>
|
||||
public long UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 账号
|
||||
/// </summary>
|
||||
public string UserName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 真实姓名
|
||||
/// </summary>
|
||||
public string RealName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 上线时间
|
||||
/// </summary>
|
||||
public DateTime Time { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 登录IP
|
||||
/// </summary>
|
||||
public string Ip { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 浏览器
|
||||
/// </summary>
|
||||
public string Browser { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作系统
|
||||
/// </summary>
|
||||
public string Os { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 登录模式
|
||||
/// </summary>
|
||||
public LoginModeEnum LoginMode { get; set; } = LoginModeEnum.PC;
|
||||
|
||||
/// <summary>
|
||||
/// 登录设备
|
||||
/// </summary>
|
||||
public string Device { get; set; }
|
||||
}
|
||||
@ -14,30 +14,28 @@ namespace Admin.NET.Core.Service;
|
||||
[ApiDescriptionSettings(Order = 300, Description = "在线用户")]
|
||||
public class SysOnlineUserService : IDynamicApiController, ITransient
|
||||
{
|
||||
private readonly SqlSugarRepository<SysOnlineUser> _sysOnlineUerRep;
|
||||
private readonly UserManager _userManager;
|
||||
private readonly SysConfigService _sysConfigService;
|
||||
private readonly IHubContext<OnlineUserHub, IOnlineUserHub> _onlineUserHubContext;
|
||||
|
||||
public SysOnlineUserService(SqlSugarRepository<SysOnlineUser> sysOnlineUerRep,
|
||||
public SysOnlineUserService(UserManager userManager,
|
||||
SysConfigService sysConfigService,
|
||||
IHubContext<OnlineUserHub, IOnlineUserHub> onlineUserHubContext)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_sysConfigService = sysConfigService;
|
||||
_onlineUserHubContext = onlineUserHubContext;
|
||||
_sysOnlineUerRep = sysOnlineUerRep;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取在线用户分页列表 🔖
|
||||
/// 获取在线用户列表 🔖
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[DisplayName("获取在线用户分页列表")]
|
||||
public async Task<SqlSugarPagedList<SysOnlineUser>> Page(PageOnlineUserInput input)
|
||||
[DisplayName("获取在线用户列表")]
|
||||
public async Task<List<OnlineUser>> GetOnlineUserList()
|
||||
{
|
||||
return await _sysOnlineUerRep.AsQueryable()
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(input.UserName), u => u.UserName.Contains(input.UserName))
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(input.RealName), u => u.RealName.Contains(input.RealName))
|
||||
.ToPagedListAsync(input.Page, input.PageSize);
|
||||
var userList = SysCacheService.HashGetAll<OnlineUser>(CacheConst.KeyUserOnline).Where(u => u.Value.TenantId == _userManager.TenantId).Select(u => u.Value).ToList();
|
||||
return await Task.FromResult(userList);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -47,10 +45,24 @@ public class SysOnlineUserService : IDynamicApiController, ITransient
|
||||
/// <returns></returns>
|
||||
[NonValidation]
|
||||
[DisplayName("强制下线")]
|
||||
public async Task ForceOffline(SysOnlineUser user)
|
||||
public async Task ForceOffline(OnlineUser user)
|
||||
{
|
||||
await _onlineUserHubContext.Clients.Client(user.ConnectionId ?? "").ForceOffline("强制下线");
|
||||
await _sysOnlineUerRep.DeleteAsync(user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过用户Id踢掉在线用户
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <returns></returns>
|
||||
[NonAction]
|
||||
public async Task ForceOfflineByUserId(long userId)
|
||||
{
|
||||
var users = SysCacheService.HashGetAll<OnlineUser>(CacheConst.KeyUserOnline).Where(u => u.Value.UserId == userId).Select(u => u.Value).ToList();
|
||||
foreach (var user in users)
|
||||
{
|
||||
await ForceOffline(user);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -62,7 +74,9 @@ public class SysOnlineUserService : IDynamicApiController, ITransient
|
||||
[NonAction]
|
||||
public async Task PublicNotice(SysNotice notice, List<long> userIds)
|
||||
{
|
||||
var userList = await _sysOnlineUerRep.GetListAsync(u => userIds.Contains(u.UserId));
|
||||
var userList = SysCacheService.HashGetAll<OnlineUser>(CacheConst.KeyUserOnline)
|
||||
.Where(u => userIds.Contains(u.Value.UserId))
|
||||
.Select(u => u.Value).ToList();
|
||||
if (userList.Count == 0) return;
|
||||
|
||||
foreach (var item in userList)
|
||||
@ -80,61 +94,15 @@ public class SysOnlineUserService : IDynamicApiController, ITransient
|
||||
[NonAction]
|
||||
public async Task SingleLogin(long userId, LoginModeEnum loginMode)
|
||||
{
|
||||
if (await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysSingleLogin))
|
||||
if (!await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysSingleLogin)) return;
|
||||
|
||||
var users = SysCacheService.HashGetAll<OnlineUser>(CacheConst.KeyUserOnline).Where(u => u.Value.UserId == userId).Select(u => u.Value).ToList();
|
||||
foreach (var user in users)
|
||||
{
|
||||
var users = await _sysOnlineUerRep.GetListAsync(u => u.UserId == userId);
|
||||
foreach (var user in users)
|
||||
if (user.LoginMode == loginMode)
|
||||
{
|
||||
if (loginMode == user.LoginMode)
|
||||
{
|
||||
await ForceOffline(user);
|
||||
}
|
||||
await ForceOffline(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过用户ID踢掉在线用户
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <returns></returns>
|
||||
[NonAction]
|
||||
public async Task ForceOfflineByUserId(long userId)
|
||||
{
|
||||
var users = await _sysOnlineUerRep.GetListAsync(u => u.UserId == userId);
|
||||
foreach (var user in users)
|
||||
{
|
||||
await ForceOffline(user);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理在线用户(开启单设备登录时只留相同账号最后登录的)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
[DisplayName("清理在线用户")]
|
||||
public async Task ClearOnline()
|
||||
{
|
||||
if (!await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysSingleLogin)) return;
|
||||
|
||||
// 相同账号最后登录的用户Id集合
|
||||
var onlineUsers = await _sysOnlineUerRep.AsQueryable().GroupBy(u => u.UserId)
|
||||
.Select(u => new
|
||||
{
|
||||
UserId = u.UserId,
|
||||
Count = SqlFunc.AggregateCount(u.UserId),
|
||||
Id = SqlFunc.AggregateMax(u.Id)
|
||||
})
|
||||
.ToListAsync();
|
||||
if (onlineUsers.Count < 1) return;
|
||||
|
||||
// 无效登录用户集合
|
||||
var onlineUserIds = onlineUsers.Select(u => u.Id).ToList();
|
||||
var offlineUsers = await _sysOnlineUerRep.AsQueryable().Where(u => !onlineUserIds.Contains(u.Id)).ToListAsync();
|
||||
foreach (var user in offlineUsers)
|
||||
{
|
||||
await ForceOffline(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -196,7 +196,7 @@ public class SysReportConfigService : IDynamicApiController, ITransient
|
||||
{
|
||||
summaryInfo[summaryFieldName] = dataTable.AsEnumerable().Sum(row =>
|
||||
{
|
||||
decimal.TryParse(row[summaryFieldName] + "", out decimal summaryValue);
|
||||
_ = decimal.TryParse(row[summaryFieldName] + "", out decimal summaryValue);
|
||||
return summaryValue;
|
||||
});
|
||||
}
|
||||
@ -218,9 +218,9 @@ public class SysReportConfigService : IDynamicApiController, ITransient
|
||||
|
||||
var dataSourceDetailList = await _sysReportDataSourceService.GetDataSourceListIncludeDetail();
|
||||
var dataSourceDetail = dataSourceDetailList.FirstOrDefault(u => u.Id == dataSource) ?? throw Oops.Bah(ErrorCodeEnum.C1002);
|
||||
ISqlSugarClient dbClient = GetDbClient(dataSourceDetail);
|
||||
SqlSugarScopeProvider dbClient = GetDbClient(dataSourceDetail);
|
||||
|
||||
var newExecParams = BuildInParamsHandle(dbClient, sqlScript, execParams);
|
||||
var newExecParams = BuildInParamsHandle(sqlScript, execParams);
|
||||
var parameters = newExecParams.Select(u => new SugarParameter(u.Key, u.Value)).ToList();
|
||||
|
||||
if (isSelectQuery)
|
||||
@ -242,9 +242,9 @@ public class SysReportConfigService : IDynamicApiController, ITransient
|
||||
/// </summary>
|
||||
/// <param name="dataSourceDetail"></param>
|
||||
/// <returns></returns>
|
||||
private ISqlSugarClient GetDbClient(SysReportDataSourceDetail dataSourceDetail)
|
||||
private SqlSugarScopeProvider GetDbClient(SysReportDataSourceDetail dataSourceDetail)
|
||||
{
|
||||
ISqlSugarClient dbClient = null;
|
||||
SqlSugarScopeProvider dbClient = null;
|
||||
if (dataSourceDetail.IsBuildIn)
|
||||
{
|
||||
// 获取内置数据库和租户的连接
|
||||
@ -287,7 +287,7 @@ public class SysReportConfigService : IDynamicApiController, ITransient
|
||||
/// <summary>
|
||||
/// 内置参数处理
|
||||
/// </summary>
|
||||
private Dictionary<string, object> BuildInParamsHandle(ISqlSugarClient dbClient, string sqlScript, Dictionary<string, object> execParams)
|
||||
private Dictionary<string, object> BuildInParamsHandle(string sqlScript, Dictionary<string, object> execParams)
|
||||
{
|
||||
var newExecParams = new Dictionary<string, object>(execParams);
|
||||
|
||||
@ -380,7 +380,7 @@ public class SysReportConfigService : IDynamicApiController, ITransient
|
||||
int colIndex = fields.FindIndex(f => f.FieldName == field.FieldName) + 1;
|
||||
decimal sum = data.Sum(r =>
|
||||
{
|
||||
decimal.TryParse(r[field.FieldName]?.ToString(), out decimal val);
|
||||
_ = decimal.TryParse(r[field.FieldName]?.ToString(), out decimal val);
|
||||
return val;
|
||||
});
|
||||
worksheet.Cells[currentRow, colIndex].Value = sum;
|
||||
|
||||
@ -320,7 +320,7 @@ public class SysRoleService : IDynamicApiController, ITransient
|
||||
[DisplayName("设置角色状态")]
|
||||
public async Task<int> SetStatus(RoleInput input)
|
||||
{
|
||||
if (!Enum.IsDefined(typeof(StatusEnum), input.Status)) throw Oops.Oh(ErrorCodeEnum.D3005);
|
||||
if (!Enum.IsDefined(input.Status)) throw Oops.Oh(ErrorCodeEnum.D3005);
|
||||
|
||||
return await _sysRoleRep.AsUpdateable()
|
||||
.SetColumns(u => u.Status == input.Status)
|
||||
|
||||
@ -264,7 +264,7 @@ public class SysUserService : IDynamicApiController, ITransient
|
||||
var user = await _sysUserRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
|
||||
user.ValidateIsSuperAdminAccountType(ErrorCodeEnum.D1015);
|
||||
|
||||
if (!Enum.IsDefined(typeof(StatusEnum), input.Status))
|
||||
if (!Enum.IsDefined(input.Status))
|
||||
throw Oops.Oh(ErrorCodeEnum.D3005);
|
||||
|
||||
if (input.Status != StatusEnum.Enable)
|
||||
|
||||
@ -521,9 +521,7 @@ public class SysWechatPayService : IDynamicApiController, ITransient
|
||||
[DisplayName("微信退款申请)")]
|
||||
public async Task Refund(RefundRequestInput input)
|
||||
{
|
||||
var vechatPay = await _sysWechatPayRep.GetFirstAsync(u => u.OutTradeNumber == input.OutTradeNumber);
|
||||
if (vechatPay == null)
|
||||
throw Oops.Bah("没有相应支付记录");
|
||||
var vechatPay = await _sysWechatPayRep.GetFirstAsync(u => u.OutTradeNumber == input.OutTradeNumber) ?? throw Oops.Bah("没有相应支付记录");
|
||||
|
||||
var request = new CreateRefundDomesticRefundRequest()
|
||||
{
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
|
||||
using NewLife.IO;
|
||||
using NewLife.Reflection;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Admin.NET.Core;
|
||||
@ -133,7 +134,7 @@ public static class SqlSugarExtension
|
||||
|
||||
Expression binaryExpresioFilter;
|
||||
|
||||
if (Enum.IsDefined(typeof(FilterLogicEnum), filter.Logic))
|
||||
if (Enum.IsDefined(filter.Logic))
|
||||
{
|
||||
if (filter.Filters is null) throw new ArgumentException("The Filters attribute is required when declaring a logic");
|
||||
binaryExpresioFilter = CreateFilterExpression(filter.Logic, filter.Filters, parameter);
|
||||
@ -151,7 +152,7 @@ public static class SqlSugarExtension
|
||||
return queryable;
|
||||
}
|
||||
|
||||
private static Expression CombineFilter(FilterLogicEnum filterLogic, Expression bExpresionBase, Expression bExpresion)
|
||||
private static BinaryExpression CombineFilter(FilterLogicEnum filterLogic, Expression bExpresionBase, Expression bExpresion)
|
||||
{
|
||||
return filterLogic switch
|
||||
{
|
||||
@ -177,7 +178,7 @@ public static class SqlSugarExtension
|
||||
{
|
||||
Expression bExpresionFilter;
|
||||
|
||||
if (Enum.IsDefined(typeof(FilterLogicEnum), filter.Logic))
|
||||
if (Enum.IsDefined(filter.Logic))
|
||||
{
|
||||
if (filter.Filters is null) throw new ArgumentException("The Filters attribute is required when declaring a logic");
|
||||
bExpresionFilter = CreateFilterExpression(filter.Logic, filter.Filters, parameter);
|
||||
@ -296,7 +297,7 @@ public static class SqlSugarExtension
|
||||
return (MemberExpression)propertyExpression;
|
||||
}
|
||||
|
||||
private static Expression AddSearchPropertyByKeyword<T>(Expression propertyExpr, string keyword, FilterOperatorEnum operatorSearch = FilterOperatorEnum.Contains)
|
||||
private static MethodCallExpression AddSearchPropertyByKeyword<T>(Expression propertyExpr, string keyword, FilterOperatorEnum operatorSearch = FilterOperatorEnum.Contains)
|
||||
{
|
||||
if (propertyExpr is not MemberExpression memberExpr || memberExpr.Member is not PropertyInfo property)
|
||||
throw new ArgumentException("propertyExpr must be a property expression.", nameof(propertyExpr));
|
||||
@ -493,7 +494,7 @@ public static class SqlSugarExtension
|
||||
ArgumentNullException.ThrowIfNull(queryable);
|
||||
|
||||
// 获取实体映射信息
|
||||
var entityInfo = queryable.Context.EntityMaintenance.GetEntityInfo(typeof(T));
|
||||
var entityInfo = queryable.Context.EntityMaintenance.GetEntityInfo<T>();
|
||||
if (entityInfo?.Columns == null || entityInfo.Columns.Count == 0) return queryable.ToSqlString();
|
||||
|
||||
// 构建需要替换的字段名映射(只处理实际有差异的字段)
|
||||
@ -572,7 +573,7 @@ public static class SqlSugarExtension
|
||||
.Select(p => (p, p.GetCustomAttribute<BindTextAbbrAttribute>()!))
|
||||
.ToDictionary(t => t.p.Name, t => (t.p, t.Item2));
|
||||
|
||||
return props.Any() ? props : null;
|
||||
return props.Count != 0 ? props : null;
|
||||
});
|
||||
|
||||
// 无绑定属性或当前属性不匹配时提前结束
|
||||
@ -631,7 +632,7 @@ public static class SqlSugarExtension
|
||||
.Select(p => (p, p.GetCustomAttribute<BindSerialAttribute>()!))
|
||||
.ToDictionary(t => t.p.Name, t => (t.p, t.Item2));
|
||||
|
||||
return props.Any() ? props : null;
|
||||
return props.Count != 0 ? props : null;
|
||||
});
|
||||
|
||||
// 无绑定属性或当前属性不匹配时提前结束
|
||||
|
||||
@ -84,18 +84,18 @@ public class IdCardHelper
|
||||
long n = 0;
|
||||
if (long.TryParse(idNumber, out n) == false || n < Math.Pow(10, 14))
|
||||
{
|
||||
return false;//数字验证
|
||||
return false; // 数字验证
|
||||
}
|
||||
string address = "11x22x35x44x53x12x23x36x45x54x13x31x37x46x61x14x32x41x50x62x15x33x42x51x63x21x34x43x52x64x65x71x81x82x91";
|
||||
if (address.IndexOf(idNumber.Remove(2)) == -1)
|
||||
{
|
||||
return false;//省份验证
|
||||
return false; // 省份验证
|
||||
}
|
||||
string birth = idNumber.Substring(6, 6).Insert(4, "-").Insert(2, "-");
|
||||
DateTime time = new DateTime();
|
||||
if (DateTime.TryParse(birth, out time) == false)
|
||||
{
|
||||
return false;//生日验证
|
||||
return false; // 生日验证
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -131,7 +131,7 @@ public class IdCardHelper
|
||||
string strSex = "";
|
||||
if (idNumber.Length == 18) strSex = idNumber.Substring(14, 3);
|
||||
|
||||
//性别代码为偶数是女性奇数为男性
|
||||
// 性别代码为偶数是女性奇数为男性
|
||||
strSex = int.Parse(strSex) % 2 == 0 ? "女" : "男";
|
||||
return strSex;
|
||||
}
|
||||
@ -220,7 +220,7 @@ public class IdCardHelper
|
||||
DateTime birthDate = DateTime.Parse(birthDay);
|
||||
DateTime nowDateTime = DateTime.Now;
|
||||
int _age = nowDateTime.Year - birthDate.Year;
|
||||
//再考虑月、天的因素
|
||||
// 再考虑月、天的因素
|
||||
if (nowDateTime.Month < birthDate.Month || (nowDateTime.Month == birthDate.Month && nowDateTime.Day < birthDate.Day))
|
||||
{
|
||||
_age--;
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
"name": "admin.net.pro",
|
||||
"type": "module",
|
||||
"version": "2.4.33",
|
||||
"lastBuildTime": "2025.08.13",
|
||||
"lastBuildTime": "2025.08.15",
|
||||
"description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架",
|
||||
"author": "zuohuaijun",
|
||||
"license": "MIT",
|
||||
@ -79,8 +79,8 @@
|
||||
"vue-router": "^4.5.1",
|
||||
"vue-signature-pad": "^3.0.2",
|
||||
"vue3-tree-org": "^4.2.2",
|
||||
"vxe-pc-ui": "^4.8.14",
|
||||
"vxe-table": "^4.15.7",
|
||||
"vxe-pc-ui": "^4.8.18",
|
||||
"vxe-table": "^4.15.8",
|
||||
"xe-utils": "^3.7.8",
|
||||
"xlsx-js-style": "^1.2.0"
|
||||
},
|
||||
@ -97,7 +97,7 @@
|
||||
"@vitejs/plugin-vue-jsx": "^5.0.1",
|
||||
"@vue/compiler-sfc": "^3.5.18",
|
||||
"cli-progress": "^3.12.0",
|
||||
"code-inspector-plugin": "^1.0.5",
|
||||
"code-inspector-plugin": "^1.1.1",
|
||||
"colors": "^1.4.0",
|
||||
"dotenv": "^17.2.1",
|
||||
"eslint": "^9.33.0",
|
||||
|
||||
@ -17,9 +17,8 @@ import { Configuration } from '../configuration';
|
||||
// Some imports not used depending on template conditions
|
||||
// @ts-ignore
|
||||
import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError } from '../base';
|
||||
import { AdminNETResultSqlSugarPagedListSysOnlineUser } from '../models';
|
||||
import { PageOnlineUserInput } from '../models';
|
||||
import { SysOnlineUser } from '../models';
|
||||
import { AdminNETResultListOnlineUser } from '../models';
|
||||
import { OnlineUser } from '../models';
|
||||
/**
|
||||
* SysOnlineUserApi - axios parameter creator
|
||||
* @export
|
||||
@ -29,11 +28,11 @@ export const SysOnlineUserApiAxiosParamCreator = function (configuration?: Confi
|
||||
/**
|
||||
*
|
||||
* @summary 强制下线 🔖
|
||||
* @param {SysOnlineUser} [body]
|
||||
* @param {OnlineUser} [body]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
apiSysOnlineUserForceOfflinePost: async (body?: SysOnlineUser, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
apiSysOnlineUserForceOfflinePost: async (body?: OnlineUser, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/api/sysOnlineUser/forceOffline`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, 'https://example.com');
|
||||
@ -76,19 +75,19 @@ export const SysOnlineUserApiAxiosParamCreator = function (configuration?: Confi
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 清理在线用户(开启单设备登录时只留相同账号最后登录的)
|
||||
* @summary 获取在线用户列表 🔖
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
apiSysOnlineUserOnlinePost: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/api/sysOnlineUser/online`;
|
||||
apiSysOnlineUserOnlineUserListGet: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/api/sysOnlineUser/onlineUserList`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, 'https://example.com');
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
|
||||
const localVarRequestOptions :AxiosRequestConfig = { method: 'GET', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
@ -112,54 +111,6 @@ export const SysOnlineUserApiAxiosParamCreator = function (configuration?: Confi
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
||||
return {
|
||||
url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 获取在线用户分页列表 🔖
|
||||
* @param {PageOnlineUserInput} [body]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
apiSysOnlineUserPagePost: async (body?: PageOnlineUserInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/api/sysOnlineUser/page`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, 'https://example.com');
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication Bearer required
|
||||
// http bearer authentication required
|
||||
if (configuration && configuration.accessToken) {
|
||||
const accessToken = typeof configuration.accessToken === 'function'
|
||||
? await configuration.accessToken()
|
||||
: await configuration.accessToken;
|
||||
localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
|
||||
}
|
||||
|
||||
localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
|
||||
|
||||
const query = new URLSearchParams(localVarUrlObj.search);
|
||||
for (const key in localVarQueryParameter) {
|
||||
query.set(key, localVarQueryParameter[key]);
|
||||
}
|
||||
for (const key in options.params) {
|
||||
query.set(key, options.params[key]);
|
||||
}
|
||||
localVarUrlObj.search = (new URLSearchParams(query)).toString();
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
|
||||
localVarRequestOptions.data = needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
|
||||
|
||||
return {
|
||||
url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
|
||||
options: localVarRequestOptions,
|
||||
@ -177,11 +128,11 @@ export const SysOnlineUserApiFp = function(configuration?: Configuration) {
|
||||
/**
|
||||
*
|
||||
* @summary 强制下线 🔖
|
||||
* @param {SysOnlineUser} [body]
|
||||
* @param {OnlineUser} [body]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async apiSysOnlineUserForceOfflinePost(body?: SysOnlineUser, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<void>>> {
|
||||
async apiSysOnlineUserForceOfflinePost(body?: OnlineUser, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<void>>> {
|
||||
const localVarAxiosArgs = await SysOnlineUserApiAxiosParamCreator(configuration).apiSysOnlineUserForceOfflinePost(body, options);
|
||||
return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
|
||||
const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
|
||||
@ -190,26 +141,12 @@ export const SysOnlineUserApiFp = function(configuration?: Configuration) {
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 清理在线用户(开启单设备登录时只留相同账号最后登录的)
|
||||
* @summary 获取在线用户列表 🔖
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async apiSysOnlineUserOnlinePost(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<void>>> {
|
||||
const localVarAxiosArgs = await SysOnlineUserApiAxiosParamCreator(configuration).apiSysOnlineUserOnlinePost(options);
|
||||
return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
|
||||
const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
|
||||
return axios.request(axiosRequestArgs);
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 获取在线用户分页列表 🔖
|
||||
* @param {PageOnlineUserInput} [body]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async apiSysOnlineUserPagePost(body?: PageOnlineUserInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminNETResultSqlSugarPagedListSysOnlineUser>>> {
|
||||
const localVarAxiosArgs = await SysOnlineUserApiAxiosParamCreator(configuration).apiSysOnlineUserPagePost(body, options);
|
||||
async apiSysOnlineUserOnlineUserListGet(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminNETResultListOnlineUser>>> {
|
||||
const localVarAxiosArgs = await SysOnlineUserApiAxiosParamCreator(configuration).apiSysOnlineUserOnlineUserListGet(options);
|
||||
return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
|
||||
const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
|
||||
return axios.request(axiosRequestArgs);
|
||||
@ -227,31 +164,21 @@ export const SysOnlineUserApiFactory = function (configuration?: Configuration,
|
||||
/**
|
||||
*
|
||||
* @summary 强制下线 🔖
|
||||
* @param {SysOnlineUser} [body]
|
||||
* @param {OnlineUser} [body]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async apiSysOnlineUserForceOfflinePost(body?: SysOnlineUser, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
|
||||
async apiSysOnlineUserForceOfflinePost(body?: OnlineUser, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
|
||||
return SysOnlineUserApiFp(configuration).apiSysOnlineUserForceOfflinePost(body, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 清理在线用户(开启单设备登录时只留相同账号最后登录的)
|
||||
* @summary 获取在线用户列表 🔖
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async apiSysOnlineUserOnlinePost(options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
|
||||
return SysOnlineUserApiFp(configuration).apiSysOnlineUserOnlinePost(options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary 获取在线用户分页列表 🔖
|
||||
* @param {PageOnlineUserInput} [body]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async apiSysOnlineUserPagePost(body?: PageOnlineUserInput, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminNETResultSqlSugarPagedListSysOnlineUser>> {
|
||||
return SysOnlineUserApiFp(configuration).apiSysOnlineUserPagePost(body, options).then((request) => request(axios, basePath));
|
||||
async apiSysOnlineUserOnlineUserListGet(options?: AxiosRequestConfig): Promise<AxiosResponse<AdminNETResultListOnlineUser>> {
|
||||
return SysOnlineUserApiFp(configuration).apiSysOnlineUserOnlineUserListGet(options).then((request) => request(axios, basePath));
|
||||
},
|
||||
};
|
||||
};
|
||||
@ -266,33 +193,22 @@ export class SysOnlineUserApi extends BaseAPI {
|
||||
/**
|
||||
*
|
||||
* @summary 强制下线 🔖
|
||||
* @param {SysOnlineUser} [body]
|
||||
* @param {OnlineUser} [body]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof SysOnlineUserApi
|
||||
*/
|
||||
public async apiSysOnlineUserForceOfflinePost(body?: SysOnlineUser, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
|
||||
public async apiSysOnlineUserForceOfflinePost(body?: OnlineUser, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
|
||||
return SysOnlineUserApiFp(this.configuration).apiSysOnlineUserForceOfflinePost(body, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @summary 清理在线用户(开启单设备登录时只留相同账号最后登录的)
|
||||
* @summary 获取在线用户列表 🔖
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof SysOnlineUserApi
|
||||
*/
|
||||
public async apiSysOnlineUserOnlinePost(options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
|
||||
return SysOnlineUserApiFp(this.configuration).apiSysOnlineUserOnlinePost(options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @summary 获取在线用户分页列表 🔖
|
||||
* @param {PageOnlineUserInput} [body]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof SysOnlineUserApi
|
||||
*/
|
||||
public async apiSysOnlineUserPagePost(body?: PageOnlineUserInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminNETResultSqlSugarPagedListSysOnlineUser>> {
|
||||
return SysOnlineUserApiFp(this.configuration).apiSysOnlineUserPagePost(body, options).then((request) => request(this.axios, this.basePath));
|
||||
public async apiSysOnlineUserOnlineUserListGet(options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminNETResultListOnlineUser>> {
|
||||
return SysOnlineUserApiFp(this.configuration).apiSysOnlineUserOnlineUserListGet(options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,20 +12,20 @@
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
import { SqlSugarPagedListSysOnlineUser } from './sql-sugar-paged-list-sys-online-user';
|
||||
import { OnlineUser } from './online-user';
|
||||
/**
|
||||
* 全局返回结果
|
||||
*
|
||||
* @export
|
||||
* @interface AdminNETResultSqlSugarPagedListSysOnlineUser
|
||||
* @interface AdminNETResultListOnlineUser
|
||||
*/
|
||||
export interface AdminNETResultSqlSugarPagedListSysOnlineUser {
|
||||
export interface AdminNETResultListOnlineUser {
|
||||
|
||||
/**
|
||||
* 状态码
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof AdminNETResultSqlSugarPagedListSysOnlineUser
|
||||
* @memberof AdminNETResultListOnlineUser
|
||||
*/
|
||||
code?: number;
|
||||
|
||||
@ -33,7 +33,7 @@ export interface AdminNETResultSqlSugarPagedListSysOnlineUser {
|
||||
* 类型success、warning、error
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof AdminNETResultSqlSugarPagedListSysOnlineUser
|
||||
* @memberof AdminNETResultListOnlineUser
|
||||
*/
|
||||
type?: string | null;
|
||||
|
||||
@ -41,21 +41,23 @@ export interface AdminNETResultSqlSugarPagedListSysOnlineUser {
|
||||
* 错误信息
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof AdminNETResultSqlSugarPagedListSysOnlineUser
|
||||
* @memberof AdminNETResultListOnlineUser
|
||||
*/
|
||||
message?: string | null;
|
||||
|
||||
/**
|
||||
* @type {SqlSugarPagedListSysOnlineUser}
|
||||
* @memberof AdminNETResultSqlSugarPagedListSysOnlineUser
|
||||
* 数据
|
||||
*
|
||||
* @type {Array<OnlineUser>}
|
||||
* @memberof AdminNETResultListOnlineUser
|
||||
*/
|
||||
result?: SqlSugarPagedListSysOnlineUser;
|
||||
result?: Array<OnlineUser> | null;
|
||||
|
||||
/**
|
||||
* 附加数据
|
||||
*
|
||||
* @type {any}
|
||||
* @memberof AdminNETResultSqlSugarPagedListSysOnlineUser
|
||||
* @memberof AdminNETResultListOnlineUser
|
||||
*/
|
||||
extras?: any | null;
|
||||
|
||||
@ -63,7 +65,7 @@ export interface AdminNETResultSqlSugarPagedListSysOnlineUser {
|
||||
* 时间
|
||||
*
|
||||
* @type {Date}
|
||||
* @memberof AdminNETResultSqlSugarPagedListSysOnlineUser
|
||||
* @memberof AdminNETResultListOnlineUser
|
||||
*/
|
||||
time?: Date;
|
||||
}
|
||||
@ -64,6 +64,7 @@ export * from './admin-netresult-list-list-string';
|
||||
export * from './admin-netresult-list-log-vis-output';
|
||||
export * from './admin-netresult-list-menu-output';
|
||||
export * from './admin-netresult-list-nu-get-package';
|
||||
export * from './admin-netresult-list-online-user';
|
||||
export * from './admin-netresult-list-pos-output';
|
||||
export * from './admin-netresult-list-report-data-source-output';
|
||||
export * from './admin-netresult-list-role-output';
|
||||
@ -121,7 +122,6 @@ export * from './admin-netresult-sql-sugar-paged-list-sys-log-op';
|
||||
export * from './admin-netresult-sql-sugar-paged-list-sys-log-vis';
|
||||
export * from './admin-netresult-sql-sugar-paged-list-sys-notice';
|
||||
export * from './admin-netresult-sql-sugar-paged-list-sys-notice-user';
|
||||
export * from './admin-netresult-sql-sugar-paged-list-sys-online-user';
|
||||
export * from './admin-netresult-sql-sugar-paged-list-sys-plugin';
|
||||
export * from './admin-netresult-sql-sugar-paged-list-sys-print';
|
||||
export * from './admin-netresult-sql-sugar-paged-list-sys-region';
|
||||
@ -350,6 +350,7 @@ export * from './nu-get-package';
|
||||
export * from './number-format-info';
|
||||
export * from './oauth-user-input';
|
||||
export * from './oauth-user-output';
|
||||
export * from './online-user';
|
||||
export * from './open-access-output';
|
||||
export * from './order-by-type';
|
||||
export * from './page-code-gen-input';
|
||||
@ -365,7 +366,6 @@ export * from './page-ldap-input';
|
||||
export * from './page-log-input';
|
||||
export * from './page-msg-log-input';
|
||||
export * from './page-notice-input';
|
||||
export * from './page-online-user-input';
|
||||
export * from './page-op-log-input';
|
||||
export * from './page-open-access-input';
|
||||
export * from './page-plugin-input';
|
||||
@ -455,7 +455,6 @@ export * from './sql-sugar-paged-list-sys-log-op';
|
||||
export * from './sql-sugar-paged-list-sys-log-vis';
|
||||
export * from './sql-sugar-paged-list-sys-notice';
|
||||
export * from './sql-sugar-paged-list-sys-notice-user';
|
||||
export * from './sql-sugar-paged-list-sys-online-user';
|
||||
export * from './sql-sugar-paged-list-sys-plugin';
|
||||
export * from './sql-sugar-paged-list-sys-print';
|
||||
export * from './sql-sugar-paged-list-sys-region';
|
||||
@ -502,7 +501,6 @@ export * from './sys-menu-meta';
|
||||
export * from './sys-notice';
|
||||
export * from './sys-notice-user';
|
||||
export * from './sys-oauth-user';
|
||||
export * from './sys-online-user';
|
||||
export * from './sys-org';
|
||||
export * from './sys-plugin';
|
||||
export * from './sys-pos-import-body';
|
||||
|
||||
@ -14,42 +14,34 @@
|
||||
|
||||
import { LoginModeEnum } from './login-mode-enum';
|
||||
/**
|
||||
* 系统在线用户表
|
||||
* 在线用户
|
||||
*
|
||||
* @export
|
||||
* @interface SysOnlineUser
|
||||
* @interface OnlineUser
|
||||
*/
|
||||
export interface SysOnlineUser {
|
||||
|
||||
/**
|
||||
* 雪花Id
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof SysOnlineUser
|
||||
*/
|
||||
id?: number;
|
||||
|
||||
/**
|
||||
* 租户Id
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof SysOnlineUser
|
||||
*/
|
||||
tenantId?: number | null;
|
||||
export interface OnlineUser {
|
||||
|
||||
/**
|
||||
* 连接Id
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof SysOnlineUser
|
||||
* @memberof OnlineUser
|
||||
*/
|
||||
connectionId?: string | null;
|
||||
|
||||
/**
|
||||
* 租户Id
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof OnlineUser
|
||||
*/
|
||||
tenantId?: number;
|
||||
|
||||
/**
|
||||
* 用户Id
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof SysOnlineUser
|
||||
* @memberof OnlineUser
|
||||
*/
|
||||
userId?: number;
|
||||
|
||||
@ -57,31 +49,31 @@ export interface SysOnlineUser {
|
||||
* 账号
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof SysOnlineUser
|
||||
* @memberof OnlineUser
|
||||
*/
|
||||
userName: string;
|
||||
userName?: string | null;
|
||||
|
||||
/**
|
||||
* 真实姓名
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof SysOnlineUser
|
||||
* @memberof OnlineUser
|
||||
*/
|
||||
realName?: string | null;
|
||||
|
||||
/**
|
||||
* 连接时间
|
||||
* 上线时间
|
||||
*
|
||||
* @type {Date}
|
||||
* @memberof SysOnlineUser
|
||||
* @memberof OnlineUser
|
||||
*/
|
||||
time?: Date | null;
|
||||
time?: Date;
|
||||
|
||||
/**
|
||||
* 连接IP
|
||||
* 登录IP
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof SysOnlineUser
|
||||
* @memberof OnlineUser
|
||||
*/
|
||||
ip?: string | null;
|
||||
|
||||
@ -89,7 +81,7 @@ export interface SysOnlineUser {
|
||||
* 浏览器
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof SysOnlineUser
|
||||
* @memberof OnlineUser
|
||||
*/
|
||||
browser?: string | null;
|
||||
|
||||
@ -97,13 +89,13 @@ export interface SysOnlineUser {
|
||||
* 操作系统
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof SysOnlineUser
|
||||
* @memberof OnlineUser
|
||||
*/
|
||||
os?: string | null;
|
||||
|
||||
/**
|
||||
* @type {LoginModeEnum}
|
||||
* @memberof SysOnlineUser
|
||||
* @memberof OnlineUser
|
||||
*/
|
||||
loginMode?: LoginModeEnum;
|
||||
|
||||
@ -111,7 +103,7 @@ export interface SysOnlineUser {
|
||||
* 登录设备
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof SysOnlineUser
|
||||
* @memberof OnlineUser
|
||||
*/
|
||||
device?: string | null;
|
||||
}
|
||||
@ -1,100 +0,0 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Admin.NET 通用权限开发平台
|
||||
* 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
|
||||
*
|
||||
* OpenAPI spec version: 1.0.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by the swagger code generator program.
|
||||
* https://github.com/swagger-api/swagger-codegen.git
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
import { Filter } from './filter';
|
||||
import { Search } from './search';
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @export
|
||||
* @interface PageOnlineUserInput
|
||||
*/
|
||||
export interface PageOnlineUserInput {
|
||||
|
||||
/**
|
||||
* @type {Search}
|
||||
* @memberof PageOnlineUserInput
|
||||
*/
|
||||
search?: Search;
|
||||
|
||||
/**
|
||||
* 模糊查询关键字
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof PageOnlineUserInput
|
||||
*/
|
||||
keyword?: string | null;
|
||||
|
||||
/**
|
||||
* @type {Filter}
|
||||
* @memberof PageOnlineUserInput
|
||||
*/
|
||||
filter?: Filter;
|
||||
|
||||
/**
|
||||
* 当前页码
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof PageOnlineUserInput
|
||||
*/
|
||||
page?: number;
|
||||
|
||||
/**
|
||||
* 页码容量
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof PageOnlineUserInput
|
||||
*/
|
||||
pageSize?: number;
|
||||
|
||||
/**
|
||||
* 排序字段
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof PageOnlineUserInput
|
||||
*/
|
||||
field?: string | null;
|
||||
|
||||
/**
|
||||
* 排序方向
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof PageOnlineUserInput
|
||||
*/
|
||||
order?: string | null;
|
||||
|
||||
/**
|
||||
* 降序排序
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof PageOnlineUserInput
|
||||
*/
|
||||
descStr?: string | null;
|
||||
|
||||
/**
|
||||
* 账号名称
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof PageOnlineUserInput
|
||||
*/
|
||||
userName?: string | null;
|
||||
|
||||
/**
|
||||
* 真实姓名
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof PageOnlineUserInput
|
||||
*/
|
||||
realName?: string | null;
|
||||
}
|
||||
@ -1,79 +0,0 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Admin.NET 通用权限开发平台
|
||||
* 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
|
||||
*
|
||||
* OpenAPI spec version: 1.0.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by the swagger code generator program.
|
||||
* https://github.com/swagger-api/swagger-codegen.git
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
import { SysOnlineUser } from './sys-online-user';
|
||||
/**
|
||||
* 分页泛型集合
|
||||
*
|
||||
* @export
|
||||
* @interface SqlSugarPagedListSysOnlineUser
|
||||
*/
|
||||
export interface SqlSugarPagedListSysOnlineUser {
|
||||
|
||||
/**
|
||||
* 页码
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof SqlSugarPagedListSysOnlineUser
|
||||
*/
|
||||
page?: number;
|
||||
|
||||
/**
|
||||
* 页容量
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof SqlSugarPagedListSysOnlineUser
|
||||
*/
|
||||
pageSize?: number;
|
||||
|
||||
/**
|
||||
* 总条数
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof SqlSugarPagedListSysOnlineUser
|
||||
*/
|
||||
total?: number;
|
||||
|
||||
/**
|
||||
* 总页数
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof SqlSugarPagedListSysOnlineUser
|
||||
*/
|
||||
totalPages?: number;
|
||||
|
||||
/**
|
||||
* 当前页集合
|
||||
*
|
||||
* @type {Array<SysOnlineUser>}
|
||||
* @memberof SqlSugarPagedListSysOnlineUser
|
||||
*/
|
||||
items?: Array<SysOnlineUser> | null;
|
||||
|
||||
/**
|
||||
* 是否有上一页
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof SqlSugarPagedListSysOnlineUser
|
||||
*/
|
||||
hasPrevPage?: boolean;
|
||||
|
||||
/**
|
||||
* 是否有下一页
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof SqlSugarPagedListSysOnlineUser
|
||||
*/
|
||||
hasNextPage?: boolean;
|
||||
}
|
||||
@ -47,12 +47,15 @@
|
||||
|
||||
<script lang="ts" setup name="sendMessage">
|
||||
import { reactive, ref } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { useUserInfo } from '/@/stores/userInfo';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import Editor from '/@/components/editor/index.vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import Editor from '/@/components/editor/index.vue';
|
||||
import { signalR } from '../signalR';
|
||||
|
||||
const i18n = useI18n();
|
||||
const stores = useUserInfo();
|
||||
const { userInfos } = storeToRefs(stores);
|
||||
const props = defineProps({
|
||||
@ -85,6 +88,7 @@ const submit = () => {
|
||||
|
||||
// 发送消息给某人(对应后台Hub的方法名称)
|
||||
signalR.send('SendMessageToUser', state.ruleForm);
|
||||
ElMessage.success(i18n.t('message.list.operationSuccessful'));
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -1,43 +1,15 @@
|
||||
<template>
|
||||
<div class="sys-onlineUser-container">
|
||||
<el-drawer v-model="state.isVisible" size="35%">
|
||||
<el-drawer v-model="state.isVisible" size="50%">
|
||||
<template #header>
|
||||
<div style="color: #fff">
|
||||
<el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-UserFilled /> </el-icon>
|
||||
<span> {{ $t('message.list.onlineUserList') }} </span>
|
||||
</div>
|
||||
</template>
|
||||
<el-card shadow="hover" :body-style="{ padding: '5px 5px 0 5px', display: 'flex', width: '100%', height: '100%', alignItems: 'start' }">
|
||||
<el-form :model="state.queryParams" ref="queryForm" :show-message="false" :inlineMessage="true" label-width="auto" style="flex: 1 1 0%">
|
||||
<el-row :gutter="10">
|
||||
<el-col class="mb5" :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
|
||||
<el-form-item :label="$t('message.list.account')" prop="userName">
|
||||
<el-input v-model="state.queryParams.userName" :placeholder="$t('message.list.account')" clearable @keyup.enter.native="handleQuery(true)" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col class="mb5" :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
|
||||
<el-form-item :label="$t('message.list.realName')" prop="realName">
|
||||
<el-input v-model="state.queryParams.realName" :placeholder="$t('message.list.realName')" clearable @keyup.enter.native="handleQuery(true)" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<el-divider style="height: calc(100% - 5px); margin: 0 10px" direction="vertical" />
|
||||
|
||||
<el-row>
|
||||
<el-col>
|
||||
<el-button-group>
|
||||
<el-button type="primary" icon="ele-Search" @click="handleQuery(true)" :loading="options.loading"> {{ $t('message.list.query') }} </el-button>
|
||||
<el-button icon="ele-Refresh" @click="resetQuery" :loading="options.loading"> {{ $t('message.list.reset') }} </el-button>
|
||||
</el-button-group>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<el-card class="full-table" shadow="hover" style="margin-top: 5px">
|
||||
<vxe-grid ref="xGrid" class="xGrid-style" v-bind="options" v-on="gridEvents">
|
||||
<vxe-grid ref="xGrid" class="xGrid-style" v-bind="options">
|
||||
<template #toolbar_buttons> </template>
|
||||
<template #toolbar_tools> </template>
|
||||
<template #empty>
|
||||
@ -62,21 +34,19 @@
|
||||
<!-- 在线用户 -->
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { ElMessageBox, ElNotification } from 'element-plus';
|
||||
import { VxeGridInstance, VxeGridListeners, VxeGridPropTypes } from 'vxe-table';
|
||||
import { ElMessage, ElMessageBox, ElNotification } from 'element-plus';
|
||||
import { VxeGridInstance } from 'vxe-table';
|
||||
import { useVxeTable } from '/@/hooks/useVxeTableOptionsHook';
|
||||
import { useThemeConfig } from '/@/stores/themeConfig';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { Local } from '/@/utils/storage';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { throttle } from 'lodash-es';
|
||||
import { signalR } from './signalR';
|
||||
|
||||
import SendMessage from '/@/views/system/onlineUser/component/sendMessage.vue';
|
||||
|
||||
import { getAPI, clearAccessTokens } from '/@/utils/axios-utils';
|
||||
import { SysOnlineUserApi, SysAuthApi } from '/@/api-services/system/api';
|
||||
import { SysOnlineUser, PageOnlineUserInput } from '/@/api-services/system/models';
|
||||
import { SysAuthApi, SysOnlineUserApi } from '/@/api-services/system/api';
|
||||
import { OnlineUser } from '/@/api-services/system/models';
|
||||
|
||||
const storesThemeConfig = useThemeConfig();
|
||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||
@ -85,25 +55,14 @@ const xGrid = ref<VxeGridInstance>();
|
||||
const sendMessageRef = ref<InstanceType<typeof SendMessage>>();
|
||||
const state = reactive({
|
||||
isVisible: false,
|
||||
queryParams: {
|
||||
userName: undefined,
|
||||
realName: undefined,
|
||||
},
|
||||
localPageParam: {
|
||||
pageSize: 50 as number,
|
||||
defaultSort: { field: 'orderNo', order: 'asc', descStr: 'desc' },
|
||||
},
|
||||
onlineUserList: [] as Array<SysOnlineUser>, // 在线用户列表
|
||||
lastUserState: {
|
||||
online: false,
|
||||
realName: '',
|
||||
}, // 最后接收的用户变更状态信息
|
||||
}, // 用户状态信息
|
||||
});
|
||||
|
||||
// 本地存储参数
|
||||
const localPageParamKey = 'localPageParam:sysOnlineUser';
|
||||
// 表格参数配置
|
||||
const options = useVxeTable<SysOnlineUser>(
|
||||
const options = useVxeTable<OnlineUser>(
|
||||
{
|
||||
id: 'sysOnlineUser',
|
||||
name: t('message.list.onlineUserList'),
|
||||
@ -112,48 +71,43 @@ const options = useVxeTable<SysOnlineUser>(
|
||||
{ type: 'seq', title: t('message.list.seq'), width: 50, fixed: 'left' },
|
||||
{ field: 'userName', title: t('message.list.account'), minWidth: 110, showOverflow: 'tooltip' },
|
||||
{ field: 'realName', title: t('message.list.realName'), minWidth: 110, showOverflow: 'tooltip' },
|
||||
{ field: 'time', title: t('message.list.loginTime'), minWidth: 120, showOverflow: 'tooltip' },
|
||||
{ field: 'ip', title: t('message.list.ipAddress'), minWidth: 100, showOverflow: 'tooltip' },
|
||||
{ field: 'browser', title: t('message.list.browser'), minWidth: 160, showOverflow: 'tooltip' },
|
||||
// { field: 'connectionId', title: '连接Id', minWidth: 160, showOverflow: 'tooltip', sortable: true },
|
||||
{ field: 'time', title: t('message.list.loginTime'), minWidth: 120, showOverflow: 'tooltip' },
|
||||
{ field: 'buttons', title: t('message.list.operation'), fixed: 'right', width: 100, showOverflow: true, slots: { default: 'row_buttons' } },
|
||||
],
|
||||
},
|
||||
// vxeGrid配置参数(此处可覆写任何参数),参考vxe-table官方文档
|
||||
{
|
||||
// 代理配置
|
||||
proxyConfig: { autoLoad: true, ajax: { query: ({ page, sort }) => handleQueryApi(page, sort) } },
|
||||
// 排序配置
|
||||
sortConfig: { defaultSort: Local.get(localPageParamKey)?.defaultSort || state.localPageParam.defaultSort },
|
||||
proxyConfig: { enabled: false },
|
||||
// 分页配置
|
||||
pagerConfig: { pageSize: Local.get(localPageParamKey)?.pageSize || state.localPageParam.pageSize },
|
||||
pagerConfig: { enabled: false },
|
||||
// 工具栏配置
|
||||
toolbarConfig: { export: true },
|
||||
toolbarConfig: { enabled: false },
|
||||
}
|
||||
);
|
||||
|
||||
// 页面初始化
|
||||
onMounted(async () => {
|
||||
state.localPageParam = Local.get(localPageParamKey) || state.localPageParam;
|
||||
// 在线用户列表
|
||||
signalR.off('OnlineUserList');
|
||||
// 监听用户在线列表
|
||||
signalR.on('OnlineUserList', async (data: any) => {
|
||||
state.onlineUserList = data.userList;
|
||||
state.lastUserState = {
|
||||
online: data.online,
|
||||
realName: data.realName,
|
||||
};
|
||||
options.data = data.userList;
|
||||
|
||||
// 开启上线下线通知
|
||||
if (themeConfig.value.onlineNotice) notificationThrottle();
|
||||
|
||||
// // 自动查询一次
|
||||
// await handleQuery();
|
||||
console.log('dddddd');
|
||||
// 上下线通知
|
||||
if (!themeConfig.value.onlineNotice) return;
|
||||
ElNotification({
|
||||
title: '提示',
|
||||
message: `${data.online ? `【${data.realName}】上线了` : `【${data.realName}】离开了`}`,
|
||||
type: `${data.online ? 'info' : 'error'}`,
|
||||
position: 'bottom-right',
|
||||
});
|
||||
});
|
||||
// 强制下线
|
||||
signalR.off('ForceOffline');
|
||||
|
||||
// 监听用户强制下线
|
||||
signalR.on('ForceOffline', async (data: any) => {
|
||||
// console.log('强制下线', data);
|
||||
await signalR.stop();
|
||||
|
||||
await getAPI(SysAuthApi).apiSysAuthLogoutPost();
|
||||
@ -161,61 +115,13 @@ onMounted(async () => {
|
||||
});
|
||||
});
|
||||
|
||||
// 通知提示节流
|
||||
const notificationThrottle = throttle(
|
||||
function () {
|
||||
ElNotification({
|
||||
title: '提示',
|
||||
message: `${state.lastUserState.online ? `【${state.lastUserState.realName}】上线了` : `【${state.lastUserState.realName}】离开了`}`,
|
||||
type: `${state.lastUserState.online ? 'info' : 'error'}`,
|
||||
position: 'bottom-right',
|
||||
});
|
||||
},
|
||||
3000,
|
||||
{
|
||||
leading: true,
|
||||
trailing: false,
|
||||
}
|
||||
);
|
||||
|
||||
// 打开页面
|
||||
const openDrawer = async () => {
|
||||
state.isVisible = true;
|
||||
await handleQuery();
|
||||
};
|
||||
|
||||
// 查询api
|
||||
const handleQueryApi = async (page: VxeGridPropTypes.ProxyAjaxQueryPageParams, sort: VxeGridPropTypes.ProxyAjaxQuerySortCheckedParams) => {
|
||||
const params = Object.assign(state.queryParams, { page: page.currentPage, pageSize: page.pageSize, field: sort.field, order: sort.order, descStr: 'desc' }) as PageOnlineUserInput;
|
||||
return getAPI(SysOnlineUserApi).apiSysOnlineUserPagePost(params);
|
||||
};
|
||||
|
||||
// 查询操作
|
||||
const handleQuery = async (reset = false) => {
|
||||
options.loading = true;
|
||||
reset ? await xGrid.value?.commitProxy('reload') : await xGrid.value?.commitProxy('query');
|
||||
options.loading = false;
|
||||
};
|
||||
|
||||
// 重置操作
|
||||
const resetQuery = async () => {
|
||||
state.queryParams.userName = undefined;
|
||||
state.queryParams.realName = undefined;
|
||||
await handleQuery();
|
||||
};
|
||||
|
||||
// 表格事件
|
||||
const gridEvents: VxeGridListeners<SysOnlineUser> = {
|
||||
// 只对 pager-config 配置时有效,分页发生改变时会触发该事件
|
||||
async pageChange({ pageSize }) {
|
||||
state.localPageParam.pageSize = pageSize;
|
||||
Local.set(localPageParamKey, state.localPageParam);
|
||||
},
|
||||
// 当排序条件发生变化时会触发该事件
|
||||
async sortChange({ field, order }) {
|
||||
state.localPageParam.defaultSort = { field: field, order: order!, descStr: 'desc' };
|
||||
Local.set(localPageParamKey, state.localPageParam);
|
||||
},
|
||||
// 获取用户在线列表
|
||||
var res = await getAPI(SysOnlineUserApi).apiSysOnlineUserOnlineUserListGet();
|
||||
options.data = res.data.result ?? [];
|
||||
};
|
||||
|
||||
// 发送消息
|
||||
@ -231,8 +137,9 @@ const forceOffline = async (row: any) => {
|
||||
type: 'warning',
|
||||
})
|
||||
.then(async () => {
|
||||
// 调用服务器上指定的方法名称和参数
|
||||
await signalR.send('ForceOffline', { connectionId: row.connectionId }).catch(function (err: any) {
|
||||
console.log(err);
|
||||
ElMessage.error(err);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
@ -54,7 +54,7 @@ connection.on('ReceiveMessage', (message: any) => {
|
||||
type: message.messageType.toString().toLowerCase(),
|
||||
position: 'top-right',
|
||||
dangerouslyUseHTMLString: true,
|
||||
duration: 0,
|
||||
duration: 5000,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -169,7 +169,7 @@ const handleEdit = async (row: any) => {
|
||||
|
||||
// 删除
|
||||
const handleDelete = (row: any) => {
|
||||
ElMessageBox.confirm(i18n.t('message.list.confirmDeletePosition', { name: row.name }), i18n.t('message.list.hint'), {
|
||||
ElMessageBox.confirm(i18n.t('message.list.confirmDelete', { name: row.name }), i18n.t('message.list.hint'), {
|
||||
confirmButtonText: i18n.t('message.list.confirmButtonText'),
|
||||
cancelButtonText: i18n.t('message.list.cancelButtonText'),
|
||||
type: 'warning',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user