mirror of
https://github.com/nsnail/NetAdmin.git
synced 2025-04-19 21:02:49 +08:00
feat: ✨ 框架代码同步
This commit is contained in:
parent
9504c96f40
commit
725662693b
@ -117,4 +117,4 @@ biz-infra-->infra
|
|||||||
| JavaScript | JavaScript解析器 | [Terser](https://github.com/terser/terser) |
|
| JavaScript | JavaScript解析器 | [Terser](https://github.com/terser/terser) |
|
||||||
| JavaScript | 代码质量检查 | [ESLint](https://github.com/eslint/eslint) |
|
| JavaScript | 代码质量检查 | [ESLint](https://github.com/eslint/eslint) |
|
||||||
| JavaScript | 代码格式化工具 | [Prettier](https://github.com/prettier/prettier) |
|
| JavaScript | 代码格式化工具 | [Prettier](https://github.com/prettier/prettier) |
|
||||||
| JavaScript | 标准加密库 | [crypto-js](https://github.com/brix/crypto-js) |
|
| JavaScript | 标准加密库 | [crypto-js](https://github.com/brix/crypto-js) |
|
@ -3,7 +3,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"cz-git": "^1.11.0",
|
"cz-git": "^1.11.0",
|
||||||
"commitizen": "^4.3.1",
|
"commitizen": "^4.3.1",
|
||||||
"prettier": "^3.4.1",
|
"prettier": "^3.4.2",
|
||||||
"standard-version": "^9.5.0"
|
"standard-version": "^9.5.0"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
@ -11,4 +11,4 @@
|
|||||||
"path": "node_modules/cz-git"
|
"path": "node_modules/cz-git"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
using NetAdmin.Domain.DbMaps.Sys;
|
using NetAdmin.Domain.DbMaps.Sys;
|
||||||
|
|
||||||
namespace NetAdmin.Domain.Dto.Sys.Api;
|
namespace NetAdmin.Domain.Dto.Sys.Api;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace NetAdmin.Domain.Dto.Sys.JobRecord;
|
namespace NetAdmin.Domain.Dto.Sys.JobRecord;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 请求:编辑计划作业执行记录
|
/// 请求:编辑计划作业执行记录
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace NetAdmin.Domain.Dto.Sys.LoginLog;
|
namespace NetAdmin.Domain.Dto.Sys.LoginLog;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 请求:编辑登录日志
|
/// 请求:编辑登录日志
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace NetAdmin.Domain.Dto.Sys.RequestLog;
|
namespace NetAdmin.Domain.Dto.Sys.RequestLog;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 请求:编辑请求日志
|
/// 请求:编辑请求日志
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace NetAdmin.Domain.Dto.Sys.RequestLogDetail;
|
namespace NetAdmin.Domain.Dto.Sys.RequestLogDetail;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 请求:编辑请求日志明细
|
/// 请求:编辑请求日志明细
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace NetAdmin.Domain.Dto.Sys.SiteMsgDept;
|
namespace NetAdmin.Domain.Dto.Sys.SiteMsgDept;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 请求:编辑站内信-部门映射
|
/// 请求:编辑站内信-部门映射
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace NetAdmin.Domain.Dto.Sys.SiteMsgFlag;
|
namespace NetAdmin.Domain.Dto.Sys.SiteMsgFlag;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 请求:编辑站内信标记
|
/// 请求:编辑站内信标记
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace NetAdmin.Domain.Dto.Sys.SiteMsgRole;
|
namespace NetAdmin.Domain.Dto.Sys.SiteMsgRole;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 请求:编辑站内信-角色映射
|
/// 请求:编辑站内信-角色映射
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace NetAdmin.Domain.Dto.Sys.SiteMsgUser;
|
namespace NetAdmin.Domain.Dto.Sys.SiteMsgUser;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 请求:编辑站内信-用户映射
|
/// 请求:编辑站内信-用户映射
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
using NetAdmin.Domain.DbMaps.Sys;
|
||||||
|
|
||||||
|
namespace NetAdmin.Domain.Dto.Sys.UserRole;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 请求:创建用户-角色映射
|
||||||
|
/// </summary>
|
||||||
|
public record CreateUserRoleReq : Sys_UserRole;
|
@ -0,0 +1,6 @@
|
|||||||
|
namespace NetAdmin.Domain.Dto.Sys.UserRole;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 请求:编辑用户-角色映射
|
||||||
|
/// </summary>
|
||||||
|
public sealed record EditUserRoleReq : CreateUserRoleReq;
|
@ -0,0 +1,8 @@
|
|||||||
|
using NetAdmin.Domain.DbMaps.Sys;
|
||||||
|
|
||||||
|
namespace NetAdmin.Domain.Dto.Sys.UserRole;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 请求:查询用户-角色映射
|
||||||
|
/// </summary>
|
||||||
|
public sealed record QueryUserRoleReq : Sys_UserRole;
|
@ -0,0 +1,13 @@
|
|||||||
|
using NetAdmin.Domain.DbMaps.Sys;
|
||||||
|
|
||||||
|
namespace NetAdmin.Domain.Dto.Sys.UserRole;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 响应:查询用户-角色映射
|
||||||
|
/// </summary>
|
||||||
|
public sealed record QueryUserRoleRsp : Sys_UserRole
|
||||||
|
{
|
||||||
|
/// <inheritdoc cref="EntityBase{T}.Id" />
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||||
|
public override long Id { get; init; }
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
namespace NetAdmin.Domain.Dto.Sys.VerifyCode;
|
namespace NetAdmin.Domain.Dto.Sys.VerifyCode;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 请求:编辑验证码
|
/// 请求:编辑验证码
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace NetAdmin.Domain.Dto.Tpl.Example;
|
namespace NetAdmin.Domain.Dto.Tpl.Example;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 请求:编辑示例
|
/// 请求:编辑示例
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CronExpressionDescriptor" Version="2.36.0"/>
|
<PackageReference Include="CronExpressionDescriptor" Version="2.36.0"/>
|
||||||
<PackageReference Include="Cronos" Version="0.8.4"/>
|
<PackageReference Include="Cronos" Version="0.9.0"/>
|
||||||
<PackageReference Include="NetAdmin.CsvHelper" Version="1.0.0"/>
|
<PackageReference Include="NetAdmin.CsvHelper" Version="1.0.0"/>
|
||||||
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14"/>
|
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace NetAdmin.Infrastructure.Enums;
|
namespace NetAdmin.Infrastructure.Enums;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 枚举扩展方法
|
/// 枚举扩展方法
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace NetAdmin.Infrastructure.Extensions;
|
namespace NetAdmin.Infrastructure.Extensions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// CountryCodes 扩展方法
|
/// CountryCodes 扩展方法
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using NetAdmin.Domain.Dto.Sys.Role;
|
using NetAdmin.Domain.Dto.Sys.Role;
|
||||||
|
using NetAdmin.Domain.Dto.Sys.UserRole;
|
||||||
|
|
||||||
namespace NetAdmin.SysComponent.Application.Modules.Sys;
|
namespace NetAdmin.SysComponent.Application.Modules.Sys;
|
||||||
|
|
||||||
@ -25,4 +26,9 @@ public interface IRoleModule : ICrudModule<CreateRoleReq, QueryRoleRsp // 创建
|
|||||||
/// 设置是否忽略权限控制
|
/// 设置是否忽略权限控制
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task<int> SetIgnorePermissionControlAsync(SetIgnorePermissionControlReq req);
|
Task<int> SetIgnorePermissionControlAsync(SetIgnorePermissionControlReq req);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色用户映射分组计数
|
||||||
|
/// </summary>
|
||||||
|
Task<IOrderedEnumerable<KeyValuePair<IImmutableDictionary<string, string>, int>>> UserCountByAsync(QueryReq<QueryUserRoleReq> req);
|
||||||
}
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
using NetAdmin.Domain.Dto.Sys.UserRole;
|
||||||
|
|
||||||
|
namespace NetAdmin.SysComponent.Application.Modules.Sys;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户-角色映射模块
|
||||||
|
/// </summary>
|
||||||
|
public interface IUserRoleModule : ICrudModule<CreateUserRoleReq, QueryUserRoleRsp // 创建类型
|
||||||
|
, EditUserRoleReq // 编辑类型
|
||||||
|
, QueryUserRoleReq, QueryUserRoleRsp // 查询类型
|
||||||
|
, DelReq // 删除类型
|
||||||
|
>;
|
@ -1,3 +1,3 @@
|
|||||||
<wpf:ResourceDictionary xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xml:space="preserve">
|
<wpf:ResourceDictionary xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xml:space="preserve">
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Csys_005Csession/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Csys_005Csession/@EntryIndexedValue">True</s:Boolean>
|
||||||
</wpf:ResourceDictionary>
|
</wpf:ResourceDictionary>
|
@ -0,0 +1,6 @@
|
|||||||
|
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户角-色映射服务
|
||||||
|
/// </summary>
|
||||||
|
public interface IUserRoleService : IService, IUserRoleModule;
|
@ -1,10 +1,11 @@
|
|||||||
using NetAdmin.Domain.DbMaps.Sys;
|
using NetAdmin.Domain.DbMaps.Sys;
|
||||||
using NetAdmin.Domain.Dto.Sys.Role;
|
using NetAdmin.Domain.Dto.Sys.Role;
|
||||||
|
using NetAdmin.Domain.Dto.Sys.UserRole;
|
||||||
|
|
||||||
namespace NetAdmin.SysComponent.Application.Services.Sys;
|
namespace NetAdmin.SysComponent.Application.Services.Sys;
|
||||||
|
|
||||||
/// <inheritdoc cref="IRoleService" />
|
/// <inheritdoc cref="IRoleService" />
|
||||||
public sealed class RoleService(BasicRepository<Sys_Role, long> rpo) //
|
public sealed class RoleService(BasicRepository<Sys_Role, long> rpo, IUserRoleService userRoleService) //
|
||||||
: RepositoryService<Sys_Role, long, IRoleService>(rpo), IRoleService
|
: RepositoryService<Sys_Role, long, IRoleService>(rpo), IRoleService
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -134,6 +135,13 @@ public sealed class RoleService(BasicRepository<Sys_Role, long> rpo) //
|
|||||||
return UpdateAsync(req, [nameof(req.IgnorePermissionControl)]);
|
return UpdateAsync(req, [nameof(req.IgnorePermissionControl)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task<IOrderedEnumerable<KeyValuePair<IImmutableDictionary<string, string>, int>>> UserCountByAsync(QueryReq<QueryUserRoleReq> req)
|
||||||
|
{
|
||||||
|
req.ThrowIfInvalid();
|
||||||
|
return userRoleService.CountByAsync(req);
|
||||||
|
}
|
||||||
|
|
||||||
private ISelect<Sys_Role> QueryInternal(QueryReq<QueryRoleReq> req)
|
private ISelect<Sys_Role> QueryInternal(QueryReq<QueryRoleReq> req)
|
||||||
{
|
{
|
||||||
#pragma warning disable RCS1196
|
#pragma warning disable RCS1196
|
||||||
|
@ -0,0 +1,119 @@
|
|||||||
|
using NetAdmin.Domain.DbMaps.Sys;
|
||||||
|
using NetAdmin.Domain.Dto.Sys.UserRole;
|
||||||
|
|
||||||
|
namespace NetAdmin.SysComponent.Application.Services.Sys;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IUserRoleService" />
|
||||||
|
public sealed class UserRoleService(BasicRepository<Sys_UserRole, long> rpo) //
|
||||||
|
: RepositoryService<Sys_UserRole, long, IUserRoleService>(rpo), IUserRoleService
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
|
||||||
|
{
|
||||||
|
req.ThrowIfInvalid();
|
||||||
|
var ret = 0;
|
||||||
|
|
||||||
|
// ReSharper disable once LoopCanBeConvertedToQuery
|
||||||
|
foreach (var item in req.Items) {
|
||||||
|
ret += await DeleteAsync(item).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task<long> CountAsync(QueryReq<QueryUserRoleReq> req)
|
||||||
|
{
|
||||||
|
req.ThrowIfInvalid();
|
||||||
|
return QueryInternal(req).WithNoLockNoWait().CountAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<IOrderedEnumerable<KeyValuePair<IImmutableDictionary<string, string>, int>>> CountByAsync(QueryReq<QueryUserRoleReq> req)
|
||||||
|
{
|
||||||
|
req.ThrowIfInvalid();
|
||||||
|
var ret = await QueryInternal(req with { Order = Orders.None })
|
||||||
|
.WithNoLockNoWait()
|
||||||
|
.GroupBy(req.GetToListExp<Sys_UserRole>())
|
||||||
|
.ToDictionaryAsync(a => a.Count())
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
return ret.Select(x => new KeyValuePair<IImmutableDictionary<string, string>, int>(
|
||||||
|
req.RequiredFields.ToImmutableDictionary(y => y, y => typeof(Sys_UserRole).GetProperty(y)!.GetValue(x.Key)!.ToString())
|
||||||
|
, x.Value))
|
||||||
|
.OrderByDescending(x => x.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<QueryUserRoleRsp> CreateAsync(CreateUserRoleReq req)
|
||||||
|
{
|
||||||
|
req.ThrowIfInvalid();
|
||||||
|
var ret = await Rpo.InsertAsync(req).ConfigureAwait(false);
|
||||||
|
return ret.Adapt<QueryUserRoleRsp>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task<int> DeleteAsync(DelReq req)
|
||||||
|
{
|
||||||
|
req.ThrowIfInvalid();
|
||||||
|
return Rpo.DeleteAsync(a => a.Id == req.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task<QueryUserRoleRsp> EditAsync(EditUserRoleReq req)
|
||||||
|
{
|
||||||
|
req.ThrowIfInvalid();
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task<IActionResult> ExportAsync(QueryReq<QueryUserRoleReq> req)
|
||||||
|
{
|
||||||
|
req.ThrowIfInvalid();
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<QueryUserRoleRsp> GetAsync(QueryUserRoleReq req)
|
||||||
|
{
|
||||||
|
req.ThrowIfInvalid();
|
||||||
|
var ret = await QueryInternal(new QueryReq<QueryUserRoleReq> { Filter = req, Order = Orders.None }).ToOneAsync().ConfigureAwait(false);
|
||||||
|
return ret.Adapt<QueryUserRoleRsp>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<PagedQueryRsp<QueryUserRoleRsp>> PagedQueryAsync(PagedQueryReq<QueryUserRoleReq> req)
|
||||||
|
{
|
||||||
|
req.ThrowIfInvalid();
|
||||||
|
var list = await QueryInternal(req).Page(req.Page, req.PageSize).WithNoLockNoWait().Count(out var total).ToListAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
return new PagedQueryRsp<QueryUserRoleRsp>(req.Page, req.PageSize, total, list.Adapt<IEnumerable<QueryUserRoleRsp>>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<IEnumerable<QueryUserRoleRsp>> QueryAsync(QueryReq<QueryUserRoleReq> req)
|
||||||
|
{
|
||||||
|
req.ThrowIfInvalid();
|
||||||
|
var ret = await QueryInternal(req).WithNoLockNoWait().Take(req.Count).ToListAsync().ConfigureAwait(false);
|
||||||
|
return ret.Adapt<IEnumerable<QueryUserRoleRsp>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ISelect<Sys_UserRole> QueryInternal(QueryReq<QueryUserRoleReq> req)
|
||||||
|
{
|
||||||
|
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
|
||||||
|
|
||||||
|
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
|
||||||
|
switch (req.Order) {
|
||||||
|
case Orders.None:
|
||||||
|
return ret;
|
||||||
|
case Orders.Random:
|
||||||
|
return ret.OrderByRandom();
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
|
||||||
|
if (!req.Prop?.Equals(nameof(req.Filter.Id), StringComparison.OrdinalIgnoreCase) ?? true) {
|
||||||
|
ret = ret.OrderByDescending(a => a.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using NetAdmin.Domain.Dto.Sys.Role;
|
using NetAdmin.Domain.Dto.Sys.Role;
|
||||||
|
using NetAdmin.Domain.Dto.Sys.UserRole;
|
||||||
|
|
||||||
namespace NetAdmin.SysComponent.Cache.Sys;
|
namespace NetAdmin.SysComponent.Cache.Sys;
|
||||||
|
|
||||||
@ -83,4 +84,10 @@ public sealed class RoleCache(IDistributedCache cache, IRoleService service) //
|
|||||||
{
|
{
|
||||||
return Service.SetIgnorePermissionControlAsync(req);
|
return Service.SetIgnorePermissionControlAsync(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task<IOrderedEnumerable<KeyValuePair<IImmutableDictionary<string, string>, int>>> UserCountByAsync(QueryReq<QueryUserRoleReq> req)
|
||||||
|
{
|
||||||
|
return Service.UserCountByAsync(req);
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using NetAdmin.Domain.Dto.Sys.Role;
|
using NetAdmin.Domain.Dto.Sys.Role;
|
||||||
|
using NetAdmin.Domain.Dto.Sys.UserRole;
|
||||||
|
|
||||||
namespace NetAdmin.SysComponent.Host.Controllers.Sys;
|
namespace NetAdmin.SysComponent.Host.Controllers.Sys;
|
||||||
|
|
||||||
@ -116,4 +117,12 @@ public sealed class RoleController(IRoleCache cache) : ControllerBase<IRoleCache
|
|||||||
{
|
{
|
||||||
return Cache.SetIgnorePermissionControlAsync(req);
|
return Cache.SetIgnorePermissionControlAsync(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色用户映射分组计数
|
||||||
|
/// </summary>
|
||||||
|
public Task<IOrderedEnumerable<KeyValuePair<IImmutableDictionary<string, string>, int>>> UserCountByAsync(QueryReq<QueryUserRoleReq> req)
|
||||||
|
{
|
||||||
|
return Cache.UserCountByAsync(req);
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using NetAdmin.Domain.Dto.Sys.Role;
|
using NetAdmin.Domain.Dto.Sys.Role;
|
||||||
|
using NetAdmin.Domain.Dto.Sys.UserRole;
|
||||||
|
|
||||||
namespace UnitTests.Sys;
|
namespace UnitTests.Sys;
|
||||||
|
|
||||||
@ -139,4 +140,14 @@ public class RoleTests(WebTestApplicationFactory<Startup> factory, ITestOutputHe
|
|||||||
Assert.True(rsp.IsSuccessStatusCode);
|
Assert.True(rsp.IsSuccessStatusCode);
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
[InlineData(default)]
|
||||||
|
[Theory]
|
||||||
|
public async Task<IOrderedEnumerable<KeyValuePair<IImmutableDictionary<string, string>, int>>> UserCountByAsync(QueryReq<QueryUserRoleReq> req)
|
||||||
|
{
|
||||||
|
var rsp = await PostJsonAsync(typeof(RoleController), req);
|
||||||
|
Assert.True(rsp.IsSuccessStatusCode);
|
||||||
|
return default;
|
||||||
|
}
|
||||||
}
|
}
|
@ -11,19 +11,19 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@element-plus/icons-vue": "2.3.1",
|
"@element-plus/icons-vue": "2.3.1",
|
||||||
"ace-builds": "1.36.5",
|
"ace-builds": "1.36.5",
|
||||||
"aieditor": "1.2.7",
|
"aieditor": "1.2.8",
|
||||||
"axios": "1.7.8",
|
"axios": "1.7.9",
|
||||||
"crypto-js": "4.2.0",
|
"crypto-js": "4.2.0",
|
||||||
"echarts": "5.5.1",
|
"echarts": "5.5.1",
|
||||||
"element-plus": "2.8.8",
|
"element-plus": "2.9.0",
|
||||||
"json-bigint": "1.0.0",
|
"json-bigint": "1.0.0",
|
||||||
"markdown-it": "14.1.0",
|
"markdown-it": "14.1.0",
|
||||||
"markdown-it-emoji": "3.0.0",
|
"markdown-it-emoji": "3.0.0",
|
||||||
"nprogress": "0.2.0",
|
"nprogress": "0.2.0",
|
||||||
"sortablejs": "1.15.4",
|
"sortablejs": "1.15.6",
|
||||||
"vkbeautify": "0.99.3",
|
"vkbeautify": "0.99.3",
|
||||||
"vue": "3.5.13",
|
"vue": "3.5.13",
|
||||||
"vue-i18n": "10.0.4",
|
"vue-i18n": "10.0.5",
|
||||||
"vue-router": "4.5.0",
|
"vue-router": "4.5.0",
|
||||||
"vue3-ace-editor": "2.2.4",
|
"vue3-ace-editor": "2.2.4",
|
||||||
"vue3-json-viewer": "2.2.2",
|
"vue3-json-viewer": "2.2.2",
|
||||||
@ -32,11 +32,11 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "5.2.1",
|
"@vitejs/plugin-vue": "5.2.1",
|
||||||
"prettier": "3.4.1",
|
"prettier": "3.4.2",
|
||||||
"prettier-plugin-organize-attributes": "1.0.0",
|
"prettier-plugin-organize-attributes": "1.0.0",
|
||||||
"sass": "1.81.0",
|
"sass": "1.82.0",
|
||||||
"terser": "5.36.0",
|
"terser": "5.37.0",
|
||||||
"vite": "6.0.1"
|
"vite": "6.0.3"
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
"> 1%",
|
"> 1%",
|
||||||
|
@ -147,4 +147,15 @@ export default {
|
|||||||
return await http.post(this.url, data, config)
|
return await http.post(this.url, data, config)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色用户映射分组计数
|
||||||
|
*/
|
||||||
|
userCountBy: {
|
||||||
|
url: `${config.API_URL}/api/sys/role/user.count.by`,
|
||||||
|
name: `角色用户映射分组计数`,
|
||||||
|
post: async function (data = {}, config = {}) {
|
||||||
|
return await http.post(this.url, data, config)
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
@ -1,13 +1,3 @@
|
|||||||
<!--
|
|
||||||
* @Descripttion: scContextmenu组件
|
|
||||||
* @version: 1.1
|
|
||||||
* @Author: sakuya
|
|
||||||
* @Date: 2021年7月23日09:25:57
|
|
||||||
* @LastEditors: sakuya
|
|
||||||
* @LastEditTime: 2022年5月30日20:17:42
|
|
||||||
* @other: 代码完全开源,欢迎参考,也欢迎PR
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<transition name="el-zoom-in-top">
|
<transition name="el-zoom-in-top">
|
||||||
<div v-if="visible" :style="{ left: left + 'px', top: top + 'px' }" @contextmenu.prevent="fun" class="sc-contextmenu" ref="contextmenu">
|
<div v-if="visible" :style="{ left: left + 'px', top: top + 'px' }" @contextmenu.prevent="fun" class="sc-contextmenu" ref="contextmenu">
|
||||||
|
@ -1,12 +1,3 @@
|
|||||||
<!--
|
|
||||||
* @Descripttion: scContextmenuItem组件
|
|
||||||
* @version: 1.3
|
|
||||||
* @Author: sakuya
|
|
||||||
* @Date: 2021年7月23日16:29:36
|
|
||||||
* @LastEditors: Xujianchen
|
|
||||||
* @LastEditTime: 2023-03-18 12:59:06
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<hr v-if="divided" />
|
<hr v-if="divided" />
|
||||||
<li :class="disabled ? 'disabled' : ''" @click.stop="liClick" @mouseenter="openSubmenu($event)" @mouseleave="closeSubmenu($event)">
|
<li :class="disabled ? 'disabled' : ''" @click.stop="liClick" @mouseenter="openSubmenu($event)" @mouseleave="closeSubmenu($event)">
|
||||||
|
@ -1,12 +1,3 @@
|
|||||||
<!--
|
|
||||||
* @Descripttion: cron规则生成器
|
|
||||||
* @version: 1.0
|
|
||||||
* @Author: sakuya
|
|
||||||
* @Date: 2021年12月29日15:23:54
|
|
||||||
* @LastEditors: Xujianchen
|
|
||||||
* @LastEditTime: 2023-03-18 13:04:08
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-input v-bind="$attrs" v-model="defaultValue">
|
<el-input v-bind="$attrs" v-model="defaultValue">
|
||||||
<template #append>
|
<template #append>
|
||||||
|
@ -1,12 +1,3 @@
|
|||||||
<!--
|
|
||||||
* @Descripttion: 弹窗扩展组件
|
|
||||||
* @version: 2.0
|
|
||||||
* @Author: sakuya
|
|
||||||
* @Date: 2021年8月27日08:51:52
|
|
||||||
* @LastEditors: Xujianchen
|
|
||||||
* @LastEditTime: 2023-03-18 13:04:33
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="sc-dialog" ref="scDialog">
|
<div class="sc-dialog" ref="scDialog">
|
||||||
<el-dialog v-bind="$attrs" v-model="dialogVisible" :fullscreen="isFullscreen" :show-close="false" draggable ref="dialog">
|
<el-dialog v-bind="$attrs" v-model="dialogVisible" :fullscreen="isFullscreen" :show-close="false" draggable ref="dialog">
|
||||||
|
@ -1,12 +1,3 @@
|
|||||||
<!--
|
|
||||||
* @Descripttion: 文件导出
|
|
||||||
* @version: 1.1
|
|
||||||
* @Author: sakuya
|
|
||||||
* @Date: 2022年5月24日16:20:12
|
|
||||||
* @LastEditors: Xujianchen
|
|
||||||
* @LastEditTime: 2023-03-19 11:59:39
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<slot :open="open">
|
<slot :open="open">
|
||||||
<el-button @click="open" plain type="primary">{{ $t('导出') }}</el-button>
|
<el-button @click="open" plain type="primary">{{ $t('导出') }}</el-button>
|
||||||
|
@ -1,12 +1,3 @@
|
|||||||
<!--
|
|
||||||
* @Descripttion: 文件导入
|
|
||||||
* @version: 1.0
|
|
||||||
* @Author: sakuya
|
|
||||||
* @Date: 2022年5月24日11:30:03
|
|
||||||
* @LastEditors:
|
|
||||||
* @LastEditTime:
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<slot :open="open">
|
<slot :open="open">
|
||||||
<el-button @click="open" plain type="primary">{{ $t('导入') }}</el-button>
|
<el-button @click="open" plain type="primary">{{ $t('导入') }}</el-button>
|
||||||
|
@ -1,12 +1,3 @@
|
|||||||
<!--
|
|
||||||
* @Descripttion: 资源文件选择器
|
|
||||||
* @version: 1.0
|
|
||||||
* @Author: sakuya
|
|
||||||
* @Date: 2021年10月11日16:01:40
|
|
||||||
* @LastEditors: Xujianchen
|
|
||||||
* @LastEditTime: 2023-03-19 11:44:42
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="sc-file-select">
|
<div class="sc-file-select">
|
||||||
<div v-loading="menuLoading" class="sc-file-select__side">
|
<div v-loading="menuLoading" class="sc-file-select__side">
|
||||||
|
@ -1,12 +1,3 @@
|
|||||||
<!--
|
|
||||||
* @Descripttion: 表单表格
|
|
||||||
* @version: 1.3
|
|
||||||
* @Author: sakuya
|
|
||||||
* @Date: 2023年2月9日12:32:26
|
|
||||||
* @LastEditors: Xujianchen
|
|
||||||
* @LastEditTime: 2023-03-18 13:08:27
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="sc-form-table" ref="scFormTable">
|
<div class="sc-form-table" ref="scFormTable">
|
||||||
<el-table :data="data" border ref="table" stripe>
|
<el-table :data="data" border ref="table" stripe>
|
||||||
|
@ -1,12 +1,3 @@
|
|||||||
<!--
|
|
||||||
* @Descripttion: 图标选择器组件
|
|
||||||
* @version: 2.0
|
|
||||||
* @Author: sakuya
|
|
||||||
* @Date: 2021年7月27日10:02:46
|
|
||||||
* @LastEditors: Xujianchen
|
|
||||||
* @LastEditTime: 2023-03-18 13:08:59
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="sc-icon-select">
|
<div class="sc-icon-select">
|
||||||
<div :class="{ hasValue: value }" @click="open" class="sc-icon-select__wrapper">
|
<div :class="{ hasValue: value }" @click="open" class="sc-icon-select__wrapper">
|
||||||
|
@ -1,12 +1,3 @@
|
|||||||
<!--
|
|
||||||
* @Descripttion: 状态指示器
|
|
||||||
* @version: 1.0
|
|
||||||
* @Author: sakuya
|
|
||||||
* @Date: 2021年11月11日09:30:12
|
|
||||||
* @LastEditors:
|
|
||||||
* @LastEditTime:
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<span :class="[{ 'sc-status-processing': pulse }, 'sc-state-bg--' + type]" class="sc-state"></span>
|
<span :class="[{ 'sc-status-processing': pulse }, 'sc-state-bg--' + type]" class="sc-state"></span>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,12 +1,3 @@
|
|||||||
<!--
|
|
||||||
* @Descripttion: 趋势标记
|
|
||||||
* @version: 1.0
|
|
||||||
* @Author: sakuya
|
|
||||||
* @Date: 2021年11月11日11:07:10
|
|
||||||
* @LastEditors: Xujianchen
|
|
||||||
* @LastEditTime: 2023-03-19 11:46:37
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<span :class="'sc-trend--' + type" class="sc-trend">
|
<span :class="'sc-trend--' + type" class="sc-trend">
|
||||||
<el-icon v-if="iconType === 'P'" class="sc-trend-icon"><el-icon-top /></el-icon>
|
<el-icon v-if="iconType === 'P'" class="sc-trend-icon"><el-icon-top /></el-icon>
|
||||||
|
@ -1,12 +1,3 @@
|
|||||||
<!--
|
|
||||||
* @Descripttion: 页面头部样式组件
|
|
||||||
* @version: 1.0
|
|
||||||
* @Author: sakuya
|
|
||||||
* @Date: 2021年7月20日08:49:07
|
|
||||||
* @LastEditors:
|
|
||||||
* @LastEditTime:
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="sc-page-header">
|
<div class="sc-page-header">
|
||||||
<div v-if="icon" class="sc-page-header__icon">
|
<div v-if="icon" class="sc-page-header__icon">
|
||||||
|
@ -1,12 +1,3 @@
|
|||||||
<!--
|
|
||||||
* @Descripttion: 密码强度检测
|
|
||||||
* @version: 1.0
|
|
||||||
* @Author: sakuya
|
|
||||||
* @Date: 2022年6月2日15:36:01
|
|
||||||
* @LastEditors:
|
|
||||||
* @LastEditTime:
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="sc-password-strength">
|
<div class="sc-password-strength">
|
||||||
<div :class="`sc-password-strength-level-${level}`" class="sc-password-strength-bar"></div>
|
<div :class="`sc-password-strength-level-${level}`" class="sc-password-strength-bar"></div>
|
||||||
|
@ -1,12 +1,3 @@
|
|||||||
<!--
|
|
||||||
* @Descripttion: 异步选择器
|
|
||||||
* @version: 1.1
|
|
||||||
* @Author: sakuya
|
|
||||||
* @Date: 2021年8月3日15:53:37
|
|
||||||
* @LastEditors: Xujianchen
|
|
||||||
* @LastEditTime: 2023-03-18 13:09:37
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="sc-select">
|
<div class="sc-select">
|
||||||
<div v-if="initLoading" class="sc-select-loading">
|
<div v-if="initLoading" class="sc-select-loading">
|
||||||
|
@ -1,18 +1,14 @@
|
|||||||
<!--
|
|
||||||
* @Descripttion: 分类筛选器
|
|
||||||
* @version: 1.0
|
|
||||||
* @Author: sakuya
|
|
||||||
* @Date: 2022年5月26日15:59:52
|
|
||||||
* @LastEditors: Xujianchen
|
|
||||||
* @LastEditTime: 2023-03-18 13:09:49
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="sc-select-filter">
|
<div class="sc-select-filter">
|
||||||
<div v-if="data.length <= 0" class="sc-select-filter__no-data">{{ $t('暂无数据') }}</div>
|
<div v-if="data.length <= 0" class="sc-select-filter__no-data">{{ $t('暂无数据') }}</div>
|
||||||
<div v-for="item in data" :class="`sc-select-filter__item${item.w100p ? ' sc-select-filter__item-w100p' : ''}`" :key="item.key">
|
<div v-for="item in data" :class="`sc-select-filter__item${item.w100p ? ' sc-select-filter__item-w100p' : ''}`" :key="item.key">
|
||||||
<div :style="{ width: labelWidth + 'rem' }" class="sc-select-filter__item-title">
|
<div :style="{ width: labelWidth + 'rem' }" @click="autoHeight" class="sc-select-filter__item-title">
|
||||||
<label>{{ item.title }}</label>
|
<label>
|
||||||
|
<span>{{ item.title }}</span>
|
||||||
|
<el-icon style="display: none">
|
||||||
|
<el-icon-arrow-up></el-icon-arrow-up>
|
||||||
|
</el-icon>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="sc-select-filter__item-options">
|
<div class="sc-select-filter__item-options">
|
||||||
<ul>
|
<ul>
|
||||||
@ -20,15 +16,12 @@
|
|||||||
v-for="option in item.options"
|
v-for="option in item.options"
|
||||||
:class="{ active: selected[item.key] && selected[item.key].includes(option.value) }"
|
:class="{ active: selected[item.key] && selected[item.key].includes(option.value) }"
|
||||||
:key="option.value"
|
:key="option.value"
|
||||||
|
:title="option.title"
|
||||||
@click="select(option, item)">
|
@click="select(option, item)">
|
||||||
<el-icon v-if="option.icon">
|
<el-icon v-if="option.icon">
|
||||||
<component :is="option.icon" />
|
<component :is="option.icon" />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
<el-badge
|
<el-badge :max="item.badgeMax ?? 999999999" :show-zero="false" :value="option.badge" badge-class="badge-small">
|
||||||
:max="item.badgeMax ?? 999999999"
|
|
||||||
:show-zero="false"
|
|
||||||
:type="option.badgeType ? option.badgeType : option.badge > 1 ? 'danger' : 'info'"
|
|
||||||
:value="option.badge">
|
|
||||||
<span>{{ option.label }}</span>
|
<span>{{ option.label }}</span>
|
||||||
</el-badge>
|
</el-badge>
|
||||||
</li>
|
</li>
|
||||||
@ -54,6 +47,10 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
selected: {},
|
selected: {},
|
||||||
|
svgIconUp:
|
||||||
|
'<svg data-v-a30f2a47="" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path fill="currentColor" d="m488.832 344.32-339.84 356.672a32 32 0 0 0 0 44.16l.384.384a29.44 29.44 0 0 0 42.688 0l320-335.872 319.872 335.872a29.44 29.44 0 0 0 42.688 0l.384-.384a32 32 0 0 0 0-44.16L535.168 344.32a32 32 0 0 0-46.336 0"></path></svg>',
|
||||||
|
svgIconDown:
|
||||||
|
'<svg data-v-a30f2a47="" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path fill="currentColor" d="M831.872 340.864 512 652.672 192.128 340.864a30.592 30.592 0 0 0-42.752 0 29.12 29.12 0 0 0 0 41.6L489.664 714.24a32 32 0 0 0 44.672 0l340.288-331.712a29.12 29.12 0 0 0 0-41.728 30.592 30.592 0 0 0-42.752 0z"></path></svg>',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@ -63,6 +60,25 @@ export default {
|
|||||||
// this.selectedValues[item.key] || (Array.isArray(item.options) && item.options.length) ? [item.options[0].value] : []
|
// this.selectedValues[item.key] || (Array.isArray(item.options) && item.options.length) ? [item.options[0].value] : []
|
||||||
// })
|
// })
|
||||||
// },
|
// },
|
||||||
|
|
||||||
|
data: {
|
||||||
|
immediate: true,
|
||||||
|
handler() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
for (const el of document.getElementsByClassName('sc-select-filter__item-options')) {
|
||||||
|
if (el.children[0].clientHeight / el.clientHeight < 1.5) {
|
||||||
|
el.previousSibling.children[0].children[1].style.display = 'none'
|
||||||
|
el.previousSibling.children[0].style.cursor = 'unset'
|
||||||
|
el.style.height = '3.5rem'
|
||||||
|
} else {
|
||||||
|
el.previousSibling.children[0].style.cursor = 'pointer'
|
||||||
|
el.previousSibling.children[0].children[1].style.display = 'unset'
|
||||||
|
el.previousSibling.children[0].children[1].innerHTML = this.svgIconUp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
selectedString() {
|
selectedString() {
|
||||||
@ -81,6 +97,16 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
autoHeight(e) {
|
||||||
|
if (e.currentTarget.nextSibling.style.height === 'auto') {
|
||||||
|
e.currentTarget.nextSibling.style.height = '3.5rem'
|
||||||
|
e.currentTarget.children[0].children[1].innerHTML = this.svgIconUp
|
||||||
|
} else {
|
||||||
|
if (e.currentTarget.nextSibling.children[0].clientHeight / e.currentTarget.nextSibling.clientHeight < 1.5) return
|
||||||
|
e.currentTarget.nextSibling.style.height = 'auto'
|
||||||
|
e.currentTarget.children[0].children[1].innerHTML = this.svgIconDown
|
||||||
|
}
|
||||||
|
},
|
||||||
select(option, item) {
|
select(option, item) {
|
||||||
//判断单选多选
|
//判断单选多选
|
||||||
if (item.multiple) {
|
if (item.multiple) {
|
||||||
@ -153,13 +179,18 @@ export default {
|
|||||||
.sc-select-filter__item-title label {
|
.sc-select-filter__item-title label {
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
padding-top: 1rem;
|
padding-top: 1rem;
|
||||||
display: inline-block;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding-right: 1rem;
|
||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sc-select-filter__item-options {
|
.sc-select-filter__item-options {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
border-bottom: 1px dashed var(--el-border-color-light);
|
border-bottom: 1px dashed var(--el-border-color-light);
|
||||||
|
height: 3.5rem;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sc-select-filter__item-options ul {
|
.sc-select-filter__item-options ul {
|
||||||
@ -202,7 +233,14 @@ export default {
|
|||||||
.sc-select-filter__no-data {
|
.sc-select-filter__no-data {
|
||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sc-select-filter__item-w100p {
|
.sc-select-filter__item-w100p {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
</style>
|
||||||
|
<style>
|
||||||
|
.badge-small {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
height: 1.3rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
@ -1,12 +1,3 @@
|
|||||||
<!--
|
|
||||||
* @Descripttion: 统计数值组件
|
|
||||||
* @version: 1.1
|
|
||||||
* @Author: sakuya
|
|
||||||
* @Date: 2021年6月23日13:11:32
|
|
||||||
* @LastEditors: sakuya
|
|
||||||
* @LastEditTime: 2022年5月14日19:55:09
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="sc-statistic">
|
<div class="sc-statistic">
|
||||||
<div class="sc-statistic-title">
|
<div class="sc-statistic-title">
|
||||||
|
@ -1,11 +1,3 @@
|
|||||||
<!--
|
|
||||||
* @Description: 数据表格组件
|
|
||||||
* @version: 1.11
|
|
||||||
* @Author: sakuya
|
|
||||||
* @Date: 2021年11月29日21:51:15
|
|
||||||
* @LastEditors: sakuya
|
|
||||||
* @LastEditTime: 2023年3月2日10:43:35
|
|
||||||
-->
|
|
||||||
<template>
|
<template>
|
||||||
<div v-loading="loading" :style="{ height: _height }" class="scTable" ref="scTableMain">
|
<div v-loading="loading" :style="{ height: _height }" class="scTable" ref="scTableMain">
|
||||||
<div :style="{ height: _table_height }" class="scTable-table">
|
<div :style="{ height: _table_height }" class="scTable-table">
|
||||||
|
@ -1,12 +1,3 @@
|
|||||||
<!--
|
|
||||||
* @Descripttion: 表格选择器组件
|
|
||||||
* @version: 1.3
|
|
||||||
* @Author: sakuya
|
|
||||||
* @Date: 2021年6月10日10:04:07
|
|
||||||
* @LastEditors: Xujianchen
|
|
||||||
* @LastEditTime: 2023-03-18 13:12:15
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-select
|
<el-select
|
||||||
v-model="defaultValue"
|
v-model="defaultValue"
|
||||||
|
@ -1,12 +1,3 @@
|
|||||||
<!--
|
|
||||||
* @Descripttion: 局部水印组件
|
|
||||||
* @version: 1.1
|
|
||||||
* @Author: sakuya
|
|
||||||
* @Date: 2021年12月18日12:16:16
|
|
||||||
* @LastEditors: Xujianchen
|
|
||||||
* @LastEditTime: 2023-03-18 13:14:19
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="sc-water-mark" ref="scWaterMark">
|
<div class="sc-water-mark" ref="scWaterMark">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
|
@ -1,12 +1,3 @@
|
|||||||
<!--
|
|
||||||
* @Descripttion: 处理iframe持久化,涉及store(VUEX)
|
|
||||||
* @version: 1.0
|
|
||||||
* @Author: sakuya
|
|
||||||
* @Date: 2021年6月30日13:20:41
|
|
||||||
* @LastEditors: Xujianchen
|
|
||||||
* @LastEditTime: 2023-03-19 11:52:34
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-show="$route.meta.type === 'iframe'" class="iframe-pages">
|
<div v-show="$route.meta.type === 'iframe'" class="iframe-pages">
|
||||||
<iframe
|
<iframe
|
||||||
|
@ -1,154 +0,0 @@
|
|||||||
<template>
|
|
||||||
<el-container v-loading="loading">
|
|
||||||
<el-main>
|
|
||||||
<el-empty v-if="jobs.length === 0" :image-size="120">
|
|
||||||
<template #description>
|
|
||||||
<h2>{{ $t('没有正在执行的作业') }}</h2>
|
|
||||||
</template>
|
|
||||||
<p style="color: #999; line-height: 1.5; margin: 0 3rem">
|
|
||||||
在处理耗时过久的作业时为了不阻碍正在处理的工作,可在作业中心进行异步执行。
|
|
||||||
</p>
|
|
||||||
</el-empty>
|
|
||||||
<el-card v-for="job in jobs" :class="`user-bar-jobs-item ${job.lastStatusCode === 'oK' ? '' : 'alert'}`" :key="job.id" shadow="hover">
|
|
||||||
<div class="user-bar-jobs-item-body">
|
|
||||||
<div class="jobIcon">
|
|
||||||
{{ job.lastStatusCode?.toUpperCase() }}
|
|
||||||
</div>
|
|
||||||
<div class="jobMain">
|
|
||||||
<div class="title">
|
|
||||||
<h2>{{ job.jobName }}</h2>
|
|
||||||
<p>{{ $t('上次执行:') }}<span v-time.tip="job.lastExecTime" :title="job.lastExecTime"></span></p>
|
|
||||||
<p>
|
|
||||||
下次执行:<span>{{ job.nextExecTime }}</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="bottom">
|
|
||||||
<div class="status">
|
|
||||||
<el-tag v-if="job.status === 'running'" type="warning">{{ $t('执行中') }}</el-tag>
|
|
||||||
<el-tag v-if="job.status === 'idle'" :type="job.lastStatusCode === 'oK' ? 'primary' : 'danger'">{{
|
|
||||||
$t('空闲')
|
|
||||||
}}</el-tag>
|
|
||||||
</div>
|
|
||||||
<div class="handler">
|
|
||||||
<el-button
|
|
||||||
:type="job.lastStatusCode === 'oK' ? 'primary' : 'danger'"
|
|
||||||
@click="view(job)"
|
|
||||||
circle
|
|
||||||
icon="el-icon-view"></el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</el-main>
|
|
||||||
<el-footer class="flex" style="justify-content: space-evenly; height: unset">
|
|
||||||
<div>
|
|
||||||
<el-badge :hidden="fail === 0" :value="fail">
|
|
||||||
<el-button @click="gotoJob">{{ $t('异常作业') }}</el-button>
|
|
||||||
</el-badge>
|
|
||||||
</div>
|
|
||||||
<div style="text-align: right; flex-grow: 1">
|
|
||||||
<el-button @click="refresh" circle icon="el-icon-refresh"></el-button>
|
|
||||||
</div>
|
|
||||||
</el-footer>
|
|
||||||
</el-container>
|
|
||||||
|
|
||||||
<save-dialog v-if="dialog.save" @closed="dialog.save = null" @mounted="$refs.saveDialog.open(dialog.save)" ref="saveDialog"></save-dialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { defineAsyncComponent } from 'vue'
|
|
||||||
const saveDialog = defineAsyncComponent(() => import('@/views/sys/job/all/save.vue'))
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
saveDialog,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
dialog: {},
|
|
||||||
loading: false,
|
|
||||||
jobs: [],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
emits: ['closed'],
|
|
||||||
inject: ['reload'],
|
|
||||||
methods: {
|
|
||||||
gotoJob() {
|
|
||||||
this.$router.push({ path: '/sys/job', query: { view: 'fail' } })
|
|
||||||
this.$emit('closed')
|
|
||||||
},
|
|
||||||
async getData() {
|
|
||||||
this.loading = true
|
|
||||||
const res = await this.$API.sys_job.query.post({ prop: 'lastStatusCode', order: 'descending' })
|
|
||||||
this.jobs = res.data
|
|
||||||
this.loading = false
|
|
||||||
},
|
|
||||||
refresh() {
|
|
||||||
this.getData()
|
|
||||||
},
|
|
||||||
async view(job) {
|
|
||||||
this.dialog.save = { mode: 'view', row: { id: job.id }, tabId: 'record' }
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.getData()
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
fail: { type: Number, default: 0 },
|
|
||||||
},
|
|
||||||
watch: {},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.user-bar-jobs-item {
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
.user-bar-jobs-item:hover {
|
|
||||||
border-color: var(--na-color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-bar-jobs-item.alert:hover {
|
|
||||||
border-color: var(--el-color-danger);
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-bar-jobs-item-body {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-bar-jobs-item-body .jobIcon {
|
|
||||||
width: 3rem;
|
|
||||||
height: 3rem;
|
|
||||||
background: var(--el-color-primary-light-9);
|
|
||||||
margin-right: 2rem;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
color: var(--na-color-primary);
|
|
||||||
border-radius: 1.5rem;
|
|
||||||
}
|
|
||||||
.user-bar-jobs-item-body .jobMain {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-bar-jobs-item-body .title h2 {
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-bar-jobs-item-body .title p {
|
|
||||||
font-size: 1rem;
|
|
||||||
color: #999;
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-bar-jobs-item-body .bottom {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-bar-jobs-item.alert .jobIcon {
|
|
||||||
background: var(--el-color-danger-light-9);
|
|
||||||
color: var(--el-color-danger);
|
|
||||||
}
|
|
||||||
</style>
|
|
261
src/frontend/admin/src/layout/components/tasks/index.vue
Normal file
261
src/frontend/admin/src/layout/components/tasks/index.vue
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
<template>
|
||||||
|
<el-container v-loading="loading">
|
||||||
|
<el-main>
|
||||||
|
<el-empty v-if="jobs.length === 0" :image-size="120">
|
||||||
|
<template #description>
|
||||||
|
<h2>{{ $t('没有正在执行的作业') }}</h2>
|
||||||
|
</template>
|
||||||
|
<p style="color: #999; line-height: 1.5; margin: 0 3rem">
|
||||||
|
在处理耗时过久的作业时为了不阻碍正在处理的工作,可在作业中心进行异步执行。
|
||||||
|
</p>
|
||||||
|
</el-empty>
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :lg="12">
|
||||||
|
<el-card
|
||||||
|
v-for="job in jobs"
|
||||||
|
:class="`user-bar-jobs-item ${job.lastStatusCode === 'oK' ? '' : 'alert'}`"
|
||||||
|
:key="job.id"
|
||||||
|
shadow="hover">
|
||||||
|
<div class="user-bar-jobs-item-body">
|
||||||
|
<div class="jobIcon">
|
||||||
|
{{ job.lastStatusCode?.toUpperCase().slice(0, 2) }}
|
||||||
|
</div>
|
||||||
|
<div class="jobMain">
|
||||||
|
<div class="title">
|
||||||
|
<h2>{{ job.jobName }}</h2>
|
||||||
|
<p>{{ $t('上次执行:') }}<span v-time.tip="job.lastExecTime" :title="job.lastExecTime"></span></p>
|
||||||
|
<p>
|
||||||
|
下次执行:<span>{{ job.nextExecTime }}</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="bottom">
|
||||||
|
<div class="status">
|
||||||
|
<el-tag v-if="job.status === 'running'" type="warning">{{ $t('执行中') }}</el-tag>
|
||||||
|
<el-tag v-if="job.status === 'idle'" :type="job.lastStatusCode === 'oK' ? 'primary' : 'danger'"
|
||||||
|
>{{ $t('空闲') }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
<div class="handler">
|
||||||
|
<el-button
|
||||||
|
:type="job.lastStatusCode === 'oK' ? 'primary' : 'danger'"
|
||||||
|
@click="dialog.jobSave = { mode: 'view', row: { id: job.id }, tabId: 'record' }"
|
||||||
|
circle
|
||||||
|
icon="el-icon-view"></el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :lg="12">
|
||||||
|
<el-empty v-if="!failJobs" description="未发现新的异常作业"></el-empty>
|
||||||
|
<el-card v-else v-for="job in failJobs" :class="`user-bar-jobs-item alert`" :key="job.job.id" shadow="hover">
|
||||||
|
<div class="user-bar-jobs-item-body">
|
||||||
|
<div class="jobIcon">
|
||||||
|
{{ job.httpStatusCode.toUpperCase().slice(0, 2) }}
|
||||||
|
</div>
|
||||||
|
<div class="jobMain">
|
||||||
|
<div class="title">
|
||||||
|
<h2>{{ job.job.jobName }}</h2>
|
||||||
|
<p>{{ $t('出错时间:') }}<span v-time.tip="job.createdTime" :title="job.createdTime"></span></p>
|
||||||
|
<p>
|
||||||
|
执行耗时:<span>{{ $TOOL.groupSeparator(job.duration) }} ms</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="bottom">
|
||||||
|
<div class="status failJobs">
|
||||||
|
{{ job.responseBody }}
|
||||||
|
</div>
|
||||||
|
<div class="handler">
|
||||||
|
<el-button
|
||||||
|
@click="dialog.jobRecordSave = { mode: 'view', row: { id: job.id } }"
|
||||||
|
circle
|
||||||
|
icon="el-icon-view"
|
||||||
|
type="danger"></el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-main>
|
||||||
|
<el-footer class="flex" style="justify-content: space-evenly; height: unset">
|
||||||
|
<div>
|
||||||
|
<el-badge :hidden="jobsCnt === 0" :value="jobsCnt">
|
||||||
|
<el-button @click="dialog.save = { tabId: 'all' }">{{ $t('作业管理') }}</el-button>
|
||||||
|
</el-badge>
|
||||||
|
</div>
|
||||||
|
<el-button @click="refresh" circle icon="el-icon-refresh"></el-button>
|
||||||
|
<div v-if="failJobs">
|
||||||
|
<el-badge :hidden="fail === 0" :value="fail">
|
||||||
|
<el-button @click="dialog.save = { tabId: 'fail' }" plain type="danger">{{ $t('异常日志') }}</el-button>
|
||||||
|
</el-badge>
|
||||||
|
</div>
|
||||||
|
</el-footer>
|
||||||
|
</el-container>
|
||||||
|
|
||||||
|
<jobSaveDialog
|
||||||
|
v-if="dialog.jobSave"
|
||||||
|
@closed="dialog.jobSave = null"
|
||||||
|
@mounted="$refs.jobSaveDialog.open(dialog.jobSave)"
|
||||||
|
ref="jobSaveDialog"></jobSaveDialog>
|
||||||
|
<jobRecordSaveDialog
|
||||||
|
v-if="dialog.jobRecordSave"
|
||||||
|
@closed="dialog.jobRecordSave = null"
|
||||||
|
@mounted="$refs.jobRecordSaveDialog.open(dialog.jobRecordSave)"
|
||||||
|
ref="jobRecordSaveDialog"></jobRecordSaveDialog>
|
||||||
|
<saveDialog v-if="dialog.save" @closed="dialog.save = null" @mounted="$refs.saveDialog.open(dialog.save)" ref="saveDialog"></saveDialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
const jobSaveDialog = defineAsyncComponent(() => import('@/views/sys/job/all/save.vue'))
|
||||||
|
const jobRecordSaveDialog = defineAsyncComponent(() => import('@/views/sys/job/record/save.vue'))
|
||||||
|
const saveDialog = defineAsyncComponent(() => import('./save.vue'))
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
jobSaveDialog,
|
||||||
|
jobRecordSaveDialog,
|
||||||
|
saveDialog,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dialog: {},
|
||||||
|
loading: false,
|
||||||
|
jobs: [],
|
||||||
|
jobsCnt: 0,
|
||||||
|
failJobs: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['closed'],
|
||||||
|
inject: ['reload'],
|
||||||
|
methods: {
|
||||||
|
async getData() {
|
||||||
|
this.loading = true
|
||||||
|
const res = await Promise.all([
|
||||||
|
this.$API.sys_job.pagedQuery.post({
|
||||||
|
prop: 'nextExecTime',
|
||||||
|
order: 'ascending',
|
||||||
|
dynamicFilter: {
|
||||||
|
field: 'enabled',
|
||||||
|
value: true,
|
||||||
|
operator: 'eq',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
this.$API.sys_job.pagedQueryRecord.post({
|
||||||
|
dynamicFilter: {
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
logic: 'or',
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
field: 'httpStatusCode',
|
||||||
|
operator: 'range',
|
||||||
|
value: '300,399',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'httpStatusCode',
|
||||||
|
operator: 'range',
|
||||||
|
value: '400,499',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'httpStatusCode',
|
||||||
|
operator: 'range',
|
||||||
|
value: '500,599',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'httpStatusCode',
|
||||||
|
operator: 'range',
|
||||||
|
value: '900,999',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
field: 'createdTime',
|
||||||
|
operator: 'greaterThan',
|
||||||
|
value: this.$TOOL.data.get('APP_SET_FAIL_JOB_VIEW_TIME') ?? this.$TOOL.dateFormat(new Date(), 'yyyy-MM-dd'),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
|
this.jobs = res[0].data.rows
|
||||||
|
this.jobsCnt = res[0].data.total
|
||||||
|
this.failJobs = res[1].data.rows
|
||||||
|
this.loading = false
|
||||||
|
},
|
||||||
|
refresh() {
|
||||||
|
this.getData()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getData()
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
fail: { type: Number, default: 0 },
|
||||||
|
},
|
||||||
|
watch: {},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.user-bar-jobs-item {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-bar-jobs-item:hover {
|
||||||
|
border-color: var(--na-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-bar-jobs-item.alert:hover {
|
||||||
|
border-color: var(--el-color-danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-bar-jobs-item-body {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-bar-jobs-item-body .jobIcon {
|
||||||
|
width: 3rem;
|
||||||
|
height: 3rem;
|
||||||
|
background: var(--el-color-primary-light-9);
|
||||||
|
margin-right: 2rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: var(--na-color-primary);
|
||||||
|
border-radius: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-bar-jobs-item-body .jobMain {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-bar-jobs-item-body .title h2 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-bar-jobs-item-body .title p {
|
||||||
|
font-size: 1rem;
|
||||||
|
color: #999;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-bar-jobs-item-body .bottom {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-bar-jobs-item.alert .jobIcon {
|
||||||
|
background: var(--el-color-danger-light-9);
|
||||||
|
color: var(--el-color-danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status.failJobs {
|
||||||
|
color: var(--el-color-danger);
|
||||||
|
width: 18rem;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
44
src/frontend/admin/src/layout/components/tasks/save.vue
Normal file
44
src/frontend/admin/src/layout/components/tasks/save.vue
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<template>
|
||||||
|
<sc-dialog v-model="visible" :title="$t('作业中心')" @closed="$emit('closed')" append-to-body destroy-on-close full-screen>
|
||||||
|
<job v-if="tabId" :statusCodes="statusCodes" :tab="tabId" />
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="visible = false">{{ $t('取消') }}</el-button>
|
||||||
|
</template>
|
||||||
|
</sc-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
const job = defineAsyncComponent(() => import('@/views/sys/job/index.vue'))
|
||||||
|
export default {
|
||||||
|
components: { job },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
statusCodes: null,
|
||||||
|
tabId: null,
|
||||||
|
loading: true,
|
||||||
|
visible: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['success', 'closed', 'mounted'],
|
||||||
|
methods: {
|
||||||
|
//显示
|
||||||
|
async open(data) {
|
||||||
|
this.visible = true
|
||||||
|
if (data.tabId === 'fail') {
|
||||||
|
data.tabId = 'log'
|
||||||
|
this.statusCodes = ['300,399', '400,499', '500,599', '900,999']
|
||||||
|
await this.$TOOL.data.set('APP_SET_FAIL_JOB_VIEW_TIME', this.$TOOL.dateFormat(new Date(), 'yyyy-MM-dd hh:mm:ss'))
|
||||||
|
}
|
||||||
|
this.tabId = data.tabId
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
//表单提交方法
|
||||||
|
async submit() {},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$emit('mounted')
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped></style>
|
@ -74,7 +74,7 @@
|
|||||||
<search @success="searchVisible = false"></search>
|
<search @success="searchVisible = false"></search>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<el-drawer v-model="tasksVisible" :size="450" :title="$t('作业中心')" destroy-on-close>
|
<el-drawer v-model="tasksVisible" :size="800" :title="$t('作业中心')" destroy-on-close>
|
||||||
<tasks :fail="failJobCnt" @closed="tasksVisible = false"></tasks>
|
<tasks :fail="failJobCnt" @closed="tasksVisible = false"></tasks>
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
</template>
|
</template>
|
||||||
@ -83,7 +83,7 @@
|
|||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
import avatar from '../../utils/avatar'
|
import avatar from '../../utils/avatar'
|
||||||
const search = defineAsyncComponent(() => import('./search.vue'))
|
const search = defineAsyncComponent(() => import('./search.vue'))
|
||||||
const tasks = defineAsyncComponent(() => import('./tasks.vue'))
|
const tasks = defineAsyncComponent(() => import('./tasks/index.vue'))
|
||||||
const message = defineAsyncComponent(() => import('@/views/profile/message/components/list.vue'))
|
const message = defineAsyncComponent(() => import('@/views/profile/message/components/list.vue'))
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
@ -461,7 +461,7 @@ export default {
|
|||||||
作业信息: 'Job information',
|
作业信息: 'Job information',
|
||||||
查看作业记录: 'View job records',
|
查看作业记录: 'View job records',
|
||||||
异常作业: 'Abnormal jobs',
|
异常作业: 'Abnormal jobs',
|
||||||
所有作业: 'All jobs',
|
作业管理: 'Job management',
|
||||||
用户列表: 'User list',
|
用户列表: 'User list',
|
||||||
是: 'Yes',
|
是: 'Yes',
|
||||||
否: 'No',
|
否: 'No',
|
||||||
|
@ -459,7 +459,7 @@ export default {
|
|||||||
作业信息: '作业信息',
|
作业信息: '作业信息',
|
||||||
查看作业记录: '查看作业记录',
|
查看作业记录: '查看作业记录',
|
||||||
异常作业: '异常作业',
|
异常作业: '异常作业',
|
||||||
所有作业: '所有作业',
|
作业管理: '作业管理',
|
||||||
用户列表: '用户列表',
|
用户列表: '用户列表',
|
||||||
是: '是',
|
是: '是',
|
||||||
否: '否',
|
否: '否',
|
||||||
|
@ -625,5 +625,5 @@ textarea {
|
|||||||
|
|
||||||
.el-header.el-header-select-filter {
|
.el-header.el-header-select-filter {
|
||||||
height: auto;
|
height: auto;
|
||||||
padding: 0 1rem
|
padding: 0 1rem;
|
||||||
}
|
}
|
@ -1,10 +1,3 @@
|
|||||||
/*
|
|
||||||
* @Descripttion: 工具集
|
|
||||||
* @version: 1.2
|
|
||||||
* @LastEditors: Xujianchen
|
|
||||||
* @LastEditTime: 2023-03-19 11:17:54
|
|
||||||
*/
|
|
||||||
|
|
||||||
import CryptoJS from 'crypto-js'
|
import CryptoJS from 'crypto-js'
|
||||||
import sysConfig from '@/config'
|
import sysConfig from '@/config'
|
||||||
|
|
||||||
|
@ -88,6 +88,13 @@
|
|||||||
<el-table-column type="selection" width="50" />
|
<el-table-column type="selection" width="50" />
|
||||||
<na-col-id :label="$t('部门编号')" prop="id" sortable="custom" width="170" />
|
<na-col-id :label="$t('部门编号')" prop="id" sortable="custom" width="170" />
|
||||||
<el-table-column :label="$t('部门名称')" min-width="150" prop="name" sortable="custom" />
|
<el-table-column :label="$t('部门名称')" min-width="150" prop="name" sortable="custom" />
|
||||||
|
<el-table-column :label="$t('用户数量')" align="right" width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-link @click.native="dialog.save = { mode: 'view', row, tabId: 'user' }"
|
||||||
|
>{{ statistics.deptId?.find((x) => x.key.deptId === row.id.toString())?.value ?? '...' }}
|
||||||
|
</el-link>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column :label="$t('排序')" align="right" prop="sort" sortable="custom" width="100" />
|
<el-table-column :label="$t('排序')" align="right" prop="sort" sortable="custom" width="100" />
|
||||||
<el-table-column label="备注" min-width="100" prop="summary" sortable="custom" />
|
<el-table-column label="备注" min-width="100" prop="summary" sortable="custom" />
|
||||||
<el-table-column :label="$t('启用')" align="center" prop="enabled" sortable="custom" width="100">
|
<el-table-column :label="$t('启用')" align="center" prop="enabled" sortable="custom" width="100">
|
||||||
@ -172,9 +179,13 @@ export default {
|
|||||||
},
|
},
|
||||||
requiredFields: ['Enabled'],
|
requiredFields: ['Enabled'],
|
||||||
}),
|
}),
|
||||||
|
this.$API.sys_user.countBy.post({
|
||||||
|
requiredFields: ['DeptId'],
|
||||||
|
}),
|
||||||
])
|
])
|
||||||
|
|
||||||
this.statistics.enabled = res[0].data
|
this.statistics.enabled = res[0].data
|
||||||
|
this.statistics.deptId = res[1].data
|
||||||
},
|
},
|
||||||
async setEnabled(enabled) {
|
async setEnabled(enabled) {
|
||||||
let loading
|
let loading
|
||||||
|
@ -100,6 +100,9 @@ export default {
|
|||||||
Object.assign(this.form, res.data)
|
Object.assign(this.form, res.data)
|
||||||
}
|
}
|
||||||
this.loading = false
|
this.loading = false
|
||||||
|
if (data.tabId) {
|
||||||
|
this.tabId = data.tabId
|
||||||
|
}
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
//加载树数据
|
//加载树数据
|
||||||
|
@ -2,30 +2,25 @@
|
|||||||
<el-container>
|
<el-container>
|
||||||
<el-header style="border: none">
|
<el-header style="border: none">
|
||||||
<el-tabs v-model="tabId" class="w100p">
|
<el-tabs v-model="tabId" class="w100p">
|
||||||
<el-tab-pane :label="$t('所有作业')" name="all"></el-tab-pane>
|
<el-tab-pane :label="$t('作业管理')" name="all"></el-tab-pane>
|
||||||
<el-tab-pane :label="$t('异常作业')" name="fail"></el-tab-pane>
|
<el-tab-pane :label="$t('作业日志')" name="log"></el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</el-header>
|
</el-header>
|
||||||
<el-main class="nopadding">
|
<el-main class="nopadding">
|
||||||
<component :is="tabId" :status-codes="['300,399', '400,499', '500,599', '900,999']" />
|
<component :is="tabId" :status-codes="statusCodes" />
|
||||||
</el-main>
|
</el-main>
|
||||||
</el-container>
|
</el-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
const fail = defineAsyncComponent(() => import('@/views/sys/job/record/index.vue'))
|
const log = defineAsyncComponent(() => import('@/views/sys/job/record/index.vue'))
|
||||||
const all = defineAsyncComponent(() => import('@/views/sys/job/all/index.vue'))
|
const all = defineAsyncComponent(() => import('@/views/sys/job/all/index.vue'))
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { all, fail },
|
components: { all, log },
|
||||||
computed: {},
|
computed: {},
|
||||||
created() {
|
created() {},
|
||||||
if (this.$route.query.view === 'fail') {
|
|
||||||
this.tabId = 'fail'
|
|
||||||
this.$TOOL.data.set('APP_SET_FAIL_JOB_VIEW_TIME', this.$TOOL.dateFormat(new Date(), 'yyyy-MM-dd hh:mm:ss'))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
tabId: 'all',
|
tabId: 'all',
|
||||||
@ -34,7 +29,25 @@ export default {
|
|||||||
inject: ['reload'],
|
inject: ['reload'],
|
||||||
methods: {},
|
methods: {},
|
||||||
mounted() {},
|
mounted() {},
|
||||||
watch: {},
|
watch: {
|
||||||
|
tab: {
|
||||||
|
immediate: true,
|
||||||
|
deep: true,
|
||||||
|
handler(n) {
|
||||||
|
this.tabId = n
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
tab: {
|
||||||
|
type: String,
|
||||||
|
default: 'all',
|
||||||
|
},
|
||||||
|
statusCodes: {
|
||||||
|
type: Array,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style scoped></style>
|
<style scoped></style>
|
@ -33,7 +33,7 @@
|
|||||||
w100p: true,
|
w100p: true,
|
||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
:label-width="6"
|
:label-width="8"
|
||||||
@on-change="filterChange"
|
@on-change="filterChange"
|
||||||
ref="selectFilter"></sc-select-filter>
|
ref="selectFilter"></sc-select-filter>
|
||||||
</el-header>
|
</el-header>
|
||||||
|
@ -210,8 +210,8 @@ export default {
|
|||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
this.statistics.errorCode = res[0].data
|
this.statistics.errorCode = res[0].data
|
||||||
this.statistics.loginUserName = res[1].data.slice(0, 20)
|
this.statistics.loginUserName = res[1].data
|
||||||
this.statistics.createdClientIp = res[2].data.slice(0, 20)
|
this.statistics.createdClientIp = res[2].data
|
||||||
},
|
},
|
||||||
async dataChange(data) {
|
async dataChange(data) {
|
||||||
this.apis = []
|
this.apis = []
|
||||||
|
@ -45,7 +45,8 @@
|
|||||||
let api = this.apis?.find((y) => y.pathCrc32.toString() === x.key.apiPathCrc32)
|
let api = this.apis?.find((y) => y.pathCrc32.toString() === x.key.apiPathCrc32)
|
||||||
return {
|
return {
|
||||||
value: x.key.apiPathCrc32,
|
value: x.key.apiPathCrc32,
|
||||||
label: `${api?.summary} : ${api?.id}`,
|
label: api?.summary,
|
||||||
|
title: api?.id,
|
||||||
badge: x.value,
|
badge: x.value,
|
||||||
}
|
}
|
||||||
}) ?? []),
|
}) ?? []),
|
||||||
@ -318,7 +319,7 @@ export default {
|
|||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
this.statistics.httpStatusCode = res[0].data
|
this.statistics.httpStatusCode = res[0].data
|
||||||
this.statistics.apiPathCrc32 = res[1].data.slice(0, 20)
|
this.statistics.apiPathCrc32 = res[1].data
|
||||||
},
|
},
|
||||||
filterChange(data) {
|
filterChange(data) {
|
||||||
Object.entries(data).forEach(([key, value]) => {
|
Object.entries(data).forEach(([key, value]) => {
|
||||||
|
@ -135,6 +135,13 @@
|
|||||||
<el-table-column type="selection" width="50" />
|
<el-table-column type="selection" width="50" />
|
||||||
<na-col-id :label="$t('角色编号')" prop="id" sortable="custom" width="170" />
|
<na-col-id :label="$t('角色编号')" prop="id" sortable="custom" width="170" />
|
||||||
<el-table-column :label="$t('角色名称')" min-width="150" prop="name" sortable="custom" />
|
<el-table-column :label="$t('角色名称')" min-width="150" prop="name" sortable="custom" />
|
||||||
|
<el-table-column :label="$t('用户数量')" align="right" width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-link @click.native="dialog.save = { mode: 'view', row, tabId: 'user' }"
|
||||||
|
>{{ statistics.roleId?.find((x) => x.key.roleId === row.id.toString())?.value ?? '...' }}
|
||||||
|
</el-link>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column :label="$t('排序')" align="right" prop="sort" sortable="custom" width="100" />
|
<el-table-column :label="$t('排序')" align="right" prop="sort" sortable="custom" width="100" />
|
||||||
<el-table-column :label="$t('无限权限')" align="center" prop="ignorePermissionControl" sortable="custom" width="100">
|
<el-table-column :label="$t('无限权限')" align="center" prop="ignorePermissionControl" sortable="custom" width="100">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
@ -267,12 +274,16 @@ export default {
|
|||||||
},
|
},
|
||||||
requiredFields: ['DataScope'],
|
requiredFields: ['DataScope'],
|
||||||
}),
|
}),
|
||||||
|
this.$API.sys_role.userCountBy.post({
|
||||||
|
requiredFields: ['RoleId'],
|
||||||
|
}),
|
||||||
])
|
])
|
||||||
|
|
||||||
this.statistics.enabled = res[0].data
|
this.statistics.enabled = res[0].data
|
||||||
this.statistics.displayDashboard = res[1].data
|
this.statistics.displayDashboard = res[1].data
|
||||||
this.statistics.ignorePermissionControl = res[2].data
|
this.statistics.ignorePermissionControl = res[2].data
|
||||||
this.statistics.dataScope = res[3].data
|
this.statistics.dataScope = res[3].data
|
||||||
|
this.statistics.roleId = res[4].data
|
||||||
},
|
},
|
||||||
async copyRole(row) {
|
async copyRole(row) {
|
||||||
const loading = this.$loading()
|
const loading = this.$loading()
|
||||||
|
@ -179,6 +179,9 @@ export default {
|
|||||||
await this.getTrees('api')
|
await this.getTrees('api')
|
||||||
await this.getTrees('dept')
|
await this.getTrees('dept')
|
||||||
this.loading = false
|
this.loading = false
|
||||||
|
if (data.tabId) {
|
||||||
|
this.tabId = data.tabId
|
||||||
|
}
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -204,6 +204,7 @@ export default {
|
|||||||
this.statistics.total = this.$refs.table?.total
|
this.statistics.total = this.$refs.table?.total
|
||||||
const res = await Promise.all([
|
const res = await Promise.all([
|
||||||
this.$API.sys_user.countBy.post({
|
this.$API.sys_user.countBy.post({
|
||||||
|
filter: this.query.filter,
|
||||||
dynamicFilter: {
|
dynamicFilter: {
|
||||||
filters: this.query.dynamicFilter.filters,
|
filters: this.query.dynamicFilter.filters,
|
||||||
},
|
},
|
||||||
@ -291,6 +292,23 @@ export default {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.roleId) {
|
||||||
|
this.$refs.search.form.filter.roleId = this.roleId
|
||||||
|
this.$refs.search.keeps.push({
|
||||||
|
field: 'roleId',
|
||||||
|
value: this.roleId,
|
||||||
|
type: 'filter',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (this.deptId) {
|
||||||
|
this.$refs.search.form.filter.deptId = this.deptId
|
||||||
|
this.$refs.search.keeps.push({
|
||||||
|
field: 'deptId',
|
||||||
|
value: this.deptId,
|
||||||
|
type: 'filter',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
this.$refs.search.form.dy.enabled = true
|
this.$refs.search.form.dy.enabled = true
|
||||||
this.$refs.search.keeps.push({
|
this.$refs.search.keeps.push({
|
||||||
field: 'enabled',
|
field: 'enabled',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user