feat: 框架代码同步 (#148)

[skip ci]

Co-authored-by: tk <fiyne1a@dingtalk.com>
This commit is contained in:
nsnail 2024-06-24 16:04:28 +08:00 committed by GitHub
parent d00f0d2d9c
commit 8bc8aa960c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
121 changed files with 2369 additions and 1497 deletions

View File

@ -24,7 +24,7 @@
"Component": "sys/user",
"Icon": "el-icon-user",
"Id": 373837957840901,
"Name": "sys-user",
"Name": "sys/user",
"ParentId": 373837917724677,
"Path": "/sys/user",
"Sort": 100,
@ -35,7 +35,7 @@
"Component": "sys/role",
"Icon": "sc-icon-role",
"Id": 373838018527237,
"Name": "sys-role",
"Name": "sys/role",
"ParentId": 373837917724677,
"Path": "/sys/role",
"Sort": 99,
@ -46,7 +46,7 @@
"Component": "sys/dept",
"Icon": "sc-icon-dept",
"Id": 373838045605893,
"Name": "sys-dept",
"Name": "sys/dept",
"ParentId": 373837917724677,
"Path": "/sys/dept",
"Sort": 98,
@ -57,7 +57,7 @@
"Component": "sys/menu",
"Icon": "el-icon-fold",
"Id": 373838070898693,
"Name": "sys-menu",
"Name": "sys/menu",
"ParentId": 373837917724677,
"Path": "/sys/menu",
"Sort": 97,
@ -78,7 +78,7 @@
"Component": "sys/config",
"Icon": "el-icon-set-up",
"Id": 380415005847557,
"Name": "sys-config",
"Name": "sys/config",
"ParentId": 485278637670422,
"Path": "/sys/config",
"Sort": 100,
@ -89,7 +89,7 @@
"Component": "sys/job",
"Icon": "sc-icon-ScheduledJob",
"Id": 510067557638158,
"Name": "sys-job",
"Name": "sys/job",
"ParentId": 485278637670422,
"Path": "/sys/job",
"Sort": 99,
@ -100,7 +100,7 @@
"Component": "sys/dic",
"Icon": "sc-icon-dic",
"Id": 375315654221829,
"Name": "sys-dic",
"Name": "sys/dic",
"ParentId": 485278637670422,
"Path": "/sys/dic",
"Sort": 98,
@ -111,7 +111,7 @@
"Component": "sys/msg",
"Icon": "el-icon-message",
"Id": 482779610341392,
"Name": "sys-msg",
"Name": "sys/msg",
"ParentId": 485278637670422,
"Path": "/sys/msg",
"Sort": 97,
@ -122,7 +122,7 @@
"Component": "sys/api",
"Icon": "sc-icon-api",
"Id": 397880678895621,
"Name": "sys-api",
"Name": "sys/api",
"ParentId": 485278637670422,
"Path": "/sys/api",
"Sort": 96,
@ -133,7 +133,7 @@
"Component": "sys/cache",
"Icon": "sc-icon-memory",
"Id": 374911555702789,
"Name": "sys-cache",
"Name": "sys/cache",
"ParentId": 485278637670422,
"Path": "/sys/cache",
"Sort": 95,
@ -154,7 +154,7 @@
"Component": "sys/log/operation",
"Icon": "el-icon-pointer",
"Id": 485285246504976,
"Name": "sys-log-operation",
"Name": "sys/log/operation",
"ParentId": 374792687640581,
"Path": "/sys/log/operation",
"Sort": 100,
@ -165,7 +165,7 @@
"Component": "sys/log/login",
"Icon": "sc-icon-OpenDoor",
"Id": 485285246504970,
"Name": "sys-log-login",
"Name": "sys/log/login",
"ParentId": 374792687640581,
"Path": "/sys/log/login",
"Sort": 99,
@ -186,7 +186,7 @@
"Component": "dev/code",
"Icon": "sc-icon-code2",
"Id": 373838147022853,
"Name": "dev-code",
"Name": "dev/code",
"ParentId": 373838105399301,
"Path": "/dev/code",
"Sort": 100,
@ -197,10 +197,20 @@
"Id": 482777529417739,
"ParentId": 373838105399301,
"Icon": "el-icon-eleme-filled",
"Name": "dev-element",
"Path": "https://element-plus.gitee.io/zh-CN/component/button.html",
"Name": "dev/element",
"Path": "http://element-plus.org/zh-CN/component/overview.html",
"Sort": 99,
"Title": "Element",
"Type": 3,
},
{
"Id": 560217289232398,
"ParentId": 373838105399301,
"Icon": "sc-icon-FreeSql",
"Name": "dev/freesql",
"Path": "https://freesql.net/guide",
"Sort": 99,
"Title": "FreeSql",
"Type": 3,
}
]

View File

@ -34,5 +34,13 @@
{
"ApiId": "api/sys/site.msg/set.site.msg.status",
"RoleId": 371729946431493,
},
{
"ApiId": "api/sys/user/get.session.user.app.config",
"RoleId": 371729946431493,
},
{
"ApiId": "api/sys/user/set.session.user.app.config",
"RoleId": 371729946431493,
}
]

View File

@ -6,5 +6,13 @@
"Password": "A8E87D23-49BC-25A1-1C7C-59186BEF5D15",
"Token": "A9AFD92E-A33D-4152-9A6C-A9C141D24887",
"UserName": "root"
},
{
"DeptId": 372119301627909,
"Enabled": true,
"Id": 560217289236492,
"Password": "A8E87D23-49BC-25A1-1C7C-59186BEF5D15",
"Token": "4208EA97-B32F-4E39-A290-4C0D27B61EBF",
"UserName": "user"
}
]

View File

@ -1,5 +1,8 @@
[
{
"Id": 370942943322181
},
{
"Id": 560217289236492
}
]

View File

@ -2,5 +2,9 @@
{
"RoleId": 370943613149253,
"UserId": 370942943322181
},
{
"RoleId": 371729946431493,
"UserId": 560217289236492
}
]

View File

@ -954,6 +954,13 @@ public class AllTests(WebApplicationFactory<Startup> factory, ITestOutputHelper
return default;
}
/// <inheritdoc />
[Fact]
public Task<GetSessionUserAppConfigRsp> GetSessionUserAppConfigAsync()
{
return default;
}
/// <inheritdoc />
[Fact]
public async Task<string> GetVersionAsync()
@ -1342,7 +1349,7 @@ public class AllTests(WebApplicationFactory<Startup> factory, ITestOutputHelper
/// <inheritdoc />
[InlineData(default)]
[Theory]
public Task<uint> ResetPasswordAsync(ResetPasswordReq req)
public Task<int> ResetPasswordAsync(ResetPasswordReq req)
{
return default;
}
@ -1363,6 +1370,14 @@ public class AllTests(WebApplicationFactory<Startup> factory, ITestOutputHelper
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public Task<int> SetDisplayDashboardAsync(SetDisplayDashboardReq req)
{
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
@ -1374,7 +1389,7 @@ public class AllTests(WebApplicationFactory<Startup> factory, ITestOutputHelper
/// <inheritdoc />
[InlineData(default)]
[Theory]
public Task SetEnabledAsync(SetDeptEnabledReq req)
public Task<int> SetEnabledAsync(SetDeptEnabledReq req)
{
return default;
}
@ -1382,7 +1397,7 @@ public class AllTests(WebApplicationFactory<Startup> factory, ITestOutputHelper
/// <inheritdoc />
[InlineData(default)]
[Theory]
public Task SetEnabledAsync(SetRoleEnabledReq req)
public Task<int> SetEnabledAsync(SetConfigEnabledReq req)
{
return default;
}
@ -1390,7 +1405,7 @@ public class AllTests(WebApplicationFactory<Startup> factory, ITestOutputHelper
/// <inheritdoc />
[InlineData(default)]
[Theory]
public Task SetEnabledAsync(SetJobEnabledReq req)
public Task<int> SetEnabledAsync(SetRoleEnabledReq req)
{
return default;
}
@ -1398,7 +1413,23 @@ public class AllTests(WebApplicationFactory<Startup> factory, ITestOutputHelper
/// <inheritdoc />
[InlineData(default)]
[Theory]
public Task SetEnabledAsync(SetUserEnabledReq req)
public Task<int> SetEnabledAsync(SetJobEnabledReq req)
{
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public Task<int> SetEnabledAsync(SetUserEnabledReq req)
{
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public Task<int> SetIgnorePermissionControlAsync(SetIgnorePermissionControlReq req)
{
return default;
}
@ -1414,7 +1445,15 @@ public class AllTests(WebApplicationFactory<Startup> factory, ITestOutputHelper
/// <inheritdoc />
[InlineData(default)]
[Theory]
public Task<uint> SetPasswordAsync(SetPasswordReq req)
public Task<int> SetPasswordAsync(SetPasswordReq req)
{
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public Task<int> SetSessionUserAppConfigAsync(SetSessionUserAppConfigReq req)
{
return default;
}

View File

@ -9,6 +9,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
[Index($"{Chars.FLG_DB_INDEX_PREFIX}{nameof(JobId)}_{nameof(TimeId)}", $"{nameof(JobId)},{nameof(TimeId)}", true)]
[Index(Chars.FLG_DB_INDEX_PREFIX + nameof(CreatedTime), nameof(CreatedTime), false)]
[Index(Chars.FLG_DB_INDEX_PREFIX + nameof(JobId), nameof(JobId), false)]
[Index(Chars.FLG_DB_INDEX_PREFIX + nameof(HttpStatusCode), nameof(HttpStatusCode), false)]
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_JobRecord))]
public record Sys_JobRecord : LiteImmutableEntity
{

View File

@ -115,7 +115,7 @@ public record Sys_User : VersionEntity, IFieldSummary, IFieldEnabled, IRegister
? Array.Empty<Sys_Role>()
: s.RoleIds.Select(x => new Sys_Role { Id = x }));
_ = config.ForType<EditSingleUserReq, Sys_User>()
_ = config.ForType<EditUserReq, Sys_User>()
.Map( //
d => d.Password, s => s.PasswordText.NullOrEmpty() ? Guid.Empty : s.PasswordText.Pwd().Guid())
.Map( //

View File

@ -9,6 +9,17 @@ namespace NetAdmin.Domain.DbMaps.Sys;
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_UserProfile))]
public record Sys_UserProfile : VersionEntity, IRegister
{
/// <summary>
/// 应用配置
/// </summary>
#if DBTYPE_SQLSERVER
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
#else
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
#endif
[JsonIgnore]
public virtual string AppConfig { get; init; }
/// <summary>
/// 出生日期
/// </summary>

View File

@ -8,5 +8,5 @@ public sealed record GetAllEntriesReq : DataAbstraction
/// <summary>
/// 数据库索引号
/// </summary>
public uint DbIndex { get; init; }
public int DbIndex { get; init; }
}

View File

@ -1,22 +1,22 @@
using NetAdmin.Domain.DbMaps.Dependency;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.UserProfile;
namespace NetAdmin.Domain.Dto.Sys.User;
namespace NetAdmin.Domain.Dto.Sys.Config;
/// <summary>
/// 请求:编辑用户单体
/// 请求:启用/禁用配置
/// </summary>
public sealed record EditSingleUserReq : CreateEditUserReq
public sealed record SetConfigEnabledReq : Sys_Config
{
/// <inheritdoc cref="IFieldEnabled.Enabled" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override bool Enabled { get; init; }
/// <inheritdoc cref="EntityBase{T}.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Id { get; init; }
/// <inheritdoc cref="Sys_User.Profile" />
public new EditUserProfileReq Profile { get; init; }
/// <inheritdoc cref="IFieldVersion.Version" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Version { get; init; }

View File

@ -5,4 +5,11 @@ namespace NetAdmin.Domain.Dto.Sys.Job;
/// <summary>
/// 请求:完成计划作业
/// </summary>
public sealed record FinishJobReq : Sys_Job;
public sealed record FinishJobReq : Sys_Job, IRegister
{
/// <inheritdoc />
public void Register(TypeAdapterConfig config)
{
_ = config.ForType<QueryJobRsp, FinishJobReq>().Map(d => d.LastStatusCode, s => ((Sys_Job)s).LastStatusCode);
}
}

View File

@ -12,6 +12,17 @@ namespace NetAdmin.Domain.Dto.Sys.Job;
/// </summary>
public sealed record QueryJobRsp : Sys_Job
{
/// <inheritdoc cref="Sys_Job.LastStatusCode" />
public new string LastStatusCode =>
#pragma warning disable IDE0072
base.LastStatusCode switch {
#pragma warning restore IDE0072
null => null
, _ => (int)base.LastStatusCode.Value == Numbers.HTTP_STATUS_BIZ_FAIL
? nameof(ErrorCodes.Unhandled).ToLowerCamelCase()
: base.LastStatusCode.Value.ToString().ToLowerCamelCase()
};
/// <inheritdoc cref="IFieldCreatedTime.CreatedTime" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override DateTime CreatedTime { get; init; }
@ -52,10 +63,6 @@ public sealed record QueryJobRsp : Sys_Job
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override DateTime? LastExecTime { get; init; }
/// <inheritdoc cref="Sys_Job.LastStatusCode" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override HttpStatusCode? LastStatusCode { get; init; }
/// <inheritdoc cref="IFieldModifiedTime.ModifiedTime" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override DateTime? ModifiedTime { get; init; }

View File

@ -1,6 +1,7 @@
using NetAdmin.Domain.DbMaps.Dependency;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.Job;
using HttpMethods = NetAdmin.Domain.Enums.HttpMethods;
namespace NetAdmin.Domain.Dto.Sys.JobRecord;
@ -11,8 +12,10 @@ namespace NetAdmin.Domain.Dto.Sys.JobRecord;
public sealed record QueryJobRecordRsp : Sys_JobRecord
{
/// <inheritdoc cref="Sys_JobRecord.HttpStatusCode" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public new HttpStatusCode HttpStatusCode => (HttpStatusCode)base.HttpStatusCode;
public new string HttpStatusCode =>
base.HttpStatusCode == Numbers.HTTP_STATUS_BIZ_FAIL
? nameof(ErrorCodes.Unhandled).ToLowerCamelCase()
: ((HttpStatusCode)base.HttpStatusCode).ToString().ToLowerCamelCase();
/// <inheritdoc cref="IFieldCreatedTime.CreatedTime" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
@ -30,6 +33,11 @@ public sealed record QueryJobRecordRsp : Sys_JobRecord
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Id { get; init; }
/// <summary>
/// 作业信息
/// </summary>
public new QueryJobRsp Job { get; init; }
/// <inheritdoc cref="Sys_JobRecord.JobId" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long JobId { get; init; }

View File

@ -0,0 +1,23 @@
using NetAdmin.Domain.DbMaps.Dependency;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
using NetAdmin.Domain.DbMaps.Sys;
namespace NetAdmin.Domain.Dto.Sys.Role;
/// <summary>
/// 请求:设置是否显示仪表板
/// </summary>
public sealed record SetDisplayDashboardReq : Sys_Role
{
/// <inheritdoc cref="Sys_Role.DisplayDashboard" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override bool DisplayDashboard { get; init; }
/// <inheritdoc cref="EntityBase{T}.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Id { get; init; }
/// <inheritdoc cref="IFieldVersion.Version" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Version { get; init; }
}

View File

@ -0,0 +1,23 @@
using NetAdmin.Domain.DbMaps.Dependency;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
using NetAdmin.Domain.DbMaps.Sys;
namespace NetAdmin.Domain.Dto.Sys.Role;
/// <summary>
/// 请求:设置是否忽略权限控制
/// </summary>
public sealed record SetIgnorePermissionControlReq : Sys_Role
{
/// <inheritdoc cref="EntityBase{T}.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Id { get; init; }
/// <inheritdoc cref="Sys_Role.IgnorePermissionControl" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override bool IgnorePermissionControl { get; init; }
/// <inheritdoc cref="IFieldVersion.Version" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Version { get; init; }
}

View File

@ -0,0 +1,23 @@
using NetAdmin.Domain.DbMaps.Dependency;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
using NetAdmin.Domain.DbMaps.Sys;
namespace NetAdmin.Domain.Dto.Sys.UserProfile;
/// <summary>
/// 响应:获取当前用户应用配置
/// </summary>
public sealed record GetSessionUserAppConfigRsp : Sys_UserProfile
{
/// <inheritdoc cref="Sys_UserProfile.AppConfig" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string AppConfig { get; init; }
/// <inheritdoc cref="EntityBase{T}.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Id { get; init; }
/// <inheritdoc cref="IFieldVersion.Version" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Version { get; init; }
}

View File

@ -0,0 +1,13 @@
using NetAdmin.Domain.DbMaps.Sys;
namespace NetAdmin.Domain.Dto.Sys.UserProfile;
/// <summary>
/// 请求:设置当前用户应用配置
/// </summary>
public record SetSessionUserAppConfigReq : Sys_UserProfile
{
/// <inheritdoc cref="Sys_UserProfile.AppConfig" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string AppConfig { get; init; }
}

View File

@ -21,7 +21,8 @@ public static class UnitOfWorkManagerExtensions
unitOfWork.Commit();
logger?.Info($"{Ln.事务已提交}: {hashCode}");
}
catch {
catch (Exception ex) {
logger?.Warn(ex);
unitOfWork.Rollback();
logger?.Warn($"{Ln.事务已回滚}: {hashCode}");
throw;

View File

@ -21,4 +21,9 @@ public interface IConfigModule : ICrudModule<CreateConfigReq, QueryConfigRsp //
/// 获取最新有效配置
/// </summary>
Task<QueryConfigRsp> GetLatestConfigAsync();
/// <summary>
/// 设置配置启用状态
/// </summary>
Task<int> SetEnabledAsync(SetConfigEnabledReq req);
}

View File

@ -20,5 +20,5 @@ public interface IDeptModule : ICrudModule<CreateDeptReq, QueryDeptRsp // 创建
/// <summary>
/// 启用/禁用部门
/// </summary>
Task SetEnabledAsync(SetDeptEnabledReq req);
Task<int> SetEnabledAsync(SetDeptEnabledReq req);
}

View File

@ -52,5 +52,5 @@ public interface IJobModule : ICrudModule<CreateJobReq, QueryJobRsp // 创建类
/// <summary>
/// 设置计划作业启用状态
/// </summary>
Task SetEnabledAsync(SetJobEnabledReq req);
Task<int> SetEnabledAsync(SetJobEnabledReq req);
}

View File

@ -17,8 +17,18 @@ public interface IRoleModule : ICrudModule<CreateRoleReq, QueryRoleRsp // 创建
/// </summary>
Task<QueryRoleRsp> EditAsync(EditRoleReq req);
/// <summary>
/// 设置是否显示仪表板
/// </summary>
Task<int> SetDisplayDashboardAsync(SetDisplayDashboardReq req);
/// <summary>
/// 启用/禁用角色
/// </summary>
Task SetEnabledAsync(SetRoleEnabledReq req);
Task<int> SetEnabledAsync(SetRoleEnabledReq req);
/// <summary>
/// 设置是否忽略权限控制
/// </summary>
Task<int> SetIgnorePermissionControlAsync(SetIgnorePermissionControlReq req);
}

View File

@ -8,9 +8,9 @@ namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// <summary>
/// 用户模块
/// </summary>
public interface IUserModule : ICrudModule<CreateUserReq, QueryUserRsp // 创建类型
, QueryUserReq, QueryUserRsp // 查询类型
, DelReq // 删除类型
public partial interface IUserModule : ICrudModule<CreateUserReq, QueryUserRsp // 创建类型
, QueryUserReq, QueryUserRsp // 查询类型
, DelReq // 删除类型
>
{
/// <summary>
@ -51,7 +51,7 @@ public interface IUserModule : ICrudModule<CreateUserReq, QueryUserRsp // 创建
/// <summary>
/// 重设密码
/// </summary>
Task<uint> ResetPasswordAsync(ResetPasswordReq req);
Task<int> ResetPasswordAsync(ResetPasswordReq req);
/// <summary>
/// 设置用户头像
@ -66,7 +66,7 @@ public interface IUserModule : ICrudModule<CreateUserReq, QueryUserRsp // 创建
/// <summary>
/// 启用/禁用用户
/// </summary>
Task SetEnabledAsync(SetUserEnabledReq req);
Task<int> SetEnabledAsync(SetUserEnabledReq req);
/// <summary>
/// 设置手机号码
@ -76,7 +76,7 @@ public interface IUserModule : ICrudModule<CreateUserReq, QueryUserRsp // 创建
/// <summary>
/// 设置密码
/// </summary>
Task<uint> SetPasswordAsync(SetPasswordReq req);
Task<int> SetPasswordAsync(SetPasswordReq req);
/// <summary>
/// 当前用户信息

View File

@ -0,0 +1,17 @@
using NetAdmin.Domain.Dto.Sys.UserProfile;
// ReSharper disable once CheckNamespace
namespace NetAdmin.SysComponent.Application.Modules.Sys;
public partial interface IUserModule
{
/// <summary>
/// 获取当前用户应用配置
/// </summary>
Task<GetSessionUserAppConfigRsp> GetSessionUserAppConfigAsync();
/// <summary>
/// 设置当前用户应用配置
/// </summary>
Task<int> SetSessionUserAppConfigAsync(SetSessionUserAppConfigReq req);
}

View File

@ -122,6 +122,13 @@ public sealed class ConfigService(BasicRepository<Sys_Config, long> rpo) //
return ret.Adapt<IEnumerable<QueryConfigRsp>>();
}
/// <inheritdoc />
public Task<int> SetEnabledAsync(SetConfigEnabledReq req)
{
req.ThrowIfInvalid();
return UpdateAsync(req, [nameof(req.Enabled)]);
}
private ISelect<Sys_Config> QueryInternal(QueryReq<QueryConfigReq> req)
{
var ret = Rpo.Select.Include(a => a.UserRegisterDept)

View File

@ -24,16 +24,14 @@ public sealed class ConstantService : ServiceBase<IConstantService>, IConstantSe
.ToDictionary(x => x.Name, x => //
x.GetEnumValues().Cast<Enum>().ToDictionary(y => y.ToString(), GetDicValue));
ret.Add( //
$"{nameof(HttpStatusCode)}s" //
, Enum.GetNames<HttpStatusCode>()
.ToDictionary(
x => x
, x => new[] {
Convert.ToInt64(Enum.Parse<HttpStatusCode>(x), CultureInfo.InvariantCulture)
.ToString(CultureInfo.InvariantCulture)
, x
}));
var httpStatusCodes = Enum.GetNames<HttpStatusCode>().ToDictionary(x => x, GetHttpStatusCodeDicValue);
httpStatusCodes.Add( //
nameof(ErrorCodes.Unhandled)
, [
Numbers.HTTP_STATUS_BIZ_FAIL.ToInvString(), nameof(ErrorCodes.Unhandled)
, nameof(Indicates.Danger).ToLowerInvariant()
]);
ret.Add($"{nameof(HttpStatusCode)}s", httpStatusCodes);
return ret;
static string[] GetDicValue(Enum y)
@ -45,6 +43,19 @@ public sealed class ConstantService : ServiceBase<IConstantService>, IConstantSe
var indicate = y.GetAttributeOfType<IndicatorAttribute>()?.Indicate.ToLowerInvariant();
return indicate.NullOrEmpty() ? ret : [..ret, indicate];
}
static string[] GetHttpStatusCodeDicValue(string name)
{
var codeInt = Convert.ToInt64(Enum.Parse<HttpStatusCode>(name), CultureInfo.InvariantCulture);
return [
codeInt.ToString(CultureInfo.InvariantCulture), name
, (codeInt switch {
>= 200 and < 300 => nameof(Indicates.Success)
, < 400 => nameof(Indicates.Warning)
, _ => nameof(Indicates.Danger)
}).ToLowerInvariant()
];
}
}
/// <inheritdoc />

View File

@ -13,4 +13,14 @@ public interface IUserProfileService : IService, IUserProfileModule
/// 编辑用户档案
/// </summary>
Task<int> EditAsync(EditUserProfileReq req);
/// <summary>
/// 获取当前用户配置
/// </summary>
Task<GetSessionUserAppConfigRsp> GetSessionUserAppConfigAsync();
/// <summary>
/// 设置当前用户配置
/// </summary>
Task<int> SetSessionUserAppConfigAsync(SetSessionUserAppConfigReq req);
}

View File

@ -120,7 +120,7 @@ public sealed class DeptService(BasicRepository<Sys_Dept, long> rpo) //
}
/// <inheritdoc />
public Task SetEnabledAsync(SetDeptEnabledReq req)
public Task<int> SetEnabledAsync(SetDeptEnabledReq req)
{
req.ThrowIfInvalid();
return UpdateAsync(req, [nameof(req.Enabled)]);

View File

@ -165,7 +165,8 @@ public sealed class JobRecordService(BasicRepository<Sys_JobRecord, long> rpo) /
private ISelect<Sys_JobRecord> QueryInternal(QueryReq<QueryJobRecordReq> req)
{
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
var ret = Rpo.Select.Include(a => a.Job)
.WhereDynamicFilter(req.DynamicFilter)
.WhereDynamic(req.Filter)
.WhereIf( //
req.Keywords?.Length > 0

View File

@ -295,7 +295,7 @@ public sealed class JobService(BasicRepository<Sys_Job, long> rpo, IJobRecordSer
}
/// <inheritdoc />
public Task SetEnabledAsync(SetJobEnabledReq req)
public Task<int> SetEnabledAsync(SetJobEnabledReq req)
{
req.ThrowIfInvalid();
return UpdateAsync(req, [nameof(Sys_Job.Enabled)]);

View File

@ -124,12 +124,26 @@ public sealed class RoleService(BasicRepository<Sys_Role, long> rpo) //
}
/// <inheritdoc />
public Task SetEnabledAsync(SetRoleEnabledReq req)
public Task<int> SetDisplayDashboardAsync(SetDisplayDashboardReq req)
{
req.ThrowIfInvalid();
return UpdateAsync(req, [nameof(req.DisplayDashboard)]);
}
/// <inheritdoc />
public Task<int> SetEnabledAsync(SetRoleEnabledReq req)
{
req.ThrowIfInvalid();
return UpdateAsync(req, [nameof(req.Enabled)]);
}
/// <inheritdoc />
public Task<int> SetIgnorePermissionControlAsync(SetIgnorePermissionControlReq req)
{
req.ThrowIfInvalid();
return UpdateAsync(req, [nameof(req.IgnorePermissionControl)]);
}
private ISelect<Sys_Role> QueryInternal(QueryReq<QueryRoleReq> req)
{
var ret = Rpo.Select.IncludeMany(a => a.Depts.Select(b => new Sys_Dept { Id = b.Id }))

View File

@ -56,7 +56,7 @@ public sealed class UserProfileService(BasicRepository<Sys_UserProfile, long> rp
/// <inheritdoc />
public Task<int> EditAsync(EditUserProfileReq req)
{
return UpdateAsync(req, null);
return UpdateAsync(req.Adapt<Sys_UserProfile>(), null);
}
/// <inheritdoc />
@ -80,6 +80,13 @@ public sealed class UserProfileService(BasicRepository<Sys_UserProfile, long> rp
return ret.Adapt<QueryUserProfileRsp>();
}
/// <inheritdoc />
public async Task<GetSessionUserAppConfigRsp> GetSessionUserAppConfigAsync()
{
var ret = await Rpo.Select.Where(a => a.Id == UserToken.Id).ToOneAsync().ConfigureAwait(false);
return ret.Adapt<GetSessionUserAppConfigRsp>();
}
/// <inheritdoc />
public async Task<PagedQueryRsp<QueryUserProfileRsp>> PagedQueryAsync(PagedQueryReq<QueryUserProfileReq> req)
{
@ -147,6 +154,13 @@ public sealed class UserProfileService(BasicRepository<Sys_UserProfile, long> rp
});
}
/// <inheritdoc />
public Task<int> SetSessionUserAppConfigAsync(SetSessionUserAppConfigReq req)
{
req.ThrowIfInvalid();
return UpdateAsync(req, [nameof(req.AppConfig)], null, a => a.Id == UserToken.Id, null, true);
}
private ISelect<Sys_UserProfile, Sys_DicContent, Sys_DicContent, Sys_DicContent, Sys_DicContent> QueryInternal(
QueryReq<QueryUserProfileReq> req)
{

View File

@ -163,6 +163,12 @@ public sealed class UserService(
return ret.Adapt<QueryUserRsp>();
}
/// <inheritdoc />
public Task<GetSessionUserAppConfigRsp> GetSessionUserAppConfigAsync()
{
return userProfileService.GetSessionUserAppConfigAsync();
}
/// <inheritdoc />
/// <exception cref="NetAdminInvalidOperationException">用户名或密码错误</exception>
public async Task<LoginRsp> LoginByPwdAsync(LoginByPwdReq req)
@ -259,7 +265,7 @@ public sealed class UserService(
/// <inheritdoc />
/// <exception cref="NetAdminInvalidOperationException">验证码不正确</exception>
/// <exception cref="NetAdminInvalidOperationException">用户不存在</exception>
public async Task<uint> ResetPasswordAsync(ResetPasswordReq req)
public async Task<int> ResetPasswordAsync(ResetPasswordReq req)
{
req.ThrowIfInvalid();
if (await verifyCodeService.VerifyAsync(req.VerifySmsCodeReq).ConfigureAwait(false)) {
@ -269,7 +275,7 @@ public sealed class UserService(
Password = req.PasswordText.Pwd()
.Guid()
};
return (uint)await UpdateAsync(dto, [nameof(Sys_User.Password)]).ConfigureAwait(false);
return await UpdateAsync(dto, [nameof(Sys_User.Password)]).ConfigureAwait(false);
}
throw new NetAdminInvalidOperationException(Ln.);
@ -335,7 +341,7 @@ public sealed class UserService(
}
/// <inheritdoc />
public Task SetEnabledAsync(SetUserEnabledReq req)
public Task<int> SetEnabledAsync(SetUserEnabledReq req)
{
req.ThrowIfInvalid();
return UpdateAsync(req, [nameof(req.Enabled)]);
@ -386,18 +392,23 @@ public sealed class UserService(
}
/// <inheritdoc />
public async Task<uint> SetPasswordAsync(SetPasswordReq req)
public async Task<int> SetPasswordAsync(SetPasswordReq req)
{
req.ThrowIfInvalid();
var version = await Rpo.Where(a => a.Id == UserToken.Id && a.Password == req.OldPassword.Pwd().Guid())
.ToOneAsync(a => new long?(a.Version))
.ConfigureAwait(false) ?? throw new NetAdminInvalidOperationException($"{Ln.旧密码不正确}");
var ret = await UpdateAsync(
return await UpdateAsync(
new Sys_User { Id = UserToken.Id, Password = req.NewPassword.Pwd().Guid(), Version = version }
, [nameof(Sys_User.Password)])
.ConfigureAwait(false);
return (uint)ret;
}
/// <inheritdoc />
public Task<int> SetSessionUserAppConfigAsync(SetSessionUserAppConfigReq req)
{
return userProfileService.SetSessionUserAppConfigAsync(req);
}
/// <inheritdoc />
@ -495,9 +506,9 @@ public sealed class UserService(
req.Filter?.RoleId > 0, a => a.Roles.Any(b => b.Id == req.Filter.RoleId))
.WhereIf( //
req.Keywords?.Length > 0
, a => a.Id == req.Keywords.Int64Try(0) || a.UserName.Contains(req.Keywords) ||
a.Mobile.Contains(req.Keywords) || a.Email.Contains(req.Keywords) ||
a.Summary.Contains(req.Keywords));
, a => a.Id == req.Keywords.Int64Try(0) || a.UserName == req.Keywords ||
a.Mobile == req.Keywords ||
a.Email == req.Keywords || a.Summary.Contains(req.Keywords));
switch (req.Order) {
case Orders.None:
return ret;

View File

@ -69,4 +69,10 @@ public sealed class ConfigCache(IDistributedCache cache, IConfigService service)
{
return Service.QueryAsync(req);
}
/// <inheritdoc />
public Task<int> SetEnabledAsync(SetConfigEnabledReq req)
{
return Service.SetEnabledAsync(req);
}
}

View File

@ -65,7 +65,7 @@ public sealed class DeptCache(IDistributedCache cache, IDeptService service) //
}
/// <inheritdoc />
public Task SetEnabledAsync(SetDeptEnabledReq req)
public Task<int> SetEnabledAsync(SetDeptEnabledReq req)
{
return Service.SetEnabledAsync(req);
}

View File

@ -123,7 +123,7 @@ public sealed class JobCache(IDistributedCache cache, IJobService service)
}
/// <inheritdoc />
public Task SetEnabledAsync(SetJobEnabledReq req)
public Task<int> SetEnabledAsync(SetJobEnabledReq req)
{
return Service.SetEnabledAsync(req);
}

View File

@ -65,8 +65,20 @@ public sealed class RoleCache(IDistributedCache cache, IRoleService service) //
}
/// <inheritdoc />
public Task SetEnabledAsync(SetRoleEnabledReq req)
public Task<int> SetDisplayDashboardAsync(SetDisplayDashboardReq req)
{
return Service.SetDisplayDashboardAsync(req);
}
/// <inheritdoc />
public Task<int> SetEnabledAsync(SetRoleEnabledReq req)
{
return Service.SetEnabledAsync(req);
}
/// <inheritdoc />
public Task<int> SetIgnorePermissionControlAsync(SetIgnorePermissionControlReq req)
{
return Service.SetIgnorePermissionControlAsync(req);
}
}

View File

@ -65,6 +65,12 @@ public sealed class UserCache(IDistributedCache cache, IUserService service, IVe
return Service.GetAsync(req);
}
/// <inheritdoc />
public Task<GetSessionUserAppConfigRsp> GetSessionUserAppConfigAsync()
{
return Service.GetSessionUserAppConfigAsync();
}
/// <inheritdoc />
public Task<LoginRsp> LoginByPwdAsync(LoginByPwdReq req)
{
@ -157,7 +163,7 @@ public sealed class UserCache(IDistributedCache cache, IUserService service, IVe
}
/// <inheritdoc />
public Task<uint> ResetPasswordAsync(ResetPasswordReq req)
public Task<int> ResetPasswordAsync(ResetPasswordReq req)
{
return Service.ResetPasswordAsync(req);
}
@ -177,7 +183,7 @@ public sealed class UserCache(IDistributedCache cache, IUserService service, IVe
}
/// <inheritdoc />
public Task SetEnabledAsync(SetUserEnabledReq req)
public Task<int> SetEnabledAsync(SetUserEnabledReq req)
{
return Service.SetEnabledAsync(req);
}
@ -189,11 +195,17 @@ public sealed class UserCache(IDistributedCache cache, IUserService service, IVe
}
/// <inheritdoc />
public Task<uint> SetPasswordAsync(SetPasswordReq req)
public Task<int> SetPasswordAsync(SetPasswordReq req)
{
return Service.SetPasswordAsync(req);
}
/// <inheritdoc />
public Task<int> SetSessionUserAppConfigAsync(SetSessionUserAppConfigReq req)
{
return Service.SetSessionUserAppConfigAsync(req);
}
/// <inheritdoc />
public Task<UserInfoRsp> UserInfoAsync()
{

View File

@ -99,4 +99,12 @@ public sealed class ConfigController(IConfigCache cache) : ControllerBase<IConfi
{
return Cache.QueryAsync(req);
}
/// <summary>
/// 设置配置启用状态
/// </summary>
public Task<int> SetEnabledAsync(SetConfigEnabledReq req)
{
return Cache.SetEnabledAsync(req);
}
}

View File

@ -95,7 +95,7 @@ public sealed class DeptController(IDeptCache cache) : ControllerBase<IDeptCache
/// <summary>
/// 启用/禁用部门
/// </summary>
public Task SetEnabledAsync(SetDeptEnabledReq req)
public Task<int> SetEnabledAsync(SetDeptEnabledReq req)
{
return Cache.SetEnabledAsync(req);
}

View File

@ -144,7 +144,7 @@ public sealed class JobController(IJobCache cache) : ControllerBase<IJobCache, I
/// <summary>
/// 启用/禁用作业
/// </summary>
public Task SetEnabledAsync(SetJobEnabledReq req)
public Task<int> SetEnabledAsync(SetJobEnabledReq req)
{
return Cache.SetEnabledAsync(req);
}

View File

@ -91,11 +91,27 @@ public sealed class RoleController(IRoleCache cache) : ControllerBase<IRoleCache
return Cache.QueryAsync(req);
}
/// <summary>
/// 设置是否显示仪表板
/// </summary>
public Task<int> SetDisplayDashboardAsync(SetDisplayDashboardReq req)
{
return Cache.SetDisplayDashboardAsync(req);
}
/// <summary>
/// 启用/禁用角色
/// </summary>
public Task SetEnabledAsync(SetRoleEnabledReq req)
public Task<int> SetEnabledAsync(SetRoleEnabledReq req)
{
return Cache.SetEnabledAsync(req);
}
/// <summary>
/// 设置是否忽略权限控制
/// </summary>
public Task<int> SetIgnorePermissionControlAsync(SetIgnorePermissionControlReq req)
{
return Cache.SetIgnorePermissionControlAsync(req);
}
}

View File

@ -96,6 +96,14 @@ public sealed class UserController(IUserCache cache, IConfigCache configCache)
return Cache.GetAsync(req);
}
/// <summary>
/// 获取当前用户应用配置
/// </summary>
public Task<GetSessionUserAppConfigRsp> GetSessionUserAppConfigAsync()
{
return Cache.GetSessionUserAppConfigAsync();
}
/// <summary>
/// 密码登录
/// </summary>
@ -168,7 +176,7 @@ public sealed class UserController(IUserCache cache, IConfigCache configCache)
/// </summary>
[AllowAnonymous]
[Transaction]
public Task<uint> ResetPasswordAsync(ResetPasswordReq req)
public Task<int> ResetPasswordAsync(ResetPasswordReq req)
{
return Cache.ResetPasswordAsync(req);
}
@ -195,7 +203,7 @@ public sealed class UserController(IUserCache cache, IConfigCache configCache)
/// 启用/禁用用户
/// </summary>
[Transaction]
public Task SetEnabledAsync(SetUserEnabledReq req)
public Task<int> SetEnabledAsync(SetUserEnabledReq req)
{
return Cache.SetEnabledAsync(req);
}
@ -213,11 +221,19 @@ public sealed class UserController(IUserCache cache, IConfigCache configCache)
/// 设置密码
/// </summary>
[Transaction]
public Task<uint> SetPasswordAsync(SetPasswordReq req)
public Task<int> SetPasswordAsync(SetPasswordReq req)
{
return Cache.SetPasswordAsync(req);
}
/// <summary>
/// 设置当前用户应用配置
/// </summary>
public Task<int> SetSessionUserAppConfigAsync(SetSessionUserAppConfigReq req)
{
return Cache.SetSessionUserAppConfigAsync(req);
}
/// <summary>
/// 当前用户信息
/// </summary>

View File

@ -68,7 +68,7 @@
}
.app-loading__title {
font-family: 'Arial', 'Microsoft YaHei', 'monospace';
font-family: 'Microsoft YaHei', sans-serif;
font-size: 24px;
color: #333;
margin-top: 30px;
@ -125,7 +125,7 @@
<script type="text/javascript">
// 黑夜模式
if (window.localStorage.getItem('APP_DARK')) {
if (window.localStorage.getItem('APP_SET_DARK')) {
document.documentElement.classList.add('dark')
}

View File

@ -43,8 +43,17 @@ export default {
},
},
async created() {
await this.$TOOL.data.downloadConfig()
//
if (this.$TOOL.data.get('APP_SET_DARK') || this.$CONFIG.APP_SET_DARK) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
//
const app_color = this.$TOOL.data.get('APP_COLOR') ?? this.$CONFIG.COLOR
const app_color = this.$TOOL.data.get('APP_SET_COLOR') || this.$CONFIG.APP_SET_COLOR
if (app_color) {
document.documentElement.style.setProperty('--el-color-primary', app_color)
for (let i = 1; i <= 9; i++) {
@ -56,28 +65,31 @@ export default {
}
//
const layout = this.$TOOL.data.get('LAYOUT') ?? this.$CONFIG.LAYOUT
const layout = this.$TOOL.data.get('APP_SET_LAYOUT') || this.$CONFIG.APP_SET_LAYOUT
if (layout) {
this.$store.commit('SET_layout', layout)
}
//
const menuIsCollapse = this.$TOOL.data.get('MENU_IS_COLLAPSE') ?? this.$CONFIG.MENU_IS_COLLAPSE
const menuIsCollapse = this.$TOOL.data.get('APP_SET_MENU_IS_COLLAPSE') || this.$CONFIG.APP_SET_MENU_IS_COLLAPSE
if (menuIsCollapse !== this.$store.state.global.menuIsCollapse) {
this.$store.commit('TOGGLE_menuIsCollapse')
}
//
const layoutTags = this.$TOOL.data.get('LAYOUT_TAGS') ?? this.$CONFIG.LAYOUT_TAGS
const layoutTags = this.$TOOL.data.get('APP_SET_MULTI_TAGS') || this.$CONFIG.APP_SET_MULTI_TAGS
if (layoutTags !== this.$store.state.global.layoutTags) {
this.$store.commit('TOGGLE_layoutTags')
}
//
const menuUniqueOpened = this.$TOOL.data.get('MENU_UNIQUE_OPENED') ?? this.$CONFIG.MENU_UNIQUE_OPENED
if (menuUniqueOpened !== this.$CONFIG.MENU_UNIQUE_OPENED) {
this.$CONFIG.MENU_UNIQUE_OPENED = menuUniqueOpened
const menuUniqueOpened = this.$TOOL.data.get('APP_SET_MENU_UNIQUE_OPENED') || this.$CONFIG.APP_SET_MENU_UNIQUE_OPENED
if (menuUniqueOpened !== this.$CONFIG.APP_SET_MENU_UNIQUE_OPENED) {
this.$CONFIG.APP_SET_MENU_UNIQUE_OPENED = menuUniqueOpened
}
//
this.$i18n.locale = this.$TOOL.data.get('APP_SET_LANG') || this.$CONFIG.APP_SET_LANG
},
}
</script>

View File

@ -103,4 +103,15 @@ export default {
return await http.post(this.url, data, config)
},
},
/**
* 设置配置启用状态
*/
setEnabled: {
url: `${config.API_URL}/api/sys/config/set.enabled`,
name: `设置配置启用状态`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
}

View File

@ -93,6 +93,17 @@ export default {
},
},
/**
* 设置是否显示仪表板
*/
setDisplayDashboard: {
url: `${config.API_URL}/api/sys/role/set.display.dashboard`,
name: `设置是否显示仪表板`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 启用/禁用角色
*/
@ -103,4 +114,15 @@ export default {
return await http.post(this.url, data, config)
},
},
/**
* 设置是否忽略权限控制
*/
setIgnorePermissionControl: {
url: `${config.API_URL}/api/sys/role/set.ignore.permission.control`,
name: `设置是否忽略权限控制`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
}

View File

@ -82,6 +82,17 @@ export default {
},
},
/**
* 获取当前用户应用配置
*/
getSessionUserAppConfig: {
url: `${config.API_URL}/api/sys/user/get.session.user.app.config`,
name: `获取当前用户应用配置`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 密码登录
*/
@ -214,6 +225,17 @@ export default {
},
},
/**
* 设置当前用户应用配置
*/
setSessionUserAppConfig: {
url: `${config.API_URL}/api/sys/user/set.session.user.app.config`,
name: `设置当前用户应用配置`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 当前用户信息
*/

View File

@ -1,25 +0,0 @@
<template>
<el-button @click="add" icon="el-icon-plus" type="primary"></el-button>
</template>
<style scoped></style>
<script>
export default {
emits: [],
props: { vue: { type: Object }, data: { type: Object } },
data() {
return {}
},
mounted() {},
created() {},
components: {},
computed: {},
methods: {
//
async add() {
this.vue.dialog.save = true
await this.vue.$nextTick()
this.vue.$refs.saveDialog.open('add', this.data)
},
},
}
</script>

View File

@ -1,9 +1,9 @@
<template>
<el-table-column :label="label" :prop="prop" sortable="custom">
<template #default="scope">
<template #default="{ row }">
<div class="avatar">
<el-avatar :src="getAvatar(scope, prop)" size="small"></el-avatar>
<span>{{ tool.getNestedProperty(scope.row, prop) }}</span>
<el-avatar :src="getAvatar(row, prop)" size="small"></el-avatar>
<span>{{ tool.getNestedProperty(row, prop) }}</span>
</div>
</template>
</el-table-column>
@ -36,8 +36,8 @@ export default {
},
methods: {
//
getAvatar(scope, prop) {
return scope.row.avatar ? scope.row.avatar : this.$CONFIG.DEFAULT_AVATAR(tool.getNestedProperty(scope.row, prop))
getAvatar(row, prop) {
return row.avatar ? row.avatar : this.$CONFIG.DEFAULT_AVATAR(tool.getNestedProperty(row, prop))
},
},
}

View File

@ -1,33 +1,33 @@
<template>
<el-table-column v-bind="$attrs">
<template #default="scope">
<el-text @click="click(scope.row)" style="cursor: pointer" tag="ins">
{{ tool.getNestedProperty(scope.row, $attrs.prop) }}
</el-text>
<template #default="{ row }">
<p>{{ row.id }}</p>
<p v-if="showTime" class="time">{{ row.createdTime }}</p>
<slot :data="row"></slot>
</template>
</el-table-column>
</template>
<script>
import tool from '@/utils/tool'
export default {
emits: ['click'],
props: {},
emits: [],
props: {
showTime: {
type: Boolean,
default: true,
},
},
data() {
return {}
},
mounted() {},
created() {},
components: {},
computed: {
tool() {
return tool
},
},
methods: {
async click(row) {
this.$emit('click', row)
},
},
computed: {},
methods: {},
}
</script>
<style scoped></style>
<style scoped>
.time {
color: var(--el-text-color-secondary);
}
</style>

View File

@ -2,6 +2,7 @@
<el-table-column v-bind:="$attrs">
<template #default="{ row }">
<na-indicator :data="row" :options="options" :prop="$attrs.prop" />
<slot :data="row" name="info"></slot>
</template>
</el-table-column>
</template>

View File

@ -1,31 +1,23 @@
<template>
<el-table-column align="right">
<template #default="scope">
<template #default="{ row }">
<el-button-group>
<template v-for="(item, i) in buttons?.filter((x) => !x.condition || x.condition(scope))" :key="i">
<template v-for="(item, i) in buttons?.filter((x) => !x.condition || x.condition(row))" :key="i">
<el-popconfirm
v-if="item.confirm"
:title="this.$t(`确定 {title}`, { title: item.title })"
@confirm="item.click(scope.row, vue)"
@confirm="item.click(row, vue)"
width="20rem">
<template #reference>
<el-button :icon="item.icon" :title="item.title" :type="item.type" @click.native.stop size="small"></el-button>
</template>
</el-popconfirm>
<el-button v-else :icon="item.icon" :title="item.title" @click="item.click(scope.row, vue)" size="small"
>{{ item.title }}
</el-button>
<el-button v-else :icon="item.icon" :title="item.title" @click="item.click(row, vue)" size="small">{{ item.title }} </el-button>
</template>
</el-button-group>
</template>
</el-table-column>
</template>
<style scoped>
.avatar {
display: flex;
gap: 0.5rem;
}
</style>
<script>
import naColOperation from '@/config/naColOperation'
@ -48,4 +40,5 @@ export default {
computed: {},
methods: {},
}
</script>
</script>
<style scoped></style>

View File

@ -1,15 +1,16 @@
<template>
<el-table-column :label="label" :prop="`${prop}.${field}`">
<template #default="scope">
<template v-for="(item, i) in Array.isArray(scope.row[prop]) ? scope.row[prop] : [scope.row[prop]]" :key="i">
<el-tag v-if="item" @click="$emit('click', item)">
{{ item ? item[field] : '' }}
</el-tag>
</template>
<template #default="{ row }">
<div class="flex">
<template v-for="(item, i) in Array.isArray(row[prop]) ? row[prop] : [row[prop]]" :key="i">
<el-tag v-if="item" @click="$emit('click', item)">
{{ item ? item[field] : '' }}
</el-tag>
</template>
</div>
</template>
</el-table-column>
</template>
<style scoped></style>
<script>
export default {
emits: ['click'],
@ -27,4 +28,9 @@ export default {
computed: {},
methods: {},
}
</script>
</script>
<style scoped>
.el-tag {
cursor: pointer;
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<el-table-column :label="label" :prop="prop">
<template #default="scope">
{{ tool.dateFormat(scope.row[prop]) }}
<template #default="{ row }">
{{ tool.dateFormat(row[prop]) }}
</template>
</el-table-column>
</template>

View File

@ -1,24 +1,50 @@
<template>
<el-table-column v-bind="$attrs">
<template #default="scope">
<div @click="click(tool.getNestedProperty(scope.row, $attrs.prop))" class="avatar">
<el-avatar
v-if="tool.getNestedProperty(scope.row, $attrs.nestProp)"
:src="getAvatar(scope, $attrs.nestProp)"
size="small"></el-avatar>
<template #default="{ row }">
<div @click="click($TOOL.getNestedProperty(row, $attrs.prop))" class="avatar">
<el-avatar v-if="$TOOL.getNestedProperty(row, $attrs.nestProp)" :src="getAvatar(row, $attrs.nestProp)" size="small"></el-avatar>
<div>
<p>{{ tool.getNestedProperty(scope.row, $attrs.nestProp) }}</p>
<p v-if="$attrs.nestProp2">{{ tool.getNestedProperty(scope.row, $attrs.nestProp2) }}</p>
<p>{{ $TOOL.getNestedProperty(row, $attrs.nestProp) }}</p>
<p v-if="$attrs.nestProp2">{{ $TOOL.getNestedProperty(row, $attrs.nestProp2) }}</p>
</div>
</div>
<save-dialog v-if="dialog.save" @closed="dialog.save = false" ref="saveDialog"></save-dialog>
</template>
</el-table-column>
</template>
<script>
import saveDialog from '@/views/sys/user/save.vue'
export default {
components: { saveDialog },
computed: {},
created() {},
data() {
return {
dialog: { save: false },
}
},
emits: ['click'],
methods: {
async click(id) {
this.dialog.save = true
await this.$nextTick()
await this.$refs.saveDialog.open({ mode: 'view', row: { id: id } })
},
//
getAvatar(row, prop) {
return row.avatar ? row.avatar : this.$CONFIG.DEFAULT_AVATAR(this.$TOOL.getNestedProperty(row, prop))
},
},
mounted() {},
props: {},
watch: {},
}
</script>
<style lang="scss" scoped>
.avatar {
div:last-child {
line-height: 1rem;
line-height: 1.2rem;
p:last-child {
color: var(--el-color-info-light-3);
}
@ -29,37 +55,4 @@
display: flex;
gap: 0.5rem;
}
</style>
<script>
import saveDialog from '@/views/sys/user/save.vue'
import tool from '@/utils/tool'
export default {
emits: ['click'],
props: {},
data() {
return {
dialog: { save: false },
}
},
mounted() {},
created() {},
components: { saveDialog },
computed: {
tool() {
return tool
},
},
methods: {
async click(id) {
this.dialog.save = true
await this.$nextTick()
await this.$refs.saveDialog.open('view', { id: id })
},
//
getAvatar(scope, prop) {
return scope.row.avatar ? scope.row.avatar : this.$CONFIG.DEFAULT_AVATAR(tool.getNestedProperty(scope.row, prop))
},
},
}
</script>
</style>

View File

@ -36,7 +36,8 @@ export default {
},
mounted() {},
async created() {
this.options = (await this.$API.sys_dept.query.post()).data
const res = await this.$API.sys_dept.query.post()
this.options = res.data
},
components: {},
computed: {},

View File

@ -41,7 +41,8 @@ export default {
},
mounted() {},
async created() {
this.options = (await this.$API.sys_dic.queryCatalog.post()).data
const res = await this.$API.sys_dic.queryCatalog.post()
this.options = res.data
},
components: {},
computed: {},

View File

@ -82,13 +82,16 @@ export default {
async captchaSuccess(obj) {
this.sendDisabled = true
try {
await this.$API.sys_verifycode.sendVerifyCode.post({
const res = await this.$API.sys_verifycode.sendVerifyCode.post({
destDevice: this.form[Array.isArray(this.phoneField) ? this.phoneField[1] : this.phoneField],
type: 'login',
deviceType: 'mobile',
verifyCaptchaReq: obj,
})
this.$message.success(this.$t('发送成功'))
if (res.data?.code) {
this.$message.success(res.data.code.toString())
}
this.waitSecs = 60
const t = setInterval(() => {
this.waitSecs -= 1

View File

@ -1,6 +1,6 @@
<template>
<template v-for="(item, i) in options" :key="i">
<div v-if="tool.getNestedProperty(data, this.prop) === item.value">
<div v-if="this.$TOOL.getNestedProperty(data, this.prop)?.toLowerCase() === item.value?.toLowerCase()">
<sc-status-indicator
:pulse="item.pulse"
:style="item.type ? '' : `background: #${Math.abs(this.$TOOL.crypto.hashCode(item.value)).toString(16).substring(0, 6)}`"
@ -9,11 +9,8 @@
<slot :data="data" :text="item.text"></slot>
</div>
</template>
<slot :data="data" name="info"></slot>
</template>
<script>
import tool from '@/utils/tool'
export default {
emits: [],
props: {
@ -27,11 +24,7 @@ export default {
mounted() {},
created() {},
components: {},
computed: {
tool() {
return tool
},
},
computed: {},
methods: {},
}
</script>

View File

@ -66,7 +66,7 @@
<el-button @click="reset" icon="el-icon-refresh-left">{{ $t('重置') }}</el-button>
</template>
<v-ace-editor
:theme="this.$TOOL.data.get('APP_DARK') ? 'github_dark' : 'github'"
:theme="this.$TOOL.data.get('APP_SET_DARK') || this.$CONFIG.APP_SET_DARK ? 'github_dark' : 'github'"
:value="vkbeautify().json(vue.query, 2)"
lang="json"
style="height: 20rem; width: 100%" />
@ -323,6 +323,7 @@ export default {
casLoaded: false,
keepKeywords: null,
keepCreatedTime: null,
keepHttpStatusCode: null,
form: {
root: {},
filter: {},
@ -404,6 +405,9 @@ export default {
if (this.keepCreatedTime) {
this.form.dy.createdTime = this.keepCreatedTime
}
if (this.keepHttpStatusCode) {
this.form.dy.httpStatusCode = this.keepHttpStatusCode
}
this.$emit('reset')
this.search()
},

View File

@ -25,7 +25,7 @@ export default {
const contents = []
const msg = h('p', { style: 'width:230px;display:flex;justify-content:space-between' }, [
h('span', {}, this.$t('即将开始更新……')),
h('a', { style: 'color:#409eff', href: 'javascript:window.location.reload()' }, this.$t('立即更新')),
h('a', { style: 'color:#21A675', href: 'javascript:window.location.reload()' }, this.$t('立即更新')),
])
const task = h('p', { style: 'font-weight:bold' }, version)
const progress = h(
@ -37,7 +37,7 @@ export default {
style: {
width: '230px',
height: '6px',
'background-color': '#409eff',
'background-color': '#21A675',
'margin-top': '6px',
'border-radius': '6px',
},

View File

@ -1,5 +1,5 @@
const T = {
color: ['#409EFF', '#36CE9E', '#f56e6a', '#626c91', '#edb00d', '#909399'],
color: ['#21A675', '#36CE9E', '#f56e6a', '#626c91', '#edb00d', '#909399'],
grid: {
left: '3%',
right: '3%',

View File

@ -8,15 +8,13 @@
</el-tag>
</el-table-column>
<el-table-column :label="$t('列名')" prop="label">
<template #default="scope">
<el-tag :effect="scope.row.hide ? 'light' : 'dark'" :type="scope.row.hide ? 'info' : ''" disable-transitions round
>{{ scope.row.label }}
</el-tag>
<template #default="{ row }">
<el-tag :effect="row.hide ? 'light' : 'dark'" :type="row.hide ? 'info' : ''" disable-transitions round>{{ row.label }} </el-tag>
</template>
</el-table-column>
<el-table-column :label="$t('显示')" prop="hide" width="60">
<template #default="scope">
<el-switch v-model="scope.row.hide" :active-value="false" :inactive-value="true" size="small" />
<template #default="{ row }">
<el-switch v-model="row.hide" :active-value="false" :inactive-value="true" size="small" />
</template>
</el-table-column>
</el-table>

View File

@ -440,7 +440,7 @@ export default {
}
.sc-file-select__item__file .item-file.item-file-doc {
color: #409eff;
color: #21a675;
}
.sc-file-select__item__upload {

View File

@ -125,7 +125,7 @@ export default {
.sc-filter-my-list li:hover {
background: #ecf5ff;
color: #409eff;
color: #21a675;
}
.sc-filter-my-list li label {

View File

@ -53,7 +53,7 @@ export default {
.sc-page-header__icon span {
width: 2.5rem;
height: 2.5rem;
background: #409eff;
background: #21a675;
border-radius: 40%;
display: flex;
align-items: center;

View File

@ -76,6 +76,7 @@ export default {
}
.sc-statistic-content-value {
font-family: 'Lucida Console', 'Microsoft YaHei', monospace;
font-weight: bold;
}

View File

@ -43,11 +43,11 @@ export default {
files: {
doc: {
icon: 'sc-icon-file-word-2-fill',
color: '#409eff',
color: '#21A675',
},
docx: {
icon: 'sc-icon-file-word-2-fill',
color: '#409eff',
color: '#21A675',
},
xls: {
icon: 'sc-icon-file-excel-2-fill',

View File

@ -10,57 +10,51 @@ const DEFAULT_CONFIG = {
//首页地址
DASHBOARD_URL: '/home',
//版本号
APP_VER: '1.0.0',
//内核版本号
CORE_VER: '1.6.9',
//接口地址
API_URL: '',
//请求超时
TIMEOUT: 10000,
//TokenName
TOKEN_NAME: 'Authorization',
//Token前缀注意最后有个空格如不需要需设置空字符串
TOKEN_PREFIX: 'Bearer ',
//追加其他头
HEADERS: {},
//请求是否开启缓存
REQUEST_CACHE: false,
//布局 默认default | 通栏header | 经典menu | 功能坞dock
//dock将关闭标签和面包屑栏
LAYOUT: 'menu',
//菜单是否折叠
MENU_IS_COLLAPSE: false,
//菜单是否启用手风琴效果
MENU_UNIQUE_OPENED: false,
//是否开启多标签
LAYOUT_TAGS: true,
//语言
LANG: 'zh-cn',
//主题颜色
COLOR: '#21A675',
//是否加密localStorage, 为空不加密可填写AES(模式ECB,移位Pkcs7)加密
LS_ENCRYPTION: '',
//localStorageAES加密秘钥位数建议填写8的倍数
LS_ENCRYPTION_key: '2XNN4K8LC0ELVWN4',
//控制台首页默认布局
DEFAULT_GRID: {
//布局 默认default | 通栏header | 经典menu | 功能坞dock
//dock将关闭标签和面包屑栏
APP_SET_LAYOUT: 'menu',
//菜单是否折叠
APP_SET_MENU_IS_COLLAPSE: false,
//菜单是否启用手风琴效果
APP_SET_MENU_UNIQUE_OPENED: false,
//是否开启多标签
APP_SET_MULTI_TAGS: true,
//语言
APP_SET_LANG: 'zh-cn',
//自动退出
APP_SET_AUTO_EXIT: 0,
//深色模式
APP_SET_DARK: false,
//主题颜色
APP_SET_COLOR: '#21A675',
//控制台首页布局
APP_SET_HOME_GRID: {
//默认分栏数量和宽度 例如 [24] [18,6] [8,8,8] [6,12,6]
layout: [8, 8, 8, 12, 12, 12, 12],
//小组件分布com取值:views/home/components 文件名

View File

@ -3,21 +3,13 @@ export default {
{
icon: 'el-icon-view',
click: async (row, vue) => {
vue.loading = true
vue.dialog.save = true
await vue.$nextTick()
await vue.$refs.saveDialog.open('view', row)
vue.loading = false
vue.dialog.save = { row, mode: 'view' }
},
},
{
icon: 'el-icon-edit',
click: async (row, vue) => {
vue.loading = true
vue.dialog.save = true
await vue.$nextTick()
await vue.$refs.saveDialog.open('edit', row)
vue.loading = false
vue.dialog.save = { row, mode: 'edit' }
},
},
],

View File

@ -46,7 +46,6 @@ import scWaterMark from '@/components/scWaterMark'
// net-admin组件
import naArea from '@/components/naArea/index.vue'
import naButtonAdd from '@/components/naButtonAdd/index.vue'
import naButtonBulkDel from '@/components/naButtonBulkDel/index.vue'
import naColAvatar from '@/components/naColAvatar'
import naColId from '@/components/naColId/index.vue'
@ -87,7 +86,6 @@ export default {
// net-admin组件
app.component('naArea', naArea)
app.component('naButtonAdd', naButtonAdd)
app.component('naButtonBulkDel', naButtonBulkDel)
app.component('naColAvatar', naColAvatar)
app.component('naColId', naColId)

View File

@ -17,7 +17,7 @@
<el-menu
:default-active="$route.meta.active || $route.fullPath"
@select="select"
active-text-color="#409EFF"
active-text-color="#21A675"
background-color="#424c50"
router
text-color="#fff">

View File

@ -45,27 +45,25 @@
<el-button @click="refresh" circle icon="el-icon-refresh"></el-button>
</el-footer>
</el-container>
<save-dialog v-if="dialog.save" @closed="dialog.save = false" ref="saveDialog"></save-dialog>
<save-dialog v-if="dialog.save" @closed="dialog.save = null" @mounted="$refs.saveDialog.open(dialog.save)" ref="saveDialog"></save-dialog>
</template>
<script>
import saveDialog from '@/views/sys/job/save.vue'
import { defineAsyncComponent } from 'vue'
const saveDialog = defineAsyncComponent(() => import('@/views/sys/job/all/save.vue'))
export default {
components: {
saveDialog,
},
data() {
return {
dialog: {
save: false,
},
dialog: {},
loading: false,
jobs: [],
}
},
mounted() {
this.getData()
},
inject: ['reload'],
methods: {
async getData() {
this.loading = true
@ -77,11 +75,14 @@ export default {
this.getData()
},
async view(job) {
this.dialog.save = true
await this.$nextTick()
await this.$refs.saveDialog.open('view', { id: job.id })
this.dialog.save = { mode: 'view', row: { id: job.id }, tabId: 'record' }
},
},
mounted() {
this.getData()
},
props: [],
watch: {},
}
</script>

View File

@ -78,37 +78,31 @@
</template>
<script>
import search from './search.vue'
import tasks from './tasks.vue'
import message from '@/views/profile/message/components/list.vue'
import { defineAsyncComponent } from 'vue'
import avatar from '../../utils/avatar'
const search = defineAsyncComponent(() => import('./search.vue'))
const tasks = defineAsyncComponent(() => import('./tasks.vue'))
const message = defineAsyncComponent(() => import('@/views/profile/message/components/list.vue'))
export default {
computed: {
avatar() {
return avatar
},
},
components: {
search,
tasks,
message,
},
watch: {
'config.dark'(val) {
if (val) {
document.documentElement.classList.add('dark')
this.$TOOL.data.set('APP_DARK', val)
} else {
document.documentElement.classList.remove('dark')
this.$TOOL.data.remove('APP_DARK')
}
computed: {
avatar() {
return avatar
},
},
async created() {
this.user = this.$GLOBAL.user
const res = await this.$API.sys_sitemsg.unreadCount.post()
this.unreadCnt = res.data
},
data() {
return {
config: {
dark: this.$TOOL.data.get('APP_DARK') || false,
dark: this.$TOOL.data.get('APP_SET_DARK') || this.$CONFIG.APP_SET_DARK,
},
user: {},
userName: '',
@ -119,12 +113,21 @@ export default {
unreadCnt: 0,
}
},
async created() {
this.user = this.$GLOBAL.user
const res = await this.$API.sys_sitemsg.unreadCount.post()
this.unreadCnt = res.data
},
methods: {
clearData(fullClear) {
const loading = this.$loading()
this.$TOOL.cookie.clear()
if (fullClear) {
this.$TOOL.data.clear()
} else {
this.$TOOL.data.clearAppSet()
}
this.$router.replace({ path: '/guest/login' })
setTimeout(() => {
loading.close()
location.reload()
}, 1000)
},
configDark() {
this.config.dark = !this.config.dark
},
@ -133,7 +136,7 @@ export default {
this.msg = false
},
//
handleUser(command) {
async handleUser(command) {
if (command === 'uc') {
this.$router.push({ path: '/profile/account' })
}
@ -141,36 +144,22 @@ export default {
this.$router.push({ path: '/cmd' })
}
if (command === 'clearCache') {
this.$confirm(this.$t('清除缓存会将系统初始化,包括登录状态、主题、语言设置等,是否继续?'), this.$t('提示'), {
type: 'info',
})
.then(() => {
const loading = this.$loading()
this.$TOOL.data.clear()
this.$TOOL.cookie.clear()
this.$router.replace({ path: '/guest/login' })
setTimeout(() => {
loading.close()
location.reload()
}, 1000)
})
.catch(() => {
//
try {
await this.$confirm(this.$t('清除缓存会将系统初始化,包括登录状态、主题、语言设置等,是否继续?'), this.$t('提示'), {
type: 'info',
})
this.clearData(true)
} catch {}
}
if (command === 'outLogin') {
this.$confirm(this.$t('确认是否退出当前用户?'), this.$t('提示'), {
type: 'warning',
confirmButtonText: this.$t('退出'),
confirmButtonClass: 'el-button--danger',
})
.then(() => {
this.$TOOL.cookie.clear()
this.$router.replace({ path: '/guest/login' })
})
.catch(() => {
//退
try {
await this.$confirm(this.$t('确认是否退出当前用户?'), this.$t('提示'), {
type: 'warning',
confirmButtonText: this.$t('退出'),
confirmButtonClass: 'el-button--danger',
})
this.clearData(false)
} catch {}
}
},
//
@ -190,6 +179,18 @@ export default {
this.tasksVisible = true
},
},
props: [],
watch: {
'config.dark'(val) {
if (val) {
document.documentElement.classList.add('dark')
this.$TOOL.data.set('APP_SET_DARK', val)
} else {
document.documentElement.classList.remove('dark')
this.$TOOL.data.remove('APP_SET_DARK')
}
},
},
}
</script>

View File

@ -29,7 +29,7 @@
</div>
<div class="adminui-side-scroll">
<el-scrollbar>
<el-menu :collapse="menuIsCollapse" :default-active="active" :unique-opened="$CONFIG.MENU_UNIQUE_OPENED" router>
<el-menu :collapse="menuIsCollapse" :default-active="active" :unique-opened="$CONFIG.APP_SET_MENU_UNIQUE_OPENED" router>
<NavMenu :navMenus="nextMenu"></NavMenu>
</el-menu>
</el-scrollbar>
@ -74,7 +74,7 @@
<div v-if="!ismobile" :class="menuIsCollapse ? 'aminui-side isCollapse' : 'aminui-side'">
<div class="adminui-side-scroll">
<el-scrollbar>
<el-menu :collapse="menuIsCollapse" :default-active="active" :unique-opened="$CONFIG.MENU_UNIQUE_OPENED" router>
<el-menu :collapse="menuIsCollapse" :default-active="active" :unique-opened="$CONFIG.APP_SET_MENU_UNIQUE_OPENED" router>
<NavMenu :navMenus="menu"></NavMenu>
</el-menu>
</el-scrollbar>
@ -172,7 +172,7 @@
</div>
<div class="adminui-side-scroll">
<el-scrollbar>
<el-menu :collapse="menuIsCollapse" :default-active="active" :unique-opened="$CONFIG.MENU_UNIQUE_OPENED" router>
<el-menu :collapse="menuIsCollapse" :default-active="active" :unique-opened="$CONFIG.APP_SET_MENU_UNIQUE_OPENED" router>
<NavMenu :navMenus="nextMenu"></NavMenu>
</el-menu>
</el-scrollbar>

View File

@ -2,7 +2,7 @@ export default {
render() {},
data() {
return {
logoutCount: this.$TOOL.data.get('AUTO_EXIT'),
logoutCount: this.$TOOL.data.get('APP_SET_AUTO_EXIT') || this.$CONFIG.APP_SET_AUTO_EXIT,
}
},
mounted() {

View File

@ -19,7 +19,7 @@ const messages = {
}
const i18n = createI18n({
locale: tool.data.get('APP_LANG') || sysConfig.LANG,
locale: tool.data.get('APP_SET_LANG') || sysConfig.APP_SET_LANG,
fallbackLocale: 'zh-cn',
globalInjection: true,
messages,

View File

@ -74,7 +74,7 @@ export default {
启用状态: 'Enabled status',
响应状态码: 'Response status code',
响应码: 'Response code',
唯一编码: 'Unique code',
唯一编码: 'Unique ID',
图标名称: 'Icon name',
图标选择器: 'Icon selector',
地区: 'Region',
@ -448,4 +448,28 @@ export default {
返回首页: 'Return to homepage',
重新登录: 'Re-login',
返回上一页: 'Return to previous page',
批量操作: 'Batch operation',
启用用户: 'Enable user',
禁用用户: 'Disable user',
'确定要 {operator} 选中的 {count} 项吗?': 'Are you sure you want to {operator} the selected {count} items?',
'操作成功 {count}/{total} 项': '{count}/{total} items operation successful',
启用角色: 'Enable role',
禁用角色: 'Disable role',
启用部门: 'Enable department',
禁用部门: 'Disable department',
启用配置: 'Enable configuration',
禁用配置: 'Disable configuration',
启用作业: 'Enable job',
禁用作业: 'Disable job',
重置为默认值: 'Reset to default value',
'确定将当前主题设置恢复默认值吗?': 'Are you sure you want to reset the current theme settings to default?',
'确定将当前设置恢复默认值吗?': 'Are you sure you want to reset the current settings to default?',
作业信息: 'Job information',
查看作业记录: 'View job records',
异常作业: 'Abnormal jobs',
所有作业: 'All jobs',
用户列表: 'User list',
: 'Yes',
: 'No',
手机: 'Mobile',
}

View File

@ -447,4 +447,27 @@ export default {
返回首页: '返回首页',
重新登录: '重新登录',
返回上一页: '返回上一页',
批量操作: '批量操作',
启用用户: '启用用户',
禁用用户: '禁用用户',
'确定要 {operator} 选中的 {count} 项吗?': '确定要 {operator} 选中的 {count} 项吗?',
'操作成功 {count}/{total} 项': '操作成功 {count}/{total} 项',
启用角色: '启用角色',
禁用角色: '禁用角色',
启用部门: '启用部门',
禁用部门: '禁用部门',
启用配置: '启用配置',
禁用配置: '禁用配置',
启用作业: '启用作业',
禁用作业: '禁用作业',
'确定将当前主题设置恢复默认值吗?': '确定将当前主题设置恢复默认值吗?',
'确定将当前设置恢复默认值吗?': '确定将当前设置恢复默认值吗?',
作业信息: '作业信息',
查看作业记录: '查看作业记录',
异常作业: '异常作业',
所有作业: '所有作业',
用户列表: '用户列表',
: '是',
: '否',
手机: '手机',
}

View File

@ -5,11 +5,11 @@ export default {
//移动端布局
ismobile: false,
//布局
layout: config.LAYOUT,
layout: config.APP_SET_LAYOUT,
//菜单是否折叠 toggle
menuIsCollapse: config.MENU_IS_COLLAPSE,
menuIsCollapse: config.APP_SET_MENU_IS_COLLAPSE,
//多标签栏
layoutTags: config.LAYOUT_TAGS,
layoutTags: config.APP_SET_MULTI_TAGS,
//主题
theme: config.THEME,
},

View File

@ -6,7 +6,7 @@ html {
height: 100%;
background-color: #f6f8f9;
font-size: var(--el-font-size-base);
font-family: 'Arial', 'Microsoft YaHei', 'monospace';
font-family: 'Microsoft YaHei', sans-serif;
}
a {
@ -92,7 +92,7 @@ textarea {
bottom: 10rem;
right: 0;
z-index: 100;
background: #409eff;
background: #21a675;
display: flex;
flex-direction: column;
align-items: center;
@ -235,7 +235,7 @@ textarea {
}
.aminui-side-split li.active {
background: #409eff;
background: #21a675;
}
.adminui-side-split-scroll::-webkit-scrollbar-thumb {
@ -410,7 +410,7 @@ textarea {
}
.adminui-tags li.active {
background: #409eff;
background: #21a675;
}
.adminui-tags li.active a {

View File

@ -1,7 +1,7 @@
/* 覆盖element-plus样式 */
:root {
--el-color-primary: #409eff;
--el-color-primary: #21a675;
--el-color-primary-light-1: #53a7ff;
--el-color-primary-light-2: #66b1ff;
--el-color-primary-light-3: #79bbff;
@ -44,6 +44,7 @@
.el-date-editor {
--el-date-editor-daterange-width: 20rem;
--el-date-editor-datetimerange-width: 30rem;
}
.el-menu {
@ -167,7 +168,7 @@
.el-table {
td {
font-family: 'Lucida Console', 'Microsoft YaHei', 'monospace';
font-family: 'Lucida Console', 'Microsoft YaHei', monospace;
}
.el-link:after {

View File

@ -26,7 +26,6 @@ export default {
text.setAttribute('text-anchor', 'middle')
text.setAttribute('font-size', '16')
text.setAttribute('font-weight', '900')
text.setAttribute('font-family', 'monospace')
// IE/Edge don't support alignment-baseline
// @see https://msdn.microsoft.com/en-us/library/gg558060(v=vs.85).aspx

View File

@ -12,7 +12,35 @@ const tool = {}
/* localStorage */
tool.data = {
set(key, data, datetime = 0) {
configJson: null,
async uploadConfig() {
try {
const json = JSON.stringify(Object.entries(localStorage).filter((x) => x[0].indexOf('APP_SET_') === 0))
if (this.configJson !== json) {
this.configJson = json
const userApi = await import('@/api/sys/user')
await userApi.default.setSessionUserAppConfig.post({
appConfig: this.configJson,
})
}
} catch {}
},
clearAppSet() {
Object.entries(localStorage)
.filter((x) => x[0].indexOf('APP_SET_') === 0)
.map((x) => localStorage.removeItem(x[0]))
},
async downloadConfig() {
try {
const userApi = await import('@/api/sys/user')
const res = await userApi.default.getSessionUserAppConfig.post({})
this.clearAppSet()
for (const item of JSON.parse(res.data.appConfig)) {
localStorage.setItem(item[0], item[1])
}
} catch {}
},
async set(key, data, datetime = 0) {
//加密
if (sysConfig.LS_ENCRYPTION === 'AES') {
data = tool.crypto.AES.encrypt(JSON.stringify(data), sysConfig.LS_ENCRYPTION_key)
@ -21,7 +49,9 @@ tool.data = {
content: data,
datetime: parseInt(datetime) === 0 ? 0 : new Date().getTime() + parseInt(datetime) * 1000,
}
return localStorage.setItem(key, JSON.stringify(cacheValue))
const ret = localStorage.setItem(key, JSON.stringify(cacheValue))
await this.uploadConfig()
return ret
},
get(key) {
try {
@ -43,11 +73,15 @@ tool.data = {
return null
}
},
remove(key) {
return localStorage.removeItem(key)
async remove(key) {
const ret = localStorage.removeItem(key)
await this.uploadConfig()
return ret
},
clear() {
return localStorage.clear()
async clear() {
const ret = localStorage.clear()
await this.uploadConfig()
return ret
},
}

View File

@ -49,7 +49,7 @@ export default {
},
},
mounted() {
this.autoLogin = this.$TOOL.data.get('AUTO_LOGIN')
this.autoLogin = this.$TOOL.data.get('AUTO_LOGIN') || false
},
methods: {
async login() {

View File

@ -69,8 +69,8 @@ export default {
data() {
return {
config: {
lang: this.$TOOL.data.get('APP_LANG') || this.$CONFIG.LANG,
dark: this.$TOOL.data.get('APP_DARK') || false,
lang: this.$TOOL.data.get('APP_SET_LANG') || this.$CONFIG.APP_SET_LANG,
dark: this.$TOOL.data.get('APP_SET_DARK') || this.$CONFIG.APP_SET_DARK,
},
lang: [
{
@ -88,15 +88,15 @@ export default {
'config.dark'(val) {
if (val) {
document.documentElement.classList.add('dark')
this.$TOOL.data.set('APP_DARK', val)
this.$TOOL.data.set('APP_SET_DARK', val)
} else {
document.documentElement.classList.remove('dark')
this.$TOOL.data.remove('APP_DARK')
this.$TOOL.data.remove('APP_SET_DARK')
}
},
'config.lang'(val) {
this.$i18n.locale = val
this.$TOOL.data.set('APP_LANG', val)
this.$TOOL.data.set('APP_SET_LANG', val)
},
},
created: function () {
@ -105,7 +105,7 @@ export default {
this.$TOOL.data.remove('MENU')
this.$TOOL.data.remove('PERMISSIONS')
this.$TOOL.data.remove('DASHBOARD_GRID')
this.$TOOL.data.remove('grid')
this.$TOOL.data.remove('APP_SET_HOME_GRID')
this.$store.commit('clearViewTags')
this.$store.commit('clearKeepLive')
this.$store.commit('clearIframeList')

View File

@ -1,7 +1,8 @@
<template>
<el-main>
<widgets v-if="dashboard" @on-mounted="onMounted"></widgets>
<work v-else @on-mounted="onMounted"></work>
<div v-if="loading" v-loading="true" style="height: 100%"></div>
<el-main v-else>
<widgets v-if="dashboard"></widgets>
<work v-else></work>
</el-main>
</template>
@ -18,20 +19,19 @@ export default {
},
data() {
return {
pageLoading: true,
loading: true,
dashboard: false,
}
},
created() {
async created() {
//
await this.$TOOL.data.downloadConfig()
this.dashboard = this.$GLOBAL.user.roles.findIndex((x) => x.displayDashboard) >= 0
this.loading = false
},
mounted() {},
methods: {
onMounted() {
this.pageLoading = false
},
},
methods: {},
}
</script>
<style></style>
<style scoped></style>

View File

@ -1,7 +1,7 @@
<template>
<el-card v-loading="loading" class="main" shadow="never">
<div class="wrap">
<img alt="" src="@/assets/img/logo.png" />
<img alt="" src="@/assets/img/logo.png" width="200" />
<h2>{{ packageJson.name }}</h2>
<p>{{ ver }}</p>
<el-link href="https://github.com/nsnail/NetAdmin" target="_blank">{{ $t('喜欢就点个 Star⭐ 吧!') }}</el-link>

View File

@ -146,7 +146,7 @@ export default {
customizing: false,
allComps: allComps,
selectLayout: [],
defaultGrid: this.$CONFIG.DEFAULT_GRID,
defaultGrid: this.$CONFIG.APP_SET_HOME_GRID,
grid: [],
}
},
@ -225,14 +225,14 @@ export default {
save() {
this.customizing = false
this.$refs.widgets.style.removeProperty('transform')
this.$TOOL.data.set('grid', this.grid)
this.$TOOL.data.set('APP_SET_HOME_GRID', this.grid)
},
//
backDefault() {
this.customizing = false
this.$refs.widgets.style.removeProperty('transform')
this.grid = JSON.parse(JSON.stringify(this.defaultGrid))
this.$TOOL.data.remove('grid')
this.$TOOL.data.remove('APP_SET_HOME_GRID')
},
//
close() {
@ -241,7 +241,7 @@ export default {
this.loadGrid()
},
loadGrid() {
this.grid = this.$TOOL.data.get('grid') || JSON.parse(JSON.stringify(this.defaultGrid))
this.grid = this.$TOOL.data.get('APP_SET_HOME_GRID') || JSON.parse(JSON.stringify(this.defaultGrid))
},
},
}
@ -259,6 +259,7 @@ export default {
flex: 1;
overflow: auto;
overflow-x: hidden;
font-family: 'Lucida Console', 'Microsoft YaHei', monospace;
}
.widgets-aside {

View File

@ -84,7 +84,7 @@ export default {
},
getMods() {
//
this.myModsName = this.$TOOL.data.get('MY_MODS') || []
this.myModsName = this.$TOOL.data.get('APP_SET_MY_MODS') || []
this.filterMenu(this.$GLOBAL.menu)
this.myMods = this.mods.filter((item) => {
return this.myModsName.includes(item.name)
@ -110,7 +110,7 @@ export default {
},
saveMods() {
this.$TOOL.data.set(
'MY_MODS',
'APP_SET_MY_MODS',
this.myMods.map((v) => v.name),
)
this.$message.success(this.$t('设置常用成功'))
@ -178,8 +178,8 @@ export default {
.modItem-add:hover,
.modItem-add:hover i {
border-color: #409eff;
color: #409eff !important;
border-color: #21a675;
color: #21a675 !important;
}
.setMods {

View File

@ -34,18 +34,42 @@
</el-form>
</el-card>
<set-mobile-dialog v-if="dialog.setMobile" @closed="dialog.setMobile = false" @success="setSuccess" ref="setMobileDialog"></set-mobile-dialog>
<set-password-dialog v-if="dialog.setPassword" @closed="dialog.setPassword = false" ref="setPasswordDialog"></set-password-dialog>
<set-email-dialog v-if="dialog.setEmail" @closed="dialog.setEmail = false" @success="setSuccess" ref="setEmailDialog"></set-email-dialog>
<set-mobile-dialog
v-if="dialog.setMobile"
@closed="dialog.setMobile = null"
@mounted="$refs.setMobileDialog.open(dialog.setMobile)"
@success="setSuccess"
ref="setMobileDialog"></set-mobile-dialog>
<set-password-dialog
v-if="dialog.setPassword"
@closed="dialog.setPassword = null"
@mounted="$refs.setPasswordDialog.open(dialog.setPassword)"
ref="setPasswordDialog"></set-password-dialog>
<set-email-dialog
v-if="dialog.setEmail"
@closed="dialog.setEmail = null"
@mounted="$refs.setEmailDialog.open(dialog.setEmail)"
@success="setSuccess"
ref="setEmailDialog"></set-email-dialog>
</template>
<script>
import setMobileDialog from '@/views/profile/account/set-mobile.vue'
import setPasswordDialog from '@/views/profile/account/set-password.vue'
import setEmailDialog from '@/views/profile/account/set-email.vue'
import { defineAsyncComponent } from 'vue'
const setMobileDialog = defineAsyncComponent(() => import('@/views/profile/account/set-mobile.vue'))
const setPasswordDialog = defineAsyncComponent(() => import('@/views/profile/account/set-password.vue'))
const setEmailDialog = defineAsyncComponent(() => import('@/views/profile/account/set-email.vue'))
export default {
components: { setMobileDialog, setPasswordDialog, setEmailDialog },
created() {
this.form = this.$GLOBAL.user
},
data() {
return {
dialog: {},
form: {},
}
},
methods: {
updateUser(res) {
try {
@ -59,34 +83,17 @@ export default {
this.form = this.$GLOBAL.user = data
},
async setPasswordClick() {
this.dialog.setPassword = true
await this.$nextTick()
this.$refs.setPasswordDialog.open()
this.dialog.setPassword = {}
},
async setEmailClick() {
this.dialog.setEmail = true
await this.$nextTick()
this.$refs.setEmailDialog.open()
this.dialog.setEmail = {}
},
async setMobileClick() {
this.dialog.setMobile = true
await this.$nextTick()
this.$refs.setMobileDialog.open(this.form.mobile ? 'edit' : 'add')
this.dialog.setMobile = { mode: this.form.mobile ? 'edit' : 'add' }
},
},
created() {
this.form = this.$GLOBAL.user
},
data() {
return {
dialog: {
setPassword: false,
setMobile: false,
},
form: {},
}
},
props: [],
watch: {},
}
</script>

View File

@ -29,7 +29,7 @@
<template #footer>
<el-button @click="visible = false">{{ $t('取消') }}</el-button>
<el-button :loading="loading" @click="submit" type="primary">{{ $t('保存') }}</el-button>
<el-button :disabled="loading" :loading="loading" @click="submit" type="primary">{{ $t('保存') }}</el-button>
</template>
</el-dialog>
</template>
@ -40,17 +40,42 @@ import phoneConfig from '@/config/naFormPhone'
import emailConfig from '@/config/naFormEmail'
export default {
created() {},
components: {
naFormPhone,
},
created() {},
data() {
return {
//
form: {
verifySmsCodeReq: {},
},
loading: false,
//
rules: {
verifySmsCodeReq: {
destDevice: phoneConfig.mobile(this),
code: phoneConfig.code(this),
},
destDevice: [emailConfig.email(this)],
code: emailConfig.code(),
},
visible: false,
}
},
emits: ['success', 'closed', 'mounted'],
methods: {
//
open() {
this.visible = true
return this
},
//
async submit() {
if (!(await this.$refs.form.validate().catch(() => {}))) {
const valid = await this.$refs.form.validate().catch(() => {})
if (!valid) {
return false
}
@ -64,23 +89,8 @@ export default {
this.loading = false
},
},
data() {
return {
visible: false,
loading: false,
form: {
verifySmsCodeReq: {},
},
rules: {
verifySmsCodeReq: {
destDevice: phoneConfig.mobile(this),
code: phoneConfig.code(this),
},
destDevice: [emailConfig.email(this)],
code: emailConfig.code(),
},
}
mounted() {
this.$emit('mounted')
},
}
</script>

View File

@ -1,5 +1,5 @@
<template>
<el-dialog v-model="visible" :title="`${titleMap[mode]}${form?.id ?? '...'}`" :width="800" @closed="$emit('closed')" destroy-on-close>
<el-dialog v-model="visible" :title="`${titleMap[mode]}`" :width="800" @closed="$emit('closed')" destroy-on-close>
<el-form :model="form" :rules="rules" label-position="top" ref="form">
<el-row class="items-center justify-content-center">
<el-col v-if="mode === 'edit'" :lg="10">
@ -34,7 +34,7 @@
<template #footer>
<el-button @click="visible = false">{{ $t('取消') }}</el-button>
<el-button :loading="loading" @click="submit" type="primary">{{ $t('保存') }}</el-button>
<el-button :disabled="loading" :loading="loading" @click="submit" type="primary">{{ $t('保存') }}</el-button>
</template>
</el-dialog>
</template>
@ -44,18 +44,50 @@ import naFormPhone from '@/components/naFormPhone/index.vue'
import phoneConfig from '@/config/naFormPhone'
export default {
created() {},
components: {
naFormPhone,
},
created() {},
data() {
return {
//
form: {
newverifySmsCodeReq: {},
originverifySmsCodeReq: {},
},
loading: false,
mode: 'add',
//
rules: {
originverifySmsCodeReq: {
destDevice: phoneConfig.mobile(this),
code: phoneConfig.code(this),
},
newverifySmsCodeReq: {
destDevice: [phoneConfig.mobile(this), phoneConfig.mobileNoUsed(this, () => this.$GLOBAL.user.id)],
code: phoneConfig.code(this),
},
},
titleMap: {
add: this.$t('绑定手机'),
edit: this.$t('更换手机'),
},
visible: false,
}
},
emits: ['success', 'closed', 'mounted'],
methods: {
open(mode = 'add') {
this.mode = mode
//
open(data) {
this.mode = data.mode
this.visible = true
return this
},
//
async submit() {
if (!(await this.$refs.form.validate().catch(() => {}))) {
const valid = await this.$refs.form.validate().catch(() => {})
if (!valid) {
return false
}
@ -69,30 +101,8 @@ export default {
this.loading = false
},
},
data() {
return {
mode: 'add',
titleMap: {
add: this.$t('绑定手机'),
edit: this.$t('更换手机'),
},
visible: false,
loading: false,
form: {
newverifySmsCodeReq: {},
originverifySmsCodeReq: {},
},
rules: {
originverifySmsCodeReq: {
destDevice: phoneConfig.mobile(this),
code: phoneConfig.code(this),
},
newverifySmsCodeReq: {
destDevice: [phoneConfig.mobile(this), phoneConfig.mobileNoUsed(this, () => this.$GLOBAL.user.id)],
code: phoneConfig.code(this),
},
},
}
mounted() {
this.$emit('mounted')
},
}
</script>

View File

@ -32,9 +32,10 @@
type="password"></el-input>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="visible = false">{{ $t('取消') }}</el-button>
<el-button :loading="loading" @click="submit" type="primary">{{ $t('保存') }}</el-button>
<el-button :disabled="loading" :loading="loading" @click="submit" type="primary">{{ $t('保存') }}</el-button>
</template>
</el-dialog>
</template>
@ -44,43 +45,51 @@ import scPasswordStrength from '@/components/scPasswordStrength/index.vue'
import naFormPassword from '@/config/naFormPassword'
export default {
created() {},
components: {
scPasswordStrength,
},
created() {},
data() {
return {
form: {},
loading: false,
//
rules: {
oldPassword: naFormPassword.passwordText(this),
newPassword: naFormPassword.passwordText(this),
confirmNewPassword: naFormPassword.passwordText2(() => this.form.newPassword),
},
visible: false,
}
},
emits: ['success', 'closed', 'mounted'],
methods: {
//
open() {
this.visible = true
return this
},
//
async submit() {
if (!(await this.$refs.form.validate().catch(() => {}))) {
const valid = await this.$refs.form.validate().catch(() => {})
if (!valid) {
return false
}
this.loading = true
try {
const res = await this.$API.sys_user.setPassword.post(this.form)
this.$emit('success', res.data, this.mode)
this.$emit('success', res.data)
this.visible = false
this.$message.success(this.$t('操作成功'))
} catch {
//
}
} catch {}
this.loading = false
},
},
data() {
return {
visible: false,
loading: false,
form: {},
rules: {
oldPassword: naFormPassword.passwordText(this),
newPassword: naFormPassword.passwordText(this),
confirmNewPassword: naFormPassword.passwordText2(() => this.form.newPassword),
},
}
mounted() {
this.$emit('mounted')
},
}
</script>
</script>
<style scoped></style>

View File

@ -1,6 +1,6 @@
<template>
<el-card :header="$t('主题样式')" shadow="never">
<el-form class="mt-4" label-width="15rem">
<el-form class="mt-4" label-width="10rem">
<el-form-item :label="$t('黑夜模式')">
<el-switch v-model="config.dark" active-icon="el-icon-moon" inactive-icon="el-icon-sunny" inline-prompt />
</el-form-item>
@ -24,10 +24,17 @@
<el-form-item :label="$t('标签栏')">
<el-switch v-model="config.layoutTags"></el-switch>
</el-form-item>
<el-form-item>
<el-popconfirm :title="$t('确定将当前主题设置恢复默认值吗?')" @confirm="themeReset" width="20rem">
<template #reference>
<el-button>{{ $t('重置为默认值') }}</el-button>
</template>
</el-popconfirm>
</el-form-item>
</el-form>
</el-card>
<el-card :header="$t('个人设置')" class="mt-4" shadow="never">
<el-form class="mt-4" label-width="15rem">
<el-form class="mt-4" label-width="10rem">
<el-form-item :label="$t('界面语言')">
<el-select v-model="config.lang">
<el-option :label="$t('简体中文')" value="zh-cn" />
@ -52,6 +59,13 @@
<el-option :label="$t('60分钟')" :value="60" />
</el-select>
</el-form-item>
<el-form-item>
<el-popconfirm :title="$t('确定将当前设置恢复默认值吗?')" @confirm="personalReset" width="20rem">
<template #reference>
<el-button>{{ $t('重置为默认值') }}</el-button>
</template>
</el-popconfirm>
</el-form-item>
</el-form>
</el-card>
</template>
@ -62,16 +76,16 @@ import colorTool from '@/utils/color'
export default {
data() {
return {
colorList: ['#409EFF', '#009688', '#536dfe', '#ff5c93', '#c62f2f', '#fd726d'],
colorList: ['#21A675', '#009688', '#536dfe', '#ff5c93', '#c62f2f', '#fd726d'],
config: {
layout: this.$TOOL.data.get('LAYOUT') ?? this.$CONFIG.LAYOUT,
menuIsCollapse: this.$TOOL.data.get('MENU_IS_COLLAPSE') ?? this.$CONFIG.MENU_IS_COLLAPSE,
menuUniqueOpened: this.$TOOL.data.get('MENU_UNIQUE_OPENED') ?? this.$CONFIG.MENU_UNIQUE_OPENED,
layoutTags: this.$TOOL.data.get('LAYOUT_TAGS') ?? this.$CONFIG.LAYOUT_TAGS,
lang: this.$TOOL.data.get('APP_LANG') ?? this.$CONFIG.LANG,
dark: this.$TOOL.data.get('APP_DARK') ?? false,
colorPrimary: this.$TOOL.data.get('APP_COLOR') ?? this.$CONFIG.COLOR ?? '#409EFF',
autoExit: this.$TOOL.data.get('AUTO_EXIT') ?? 0,
layout: this.$TOOL.data.get('APP_SET_LAYOUT') || this.$CONFIG.APP_SET_LAYOUT,
menuIsCollapse: this.$TOOL.data.get('APP_SET_MENU_IS_COLLAPSE') || this.$CONFIG.APP_SET_MENU_IS_COLLAPSE,
menuUniqueOpened: this.$TOOL.data.get('APP_SET_MENU_UNIQUE_OPENED') || this.$CONFIG.APP_SET_MENU_UNIQUE_OPENED,
layoutTags: this.$TOOL.data.get('APP_SET_MULTI_TAGS') || this.$CONFIG.APP_SET_MULTI_TAGS,
lang: this.$TOOL.data.get('APP_SET_LANG') || this.$CONFIG.APP_SET_LANG,
dark: this.$TOOL.data.get('APP_SET_DARK') || this.$CONFIG.APP_SET_DARK,
colorPrimary: this.$TOOL.data.get('APP_SET_COLOR') || this.$CONFIG.APP_SET_COLOR || '#17ABE3',
autoExit: this.$TOOL.data.get('APP_SET_AUTO_EXIT') || this.$CONFIG.APP_SET_AUTO_EXIT,
},
}
},
@ -79,51 +93,51 @@ export default {
'config.dark'(val) {
if (val) {
document.documentElement.classList.add('dark')
this.$TOOL.data.set('APP_DARK', val)
this.$TOOL.data.set('APP_SET_DARK', val)
} else {
document.documentElement.classList.remove('dark')
this.$TOOL.data.remove('APP_DARK')
this.$TOOL.data.remove('APP_SET_DARK')
}
},
'config.layout'(val) {
if (val) {
this.$TOOL.data.set('LAYOUT', val)
this.$TOOL.data.set('APP_SET_LAYOUT', val)
this.$store.commit('SET_layout', val)
} else {
this.$TOOL.data.remove('LAYOUT')
this.$TOOL.data.remove('APP_SET_LAYOUT')
}
},
'config.menuIsCollapse'(val) {
if (typeof val === 'boolean') {
this.$TOOL.data.set('MENU_IS_COLLAPSE', val)
this.$TOOL.data.set('APP_SET_MENU_IS_COLLAPSE', val)
this.$store.commit('TOGGLE_menuIsCollapse')
} else {
this.$TOOL.data.remove('MENU_IS_COLLAPSE')
this.$TOOL.data.remove('APP_SET_MENU_IS_COLLAPSE')
}
},
'config.layoutTags'(val) {
if (typeof val === 'boolean') {
this.$TOOL.data.set('LAYOUT_TAGS', val)
this.$TOOL.data.set('APP_SET_MULTI_TAGS', val)
this.$store.commit('TOGGLE_layoutTags')
} else {
this.$TOOL.data.remove('LAYOUT_TAGS')
this.$TOOL.data.remove('APP_SET_MULTI_TAGS')
}
},
'config.menuUniqueOpened'(val) {
if (typeof val === 'boolean') {
this.$TOOL.data.set('MENU_UNIQUE_OPENED', val)
this.$TOOL.data.set('APP_SET_MENU_UNIQUE_OPENED', val)
} else {
this.$TOOL.data.remove('MENU_UNIQUE_OPENED')
this.$TOOL.data.remove('APP_SET_MENU_UNIQUE_OPENED')
}
},
'config.lang'(val) {
this.$i18n.locale = val
this.$TOOL.data.set('APP_LANG', val)
this.$TOOL.data.set('APP_SET_LANG', val)
},
'config.colorPrimary'(val) {
if (!val) {
val = '#409EFF'
this.config.colorPrimary = '#409EFF'
val = '#21A675'
this.config.colorPrimary = '#21A675'
}
document.documentElement.style.setProperty('--el-color-primary', val)
for (let i = 1; i <= 9; i++) {
@ -132,16 +146,38 @@ export default {
for (let i = 1; i <= 9; i++) {
document.documentElement.style.setProperty(`--el-color-primary-dark-${i}`, colorTool.darken(val, i / 10))
}
this.$TOOL.data.set('APP_COLOR', val)
this.$TOOL.data.set('APP_SET_COLOR', val)
},
'config.autoExit'(val) {
if (val === 0) {
this.$TOOL.data.remove('AUTO_EXIT')
this.$TOOL.data.remove('APP_SET_AUTO_EXIT')
} else {
this.$TOOL.data.set('AUTO_EXIT', val)
this.$TOOL.data.set('APP_SET_AUTO_EXIT', val)
}
},
},
methods: {
async themeReset() {
this.$loading()
this.$message.success(this.$t('操作成功'))
localStorage.removeItem('APP_SET_LAYOUT')
localStorage.removeItem('APP_SET_MENU_IS_COLLAPSE')
localStorage.removeItem('APP_SET_MENU_UNIQUE_OPENED')
localStorage.removeItem('APP_SET_MULTI_TAGS')
localStorage.removeItem('APP_SET_DARK')
localStorage.removeItem('APP_SET_COLOR')
await this.$TOOL.data.uploadConfig()
window.location.reload()
},
async personalReset() {
this.$loading()
this.$message.success(this.$t('操作成功'))
localStorage.removeItem('APP_SET_AUTO_EXIT')
localStorage.removeItem('APP_SET_LANG')
await this.$TOOL.data.uploadConfig()
window.location.reload()
},
},
}
</script>

View File

@ -66,25 +66,22 @@
</template>
<script>
import scStatistic from '@/components/scStatistic'
import naInfo from '@/components/naInfo/index.vue'
import tool from '@/utils/tool'
export default {
components: {
scStatistic,
naInfo,
},
data() {
return {
dialog: {
info: false,
},
query: {
filter: {
dbIndex: 1,
},
},
dialog: {
info: false,
},
statistics: {
keyspaceHits: 0,
keyspaceMisses: 0,
@ -95,21 +92,11 @@ export default {
},
}
},
mounted() {
this.cacheStatistics()
},
watch: {
'query.filter.dbIndex': {
handler() {
this.$refs.table.upData()
},
},
},
methods: {
async rowClick(row) {
this.dialog.info = true
await this.$nextTick()
this.$refs.info.open(tool.sortProperties(row), `缓存详情`)
this.$refs.info.open(this.$TOOL.sortProperties(row), this.$t('缓存详情'))
},
async cacheStatistics() {
try {
@ -122,6 +109,16 @@ export default {
}
},
},
mounted() {
this.cacheStatistics()
},
watch: {
'query.filter.dbIndex': {
handler() {
this.$refs.table.upData()
},
},
},
}
</script>

View File

@ -13,8 +13,8 @@
],
},
]"
:label-width="10"
@on-change="filterChange"
label-width="10"
ref="selectFilter"></sc-select-filter>
</el-header>
<el-header>
@ -27,13 +27,26 @@
ref="search" />
</div>
<div class="right-panel">
<na-button-add :vue="this" />
<el-button @click="this.dialog.save = { mode: 'add' }" icon="el-icon-plus" type="primary"></el-button>
<na-button-bulk-del :api="$API.sys_config.bulkDelete" :vue="this" />
<el-dropdown v-show="this.selection.length > 0">
<el-button type="primary">
{{ $t('批量操作') }}
<el-icon>
<el-icon-arrow-down />
</el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="setEnabled(true)">{{ $t('启用配置') }}</el-dropdown-item>
<el-dropdown-item @click="setEnabled(false)">{{ $t('禁用配置') }}</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</el-header>
<el-main class="nopadding">
<sc-table
v-loading="loading"
:apiObj="$API.sys_config.pagedQuery"
:context-menus="['id', 'userRegisterConfirm', 'enabled', 'createdTime']"
:params="query"
@ -47,22 +60,21 @@
row-key="id"
stripe>
<el-table-column type="selection" />
<el-table-column :label="$t('配置编号')" align="center" prop="id" width="170" />
<na-col-id :label="$t('配置编号')" prop="id" width="170" />
<el-table-column :label="$t('用户注册')" align="center">
<el-table-column :label="$t('默认部门')" align="center" prop="userRegisterDept.name" width="150" />
<el-table-column :label="$t('默认角色')" align="center" prop="userRegisterRole.name" width="150" />
<el-table-column :label="$t('人工审核')" align="center" prop="userRegisterConfirm" width="120">
<template #default="scope">
<el-switch v-model="scope.row.userRegisterConfirm" @change="changeSwitch($event, scope.row)"></el-switch>
<template #default="{ row }">
<el-switch v-model="row.userRegisterConfirm" @change="changeSwitch($event, row)"></el-switch>
</template>
</el-table-column>
</el-table-column>
<el-table-column :label="$t('启用')" align="center" prop="enabled" width="100">
<template #default="scope">
<el-switch v-model="scope.row.enabled" @change="changeSwitch($event, scope.row)"></el-switch>
<template #default="{ row }">
<el-switch v-model="row.enabled" @change="changeSwitch($event, row)"></el-switch>
</template>
</el-table-column>
<el-table-column :label="$t('创建时间')" align="center" prop="createdTime" width="170" />
<na-col-operation
:buttons="
naColOperation.buttons.concat({
@ -73,25 +85,25 @@
type: 'danger',
})
"
:vue="this"
width="170" />
:vue="this" />
</sc-table>
</el-main>
</el-container>
<save-dialog
v-if="dialog.save"
@closed="dialog.save = false"
@success="(data, mode) => table.handleUpdate($refs.table, data, mode)"
@closed="dialog.save = null"
@mounted="$refs.saveDialog.open(dialog.save)"
@success="(data, mode) => $refs.table.upData()"
ref="saveDialog"></save-dialog>
</template>
<script>
import saveDialog from './save'
import naColOperation from '@/config/naColOperation'
import { defineAsyncComponent } from 'vue'
import table from '@/config/table'
import tool from '@/utils/tool'
import naColOperation from '@/config/naColOperation'
const saveDialog = defineAsyncComponent(() => import('./save.vue'))
export default {
components: {
saveDialog,
@ -104,13 +116,14 @@ export default {
return table
},
},
created() {},
created() {
if (this.keywords) {
this.query.keywords = this.keywords
}
},
data() {
return {
dialog: {
info: false,
save: false,
},
dialog: {},
loading: false,
query: {
dynamicFilter: {
@ -123,6 +136,33 @@ export default {
},
inject: ['reload'],
methods: {
async setEnabled(enabled) {
let loading
try {
await this.$confirm(
this.$t('确定要 {operator} 选中的 {count} 项吗?', {
operator: enabled ? this.$t('启用') : this.$t('禁用'),
count: this.selection.length,
}),
this.$t('提示'),
{
type: 'warning',
},
)
loading = this.$loading()
const res = await Promise.all(this.selection.map((x) => this.$API.sys_config.setEnabled.post(Object.assign(x, { enabled: enabled }))))
this.$message.success(
this.$t('操作成功 {count}/{total} 项', {
count: res.map((x) => x.data ?? 0).reduce((a, b) => a + b, 0),
total: this.selection.length,
}),
)
} catch {
//
}
this.$refs.table.refresh()
loading?.close()
},
async changeSwitch(event, row) {
try {
await this.$API.sys_config.edit.post(row)
@ -135,10 +175,9 @@ export default {
filterChange(data) {
Object.entries(data).forEach(([key, value]) => {
this.$refs.search.form.dy[key] = value === 'true' ? true : value === 'false' ? false : value
this.$refs.search.search()
})
this.$refs.search.search()
},
async rowDel(row) {
try {
const res = await this.$API.sys_config.delete.post({ id: row.id })
@ -168,7 +207,14 @@ export default {
this.$refs.table.upData()
},
},
mounted() {},
mounted() {
if (this.keywords) {
this.$refs.search.form.root.keywords = this.keywords
this.$refs.search.keepKeywords = this.keywords
}
},
props: ['keywords'],
watch: {},
}
</script>

View File

@ -35,7 +35,7 @@
<el-tab-pane v-if="mode === 'view'" :label="$t('原始数据')">
<json-viewer
:expand-depth="5"
:theme="this.$TOOL.data.get('APP_DARK') ? 'dark' : 'light'"
:theme="this.$TOOL.data.get('APP_SET_DARK') || this.$CONFIG.APP_SET_DARK ? 'dark' : 'light'"
:value="form"
copyable
expanded
@ -45,7 +45,7 @@
</div>
<template #footer>
<el-button @click="visible = false">{{ $t('取消') }}</el-button>
<el-button v-if="mode !== 'view'" :loading="loading" @click="submit" type="primary">{{ $t('保存') }}</el-button>
<el-button v-if="mode !== 'view'" :disabled="loading" :loading="loading" @click="submit" type="primary">{{ $t('保存') }}</el-button>
</template>
</sc-dialog>
</template>
@ -53,37 +53,37 @@
<script>
export default {
components: {},
emits: ['success', 'closed'],
data() {
return {
//
form: {
enabled: true,
},
loading: false,
mode: 'add',
//
rules: {
userRegisterDeptId: [{ required: true, message: '请选择默认部门' }],
userRegisterRoleId: [{ required: true, message: '请选择默认角色' }],
},
titleMap: {
add: this.$t('新增配置'),
edit: this.$t('编辑配置'),
view: this.$t('查看配置'),
},
visible: false,
loading: false,
//
form: {
enabled: true,
},
//
rules: {
userRegisterDeptId: [{ required: true, message: '请选择默认部门' }],
userRegisterRoleId: [{ required: true, message: '请选择默认角色' }],
},
}
},
mounted() {},
emits: ['success', 'closed', 'mounted'],
methods: {
//
async open(mode = 'add', data) {
async open(data) {
this.visible = true
this.loading = true
this.mode = mode
if (data) {
Object.assign(this.form, (await this.$API.sys_config.get.post({ id: data.id })).data)
this.mode = data.mode
if (data.row?.id) {
const res = await this.$API.sys_config.get.post({ id: data.row.id })
Object.assign(this.form, res.data)
}
this.loading = false
return this
@ -95,20 +95,21 @@ export default {
if (!valid) {
return false
}
this.loading = true
const method = this.mode === 'add' ? this.$API.sys_config.create : this.$API.sys_config.edit
try {
const method = this.mode === 'add' ? this.$API.sys_config.create : this.$API.sys_config.edit
const res = await method.post(this.form)
this.$emit('success', res.data, this.mode)
this.visible = false
this.$message.success(this.$t('操作成功'))
} catch {
///
}
} catch {}
this.loading = false
},
},
mounted() {
this.$emit('mounted')
},
}
</script>

Some files were not shown because too many files have changed in this diff Show More