😎1、调整登录逻辑 2、清理无效代码

This commit is contained in:
zuohuaijun 2025-01-15 01:03:08 +08:00
parent d6b96fb751
commit dc551bc59c
23 changed files with 116 additions and 735 deletions

View File

@ -99,7 +99,7 @@ public class AppAuthService : IDynamicApiController, ITransient
// 登录成功则清空密码错误次数
_sysCacheService.Remove(keyPasswordErrorTimes);
return await CreateToken(user, input.LoginMode);
return await CreateToken(user);
}
/// <summary>
@ -148,7 +148,7 @@ public class AppAuthService : IDynamicApiController, ITransient
var user = await _sysUserRep.AsQueryable().Includes(u => u.SysOrg).ClearFilter().FirstAsync(u => u.Phone.Equals(input.Phone));
_ = user ?? throw Oops.Oh(ErrorCodeEnum.D0009);
return await CreateToken(user, input.LoginMode);
return await CreateToken(user);
}
/// <summary>
@ -158,7 +158,7 @@ public class AppAuthService : IDynamicApiController, ITransient
/// <param name="loginMode"></param>
/// <returns></returns>
[NonAction]
public virtual async Task<LoginOutput> CreateToken(SysUser user, LoginModeEnum loginMode)
public virtual async Task<LoginOutput> CreateToken(SysUser user, LoginModeEnum loginMode = LoginModeEnum.APP)
{
// 单用户登录
await _sysOnlineUserService.SingleLogin(user.Id, loginMode);

View File

@ -68,13 +68,16 @@ public class DictAttribute : ValidationAttribute, ITransient
return ValidationResult.Success;
}
// 先尝试从 ValidationContext 的依赖注入容器中拿服务,再从全局的 App 容器中获取
if (validationContext.GetService(typeof(SysDictDataService)) is not SysDictDataService sysDictDataService)
sysDictDataService = App.GetRequiredService<SysDictDataService>();
// 先尝试从 ValidationContext 的依赖注入容器中拿服务,拿不到或类型不匹配时,再从全局的 App 容器中获取
if (validationContext.GetService(typeof(SysDictTypeService)) is not SysDictTypeService sysDictDataService)
sysDictDataService = App.GetRequiredService<SysDictTypeService>();
// 获取字典值列表
var dictDataList = sysDictDataService.GetDataList(DictTypeCode).GetAwaiter().GetResult();
var dictDataList = sysDictDataService.GetDataList(new GetDataDictTypeInput { Code = DictTypeCode }).GetAwaiter().GetResult();
// 使用 HashSet 来提高查找效率
var dictHash = new HashSet<string>(dictDataList.Select(u => u.Code));
if (!dictHash.Contains(valueAsString)) return new ValidationResult($"提示:{ErrorMessage}|字典【{DictTypeCode}】不包含【{valueAsString}】!");
return ValidationResult.Success;

View File

@ -26,11 +26,6 @@ public class SqlSugarConst
/// </summary>
public const string PrimaryKey = "Id";
/// <summary>
/// 默认应用Id
/// </summary>
public const long DefaultAppId = 1300000000001;
/// <summary>
/// 默认租户Id
/// </summary>

View File

@ -1,82 +0,0 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core;
/// <summary>
/// 系统应用表
/// </summary>
[SysTable]
[SugarTable(null, "系统应用表")]
public partial class SysApp : EntityBase
{
/// <summary>
/// 名称
/// </summary>
[SugarColumn(ColumnDescription = "名称", Length = 32), Required, MaxLength(32)]
public virtual string Name { get; set; }
/// <summary>
/// 图标
/// </summary>
[SugarColumn(ColumnDescription = "图标", Length = 256), Required, MaxLength(256)]
public virtual string? Logo { get; set; }
/// <summary>
/// 标题
/// </summary>
[SugarColumn(ColumnDescription = "标题", Length = 32), MaxLength(32)]
public string Title { get; set; }
/// <summary>
/// 副标题
/// </summary>
[SugarColumn(ColumnDescription = "副标题", Length = 32), MaxLength(32)]
public string ViceTitle { get; set; }
/// <summary>
/// 副描述
/// </summary>
[SugarColumn(ColumnDescription = "副描述", Length = 64), MaxLength(64)]
public string? ViceDesc { get; set; }
/// <summary>
/// 水印
/// </summary>
[SugarColumn(ColumnDescription = "水印", Length = 32), MaxLength(32)]
public string? Watermark { get; set; }
/// <summary>
/// 版权信息
/// </summary>
[SugarColumn(ColumnDescription = "版权信息", Length = 64), MaxLength(64)]
public string? Copyright { get; set; }
/// <summary>
/// ICP备案号
/// </summary>
[SugarColumn(ColumnDescription = "ICP备案号", Length = 32), MaxLength(32)]
public string? Icp { get; set; }
/// <summary>
/// 排序
/// </summary>
[IgnoreUpdateSeedColumn]
[SugarColumn(ColumnDescription = "排序")]
public int OrderNo { get; set; } = 100;
/// <summary>
/// 备注
/// </summary>
[SugarColumn(ColumnDescription = "备注", Length = 256), MaxLength(256)]
public string? Remark { get; set; }
/// <summary>
/// 应用租户
/// </summary>
[Navigate(NavigateType.OneToMany, nameof(SysTenant.AppId))]
public List<SysTenant> TenantList { get; set; }
}

View File

@ -1,41 +0,0 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core;
/// <summary>
/// 系统应用菜单表
/// </summary>
[SugarTable(null, "系统应用菜单表")]
[SysTable]
public partial class SysAppMenu : EntityBaseId
{
/// <summary>
/// 应用Id
/// </summary>
[SugarColumn(ColumnDescription = "应用Id")]
public long AppId { get; set; }
/// <summary>
/// 应用
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
[Navigate(NavigateType.OneToOne, nameof(AppId))]
public SysApp SysApp { get; set; }
/// <summary>
/// 菜单Id
/// </summary>
[SugarColumn(ColumnDescription = "菜单Id")]
public long MenuId { get; set; }
/// <summary>
/// 菜单
/// </summary>
[Navigate(NavigateType.OneToOne, nameof(MenuId))]
public SysMenu SysMenu { get; set; }
}

View File

@ -14,23 +14,9 @@ namespace Admin.NET.Core;
public partial class SysTenant : EntityBase
{
/// <summary>
/// 应用Id
/// 租管用户Id
/// </summary>
[SugarColumn(ColumnDescription = "应用Id", DefaultValue = "1300000000001")]
public long? AppId { get; set; }
/// <summary>
/// 应用
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
[Navigate(NavigateType.OneToOne, nameof(AppId))]
public SysApp SysApp { get; set; }
/// <summary>
/// 用户Id
/// </summary>
[SugarColumn(ColumnDescription = "用户Id")]
[SugarColumn(ColumnDescription = "租管用户Id")]
public long UserId { get; set; }
/// <summary>

View File

@ -21,18 +21,18 @@ public enum GenderEnum
/// <summary>
/// 男性
/// </summary>
[Description("男性")]
[Description("男性"), Theme("success")]
Male = 1,
/// <summary>
/// 女性
/// </summary>
[Description("女性")]
[Description("女性"), Theme("danger")]
Female = 2,
/// <summary>
/// 未说明的性别
/// </summary>
[Description("未说明的性别"), Theme("info")]
[Description("未说明的性别"), Theme("warning")]
Unspecified = 9
}

View File

@ -58,5 +58,17 @@ public enum UserEventTypeEnum
/// 解除登录锁定
/// </summary>
[Description("解除登录锁定")]
UnlockLogin = 7
UnlockLogin = 7,
/// <summary>
/// 系统登录
/// </summary>
[Description("系统登录")]
Login = 8,
/// <summary>
/// 系统退出
/// </summary>
[Description("系统退出")]
Logout = 9,
}

View File

@ -1,23 +0,0 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core;
/// <summary>
/// 系统应用菜单表种子数据
/// </summary>
public class SysAppMenuSeedData : ISqlSugarEntitySeedData<SysAppMenu>
{
/// <summary>
/// 种子数据
/// </summary>
/// <returns></returns>
public IEnumerable<SysAppMenu> HasData()
{
long id = SqlSugarConst.DefaultAppId;
return new SysMenuSeedData().HasData().Select(u => new SysAppMenu { Id = id++, AppId = SqlSugarConst.DefaultAppId, MenuId = u.Id });
}
}

View File

@ -1,25 +0,0 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core;
/// <summary>
/// 系统应用表种子数据
/// </summary>
public class SysAppSeedData : ISqlSugarEntitySeedData<SysApp>
{
/// <summary>
/// 种子数据
/// </summary>
/// <returns></returns>
public IEnumerable<SysApp> HasData()
{
return
[
new SysApp{ Id=SqlSugarConst.DefaultAppId, Name="默认应用", Logo="/upload/logo.png", Title="Admin.NET", ViceTitle="Admin.NET", ViceDesc="站在巨人肩膀上的 .NET 通用权限开发框架", Watermark="Admin.NET", Copyright="Copyright \u00a9 2021-present Admin.NET All rights reserved.", Icp="省ICP备12345678号", Remark="系统默认应用", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
];
}
}

View File

@ -21,7 +21,19 @@ public class SysTenantSeedData : ISqlSugarEntitySeedData<SysTenant>
return
[
new SysTenant{ Id=SqlSugarConst.DefaultTenantId, AppId=SqlSugarConst.DefaultTenantId, OrgId=SqlSugarConst.DefaultTenantId, UserId=1300000000111, Host="gitee.com", TenantType=TenantTypeEnum.Id, DbType=defaultDbConfig.DbType, Connection=defaultDbConfig.ConnectionString, ConfigId=SqlSugarConst.MainConfigId, Remark="系统默认", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysTenant
{
Id=SqlSugarConst.DefaultTenantId,
OrgId=SqlSugarConst.DefaultTenantId,
UserId=1300000000111,
Host="gitee.com",
TenantType=TenantTypeEnum.Id,
DbType=defaultDbConfig.DbType,
Connection=defaultDbConfig.ConnectionString,
ConfigId=SqlSugarConst.MainConfigId,
Remark="系统默认",
CreateTime=DateTime.Parse("2022-02-10 00:00:00")
},
];
}
}

View File

@ -23,11 +23,11 @@ public class SysUserSeedData : ISqlSugarEntitySeedData<SysUser>
return
[
new SysUser{ Id=1300000000101, Account="superadmin", Password=encryptPassword, NickName="超级管理员", RealName="超级管理员", Phone="18012345678", Birthday=DateTime.Parse("2000-01-01"), Sex=GenderEnum.Male, AccountType=AccountTypeEnum.SuperAdmin, Remark="超级管理员", CreateTime=DateTime.Parse("2022-02-10 00:00:00"), TenantId=SqlSugarConst.DefaultTenantId },
new SysUser{ Id=1300000000111, Account="admin", Password=encryptPassword, NickName="系统管理员", RealName="系统管理员", Phone="18012345678", Birthday=DateTime.Parse("2000-01-01"), Sex=GenderEnum.Male, AccountType=AccountTypeEnum.SysAdmin, Remark="系统管理员", CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrgId=SqlSugarConst.DefaultTenantId, PosId=1300000000102, TenantId=SqlSugarConst.DefaultTenantId },
new SysUser{ Id=1300000000112, Account="user1", Password=encryptPassword, NickName="部门主管", RealName="部门主管", Phone="18012345678", Birthday=DateTime.Parse("2000-01-01"), Sex=GenderEnum.Female, AccountType=AccountTypeEnum.NormalUser, Remark="部门主管", CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrgId=SqlSugarConst.DefaultTenantId + 1, PosId=1300000000108, TenantId=SqlSugarConst.DefaultTenantId },
new SysUser{ Id=1300000000113, Account="user2", Password=encryptPassword, NickName="部门职员", RealName="部门职员", Phone="18012345678", Birthday=DateTime.Parse("2000-01-01"), Sex=GenderEnum.Female, AccountType=AccountTypeEnum.NormalUser, Remark="部门职员", CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrgId=SqlSugarConst.DefaultTenantId + 2, PosId=1300000000110, TenantId=SqlSugarConst.DefaultTenantId },
new SysUser{ Id=1300000000114, Account="user3", Password=encryptPassword, NickName="普通用户", RealName="普通用户", Phone="18012345678", Birthday=DateTime.Parse("2000-01-01"), Sex=GenderEnum.Female, AccountType=AccountTypeEnum.NormalUser, Remark="普通用户", CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrgId=SqlSugarConst.DefaultTenantId + 3, PosId=1300000000115, TenantId=SqlSugarConst.DefaultTenantId },
new SysUser{ Id=1300000000115, Account="user4", Password=encryptPassword, NickName="其他", RealName="其他", Phone="18012345678", Birthday=DateTime.Parse("2000-01-01"), Sex=GenderEnum.Female, AccountType=AccountTypeEnum.Member, Remark="会员", CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrgId=SqlSugarConst.DefaultTenantId + 4, PosId=1300000000116, TenantId=SqlSugarConst.DefaultTenantId },
new SysUser{ Id=1300000000111, Account="admin", Password=encryptPassword, NickName="系统管理员", RealName="系统管理员", Phone="18012345677", Birthday=DateTime.Parse("2000-01-01"), Sex=GenderEnum.Male, AccountType=AccountTypeEnum.SysAdmin, Remark="系统管理员", CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrgId=SqlSugarConst.DefaultTenantId, PosId=1300000000102, TenantId=SqlSugarConst.DefaultTenantId },
new SysUser{ Id=1300000000112, Account="user1", Password=encryptPassword, NickName="部门主管", RealName="部门主管", Phone="18012345676", Birthday=DateTime.Parse("2000-01-01"), Sex=GenderEnum.Female, AccountType=AccountTypeEnum.NormalUser, Remark="部门主管", CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrgId=SqlSugarConst.DefaultTenantId + 1, PosId=1300000000108, TenantId=SqlSugarConst.DefaultTenantId },
new SysUser{ Id=1300000000113, Account="user2", Password=encryptPassword, NickName="部门职员", RealName="部门职员", Phone="18012345675", Birthday=DateTime.Parse("2000-01-01"), Sex=GenderEnum.Female, AccountType=AccountTypeEnum.NormalUser, Remark="部门职员", CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrgId=SqlSugarConst.DefaultTenantId + 2, PosId=1300000000110, TenantId=SqlSugarConst.DefaultTenantId },
new SysUser{ Id=1300000000114, Account="user3", Password=encryptPassword, NickName="普通用户", RealName="普通用户", Phone="18012345674", Birthday=DateTime.Parse("2000-01-01"), Sex=GenderEnum.Female, AccountType=AccountTypeEnum.NormalUser, Remark="普通用户", CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrgId=SqlSugarConst.DefaultTenantId + 3, PosId=1300000000115, TenantId=SqlSugarConst.DefaultTenantId },
new SysUser{ Id=1300000000115, Account="user4", Password=encryptPassword, NickName="其他", RealName="其他", Phone="18012345673", Birthday=DateTime.Parse("2000-01-01"), Sex=GenderEnum.Female, AccountType=AccountTypeEnum.Member, Remark="会员", CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrgId=SqlSugarConst.DefaultTenantId + 4, PosId=1300000000116, TenantId=SqlSugarConst.DefaultTenantId },
];
}
}

View File

@ -1,173 +0,0 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Service;
/// <summary>
/// 应用基础输入参数
/// </summary>
public class SysAppInput
{
/// <summary>
/// 主键Id
/// </summary>
public virtual long? Id { get; set; }
/// <summary>
/// 名称
/// </summary>
[Required(ErrorMessage = "名称不能为空")]
public virtual string Name { get; set; }
/// <summary>
/// 标题
/// </summary>
[Required(ErrorMessage = "标题不能为空")]
public virtual string Title { get; set; }
/// <summary>
/// 副标题
/// </summary>
[Required(ErrorMessage = "副标题不能为空")]
public virtual string ViceTitle { get; set; }
/// <summary>
/// 副描述
/// </summary>
public virtual string? ViceDesc { get; set; }
/// <summary>
/// 水印
/// </summary>
public virtual string? Watermark { get; set; }
/// <summary>
/// 版权信息
/// </summary>
public virtual string? Copyright { get; set; }
/// <summary>
/// ICP备案号
/// </summary>
public virtual string? Icp { get; set; }
/// <summary>
/// 排序
/// </summary>
[Required(ErrorMessage = "排序不能为空")]
public virtual int? OrderNo { get; set; }
/// <summary>
/// 备注
/// </summary>
public virtual string? Remark { get; set; }
/// <summary>
/// 图标
/// </summary>
public virtual string? Logo { get; set; }
}
/// <summary>
/// 增加应用输入参数
/// </summary>
public class AddSysAppInput
{
/// <summary>
/// 图标
/// </summary>
[Required(ErrorMessage = "Logo不能为空")]
public string Logo { get; set; }
/// <summary>
/// 名称
/// </summary>
[Required(ErrorMessage = "名称不能为空")]
public string Name { get; set; }
/// <summary>
/// 标题
/// </summary>
[Required(ErrorMessage = "标题不能为空")]
public string Title { get; set; }
/// <summary>
/// 副标题
/// </summary>
[Required(ErrorMessage = "副标题不能为空")]
public string ViceTitle { get; set; }
/// <summary>
/// 副描述
/// </summary>
[Required(ErrorMessage = "副描述不能为空")]
public string ViceDesc { get; set; }
/// <summary>
/// 水印
/// </summary>
[Required(ErrorMessage = "水印不能为空")]
public string Watermark { get; set; }
/// <summary>
/// 版权信息
/// </summary>
[Required(ErrorMessage = "版权信息不能为空")]
public string Copyright { get; set; }
/// <summary>
/// ICP备案号
/// </summary>
[Required(ErrorMessage = "备案号不能为空")]
public string Icp { get; set; }
/// <summary>
/// 排序
/// </summary>
[Required(ErrorMessage = "排序不能为空")]
public int OrderNo { get; set; } = 100;
/// <summary>
/// 备注
/// </summary>
[MaxLength(256, ErrorMessage = "备注字符长度不能超过256")]
public string Remark { get; set; }
}
/// <summary>
/// 更新应用输入参数
/// </summary>
public class UpdateSysAppInput : AddSysAppInput
{
/// <summary>
/// 主键Id
/// </summary>
[Required(ErrorMessage = "主键Id不能为空")]
public long Id { get; set; }
}
/// <summary>
/// 授权应用菜单
/// </summary>
public class UpdateAppMenuInput : BaseIdInput
{
/// <summary>
/// 菜单Id集合
/// </summary>
public List<long> MenuIdList { get; set; }
}
/// <summary>
/// 租户iId
/// </summary>
public class ChangeAppInput : BaseIdInput
{
/// <summary>
/// 租户Id
/// </summary>
[Required(ErrorMessage = "租户不能为空")]
public long TenantId { get; set; }
}

View File

@ -1,103 +0,0 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Service;
/// <summary>
/// 应用输出参数
/// </summary>
public class SysAppOutput
{
/// <summary>
/// 主键Id
/// </summary>
public long Id { get; set; }
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 图标
/// </summary>
public string? Logo { get; set; }
/// <summary>
/// 标题
/// </summary>
public string Title { get; set; }
/// <summary>
/// 副标题
/// </summary>
public string ViceTitle { get; set; }
/// <summary>
/// 副描述
/// </summary>
public string? ViceDesc { get; set; }
/// <summary>
/// 水印
/// </summary>
public string? Watermark { get; set; }
/// <summary>
/// 版权信息
/// </summary>
public string? Copyright { get; set; }
/// <summary>
/// ICP备案号
/// </summary>
public string? Icp { get; set; }
/// <summary>
/// 排序
/// </summary>
public int OrderNo { get; set; }
/// <summary>
/// 备注
/// </summary>
public string? Remark { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime? CreateTime { get; set; }
/// <summary>
/// 更新时间
/// </summary>
public DateTime? UpdateTime { get; set; }
/// <summary>
/// 创建者Id
/// </summary>
public long? CreateUserId { get; set; }
/// <summary>
/// 创建者姓名
/// </summary>
public string? CreateUserName { get; set; }
/// <summary>
/// 修改者Id
/// </summary>
public long? UpdateUserId { get; set; }
/// <summary>
/// 修改者姓名
/// </summary>
public string? UpdateUserName { get; set; }
/// <summary>
/// 软删除
/// </summary>
public bool IsDelete { get; set; }
}

View File

@ -1,183 +0,0 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Service;
/// <summary>
/// 系统应用服务 🧩
/// </summary>
[ApiDescriptionSettings(Name = "SysApp", Order = 495, Description = "系统应用")]
public class SysAppService : IDynamicApiController, ITransient
{
private readonly SqlSugarRepository<SysAppMenu> _sysAppMenuRep;
private readonly SqlSugarRepository<SysApp> _sysAppRep;
private readonly SysAuthService _sysAuthService;
private readonly UserManager _userManager;
public SysAppService(SqlSugarRepository<SysApp> sysAppRep,
SqlSugarRepository<SysAppMenu> sysAppMenuRep,
SysAuthService sysAuthService,
UserManager userManager)
{
_sysAppRep = sysAppRep;
_userManager = userManager;
_sysAppMenuRep = sysAppMenuRep;
_sysAuthService = sysAuthService;
}
/// <summary>
/// 分页查询应用 🔖
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("分页查询应用")]
[ApiDescriptionSettings(Name = "Page"), HttpPost]
public async Task<SqlSugarPagedList<SysAppOutput>> Page(BasePageInput input)
{
input.Keyword = input.Keyword?.Trim();
var query = _sysAppRep.AsQueryable()
.WhereIF(!string.IsNullOrWhiteSpace(input.Keyword), u => u.Name.Contains(input.Keyword) ||
u.Title.Contains(input.Keyword) || u.ViceTitle.Contains(input.Keyword) ||
u.ViceDesc.Contains(input.Keyword) || u.Remark.Contains(input.Keyword))
.OrderBy(u => new { u.OrderNo, u.Id })
.Select<SysAppOutput>();
return await query.OrderBuilder(input).ToPagedListAsync(input.Page, input.PageSize);
}
/// <summary>
/// 增加应用 🔖
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("增加应用")]
[ApiDescriptionSettings(Name = "Add"), HttpPost]
public async Task<long> Add(AddSysAppInput input)
{
var entity = input.Adapt<SysApp>();
return await _sysAppRep.InsertAsync(entity) ? entity.Id : 0;
}
/// <summary>
/// 更新应用 🔖
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("更新应用")]
[ApiDescriptionSettings(Name = "Update"), HttpPost]
public async Task Update(UpdateSysAppInput input)
{
_ = await _sysAppRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
var entity = input.Adapt<SysApp>();
await _sysAppRep.AsUpdateable(entity).ExecuteCommandAsync();
}
/// <summary>
/// 删除应用 🔖
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("删除应用")]
[ApiDescriptionSettings(Name = "Delete"), HttpPost]
public async Task Delete(BaseIdInput input)
{
var entity = await _sysAppRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
// 禁止删除存在关联租户的应用
if (await _sysAppRep.Context.Queryable<SysTenant>().AnyAsync(u => u.AppId == input.Id)) throw Oops.Oh(ErrorCodeEnum.A1001);
// 禁止删除存在关联菜单的应用
if (await _sysAppMenuRep.AsQueryable().AnyAsync(u => u.AppId == input.Id)) throw Oops.Oh(ErrorCodeEnum.A1002);
await _sysAppRep.DeleteAsync(entity);
}
/// <summary>
/// 获取应用菜单 🔖
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[DisplayName("获取应用菜单")]
public async Task<List<long>> GetMenuList([FromQuery] long id)
{
return await _sysAppMenuRep.AsQueryable().Where(u => u.AppId == id).Select(u => u.MenuId).ToListAsync();
}
/// <summary>
/// 授权应用菜单 🔖
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[UnitOfWork]
[DisplayName("授权应用菜单")]
public async Task GrantMenu(UpdateAppMenuInput input)
{
input.MenuIdList ??= new();
await _sysAppMenuRep.DeleteAsync(u => u.AppId == input.Id);
var list = input.MenuIdList.Select(id => new SysAppMenu { AppId = input.Id, MenuId = id }).ToList();
await _sysAppMenuRep.InsertRangeAsync(list);
// 清除应用下其他模块越权的授权数据,包括角色菜单,用户收藏菜单
var tenantIds = await _sysAppRep.Context.Queryable<SysTenant>().Where(u => u.AppId == input.Id).Select(u => u.Id).ToListAsync();
var roleIds = await _sysAppRep.Context.Queryable<SysRole>().Where(u => tenantIds.Contains((long)u.TenantId)).Select(u => u.Id).ToListAsync();
var userIds = await _sysAppRep.Context.Queryable<SysUser>().Where(u => tenantIds.Contains((long)u.TenantId)).Select(u => u.Id).ToListAsync();
await _sysAppRep.Context.Deleteable<SysRoleMenu>().Where(u => roleIds.Contains(u.RoleId) && !input.MenuIdList.Contains(u.MenuId)).ExecuteCommandAsync();
await _sysAppRep.Context.Deleteable<SysUserMenu>().Where(u => userIds.Contains(u.UserId) && !input.MenuIdList.Contains(u.MenuId)).ExecuteCommandAsync();
}
/// <summary>
/// 获取应用数据 🔖
/// </summary>
/// <returns></returns>
[DisplayName("获取应用数据")]
public async Task<dynamic> GetAppData()
{
var list = await _sysAppRep.AsQueryable().Includes(u => u.TenantList).ToListAsync();
return list.Where(u => u.TenantList.Count > 0).Select(u => new
{
u.Id,
Value = u.Id,
Label = u.Name,
Children = u.TenantList.Select(t => new
{
t.Id,
Value = t.Id,
Label = t.Host ?? (t.Id + ""),
})
});
}
/// <summary>
/// 切换应用 🔖
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("切换应用")]
public async Task<LoginOutput> ChangeApp(ChangeAppInput input)
{
_ = await _sysAppRep.Context.Queryable<SysTenant>().FirstAsync(u => u.Id == input.TenantId) ?? throw Oops.Oh(ErrorCodeEnum.Z1003);
_ = await _sysAppRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
var user = await _sysAppRep.Context.Queryable<SysUser>().FirstAsync(u => u.Id == _userManager.UserId);
user.TenantId = input.TenantId;
return await _sysAuthService.CreateToken(user, input.Id, LoginModeEnum.PC);
}
/// <summary>
/// 获取当前应用信息
/// </summary>
/// <returns></returns>
[NonAction]
public async Task<SysApp> GetCurrentAppInfo()
{
var appId = _userManager.AppId > 0 ? _userManager.AppId : SqlSugarConst.DefaultAppId;
return await _sysAppRep.GetFirstAsync(u => u.Id == appId) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
}
}

View File

@ -73,5 +73,5 @@ public class LoginPhoneInput
/// <summary>
/// 登录模式
/// </summary>
public LoginModeEnum LoginMode { get; set; }
public LoginModeEnum LoginMode { get; set; } = LoginModeEnum.PC;
}

View File

@ -21,13 +21,15 @@ public class SysAuthService : IDynamicApiController, ITransient
private readonly SysCacheService _sysCacheService;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ICaptcha _captcha;
private readonly IEventPublisher _eventPublisher;
public SysAuthService(UserManager userManager,
SqlSugarRepository<SysUser> sysUserRep,
SysConfigService sysConfigService,
SysCacheService sysCacheService,
IHttpContextAccessor httpContextAccessor,
ICaptcha captcha)
ICaptcha captcha,
IEventPublisher eventPublisher)
{
_userManager = userManager;
_sysUserRep = sysUserRep;
@ -35,6 +37,7 @@ public class SysAuthService : IDynamicApiController, ITransient
_sysCacheService = sysCacheService;
_httpContextAccessor = httpContextAccessor;
_captcha = captcha;
_eventPublisher = eventPublisher;
}
/// <summary>
@ -57,21 +60,18 @@ public class SysAuthService : IDynamicApiController, ITransient
// 判断是否开启验证码并校验
if (await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysCaptcha) && !_captcha.Validate(input.CodeId.ToString(), input.Code)) throw Oops.Oh(ErrorCodeEnum.D0008);
// 获取登录租户和用户
var (tenant, user) = await GetLoginUserAndTenant(input.TenantId, account: input.Account);
// 账号是否被冻结
if (user.Status == StatusEnum.Disable) throw Oops.Oh(ErrorCodeEnum.D1017);
// 获取并验证账号
var user = await GetLoginUser(input.TenantId, account: input.Account);
// 是否开启域登录验证
if (await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysDomainLogin))
{
var userLdap = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysUserLdap>>().GetFirstAsync(u => u.UserId == user.Id && u.TenantId == tenant.Id);
var userLdap = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysUserLdap>>().GetFirstAsync(u => u.UserId == user.Id && u.TenantId == user.TenantId);
if (userLdap == null)
{
VerifyPassword(input.Password, keyPasswordErrorTimes, passwordErrorTimes, user);
}
else if (!await App.GetRequiredService<SysLdapService>().AuthAccount(tenant.Id, userLdap.Account, CryptogramUtil.Decrypt(input.Password)))
else if (!await App.GetRequiredService<SysLdapService>().AuthAccount(user.TenantId, userLdap.Account, CryptogramUtil.Decrypt(input.Password)))
{
_sysCacheService.Set(keyPasswordErrorTimes, ++passwordErrorTimes, TimeSpan.FromMinutes(30));
throw Oops.Oh(ErrorCodeEnum.D1000);
@ -83,42 +83,42 @@ public class SysAuthService : IDynamicApiController, ITransient
// 登录成功则清空密码错误次数
_sysCacheService.Remove(keyPasswordErrorTimes);
return await CreateToken(user, tenant.AppId, input.LoginMode);
return await CreateToken(user);
}
/// <summary>
/// 获取登录租户和用户
/// 获取登录用户
/// </summary>
/// <param name="tenantId"></param>
/// <param name="account"></param>
/// <param name="phone"></param>
/// <returns></returns>
[NonAction]
public async Task<(SysTenant tenant, SysUser user)> GetLoginUserAndTenant(long tenantId, string account = null, string phone = null)
public async Task<SysUser> GetLoginUser(long tenantId, string account = null, string phone = null)
{
// 若没有传值租户Id则从请求页URL参数中获取租户Id空则默认租户
if (tenantId < 1)
{
var tenantidStr = _httpContextAccessor.HttpContext.Request.Query["tenantid"].ToString();
tenantId = string.IsNullOrWhiteSpace(tenantidStr) ? SqlSugarConst.DefaultTenantId : long.Parse(tenantidStr);
}
// 判断租户是否存在及状态
var tenant = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysTenant>>().GetFirstAsync(u => u.Id == tenantId);
if (tenant?.Status != StatusEnum.Enable) throw Oops.Oh(ErrorCodeEnum.Z1003);
//// 若没有传值租户Id则从请求页URL参数中获取租户Id空则默认租户
//if (tenantId < 1)
//{
// var tenantidStr = _httpContextAccessor.HttpContext.Request.Query["tenantid"].ToString();
// tenantId = string.IsNullOrWhiteSpace(tenantidStr) ? SqlSugarConst.DefaultTenantId : long.Parse(tenantidStr);
//}
// 判断账号是否存在
var user = await _sysUserRep.AsQueryable().Includes(t => t.SysOrg).ClearFilter()
.Where(u => u.AccountType == AccountTypeEnum.SuperAdmin || u.TenantId == tenantId)
//.WhereIF(tenantId > 0, u => u.TenantId == tenantId)
.WhereIF(!string.IsNullOrWhiteSpace(account), u => u.Account.Equals(account))
.WhereIF(!string.IsNullOrWhiteSpace(phone), u => u.Phone.Equals(phone))
.FirstAsync();
_ = user ?? throw Oops.Oh(ErrorCodeEnum.D0009);
// 如果是超级管理员,则引用登录选择的租户进入系统
if (user.AccountType == AccountTypeEnum.SuperAdmin) user.TenantId = tenantId;
// 判断账号是否被冻结
if (user.Status == StatusEnum.Disable) throw Oops.Oh(ErrorCodeEnum.D1017);
return (tenant, user);
// 判断租户是否存在及状态
var tenant = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysTenant>>().GetFirstAsync(u => u.Id == user.TenantId);
if (tenant?.Status != StatusEnum.Enable) throw Oops.Oh(ErrorCodeEnum.Z1003);
return user;
}
/// <summary>
@ -200,26 +200,21 @@ public class SysAuthService : IDynamicApiController, ITransient
// 校验短信验证码
App.GetRequiredService<SysSmsService>().VerifyCode(new SmsVerifyCodeInput { Phone = input.Phone, Code = input.Code });
// 获取登录租户和用户
var (tenant, user) = await GetLoginUserAndTenant(input.TenantId, phone: input.Phone);
// 获取并验证账号
var user = await GetLoginUser(input.TenantId, phone: input.Phone);
return await CreateToken(user, tenant.AppId, input.LoginMode);
return await CreateToken(user);
}
/// <summary>
/// 生成Token令牌 🔖
/// </summary>
/// <param name="user"></param>
/// <param name="appId"></param>
/// <param name="loginMode"></param>
/// <returns></returns>
[NonAction]
internal async Task<LoginOutput> CreateToken(SysUser user, long? appId, LoginModeEnum loginMode)
internal async Task<LoginOutput> CreateToken(SysUser user, LoginModeEnum loginMode = LoginModeEnum.PC)
{
// 默认PC端登录模式
if (loginMode == 0)
loginMode = LoginModeEnum.PC;
// 单用户登录
await App.GetRequiredService<SysOnlineUserService>().SingleLogin(user.Id, loginMode);
@ -227,7 +222,6 @@ public class SysAuthService : IDynamicApiController, ITransient
var tokenExpire = await _sysConfigService.GetTokenExpire();
var accessToken = JWTEncryption.Encrypt(new Dictionary<string, object>
{
{ ClaimConst.AppId, appId },
{ ClaimConst.UserId, user.Id },
{ ClaimConst.TenantId, user.TenantId },
{ ClaimConst.Account, user.Account },
@ -263,6 +257,9 @@ public class SysAuthService : IDynamicApiController, ITransient
u.LastLoginDevice,
}).ExecuteCommandAsync();
// 发布系统登录事件
await _eventPublisher.PublishAsync(UserEventTypeEnum.Login, user);
return new LoginOutput
{
AccessToken = accessToken,
@ -330,11 +327,14 @@ public class SysAuthService : IDynamicApiController, ITransient
/// 退出系统 🔖
/// </summary>
[DisplayName("退出系统")]
public void Logout()
public async void Logout()
{
if (string.IsNullOrWhiteSpace(_userManager.Account))
throw Oops.Oh(ErrorCodeEnum.D1011);
// 发布系统退出事件
await _eventPublisher.PublishAsync(UserEventTypeEnum.Logout, _userManager);
_httpContextAccessor.HttpContext.SignoutToSwagger();
}

View File

@ -112,7 +112,7 @@ public class SysLdapService : IDynamicApiController, ITransient
/// <param name="tenantId">租户</param>
/// <returns></returns>
[NonAction]
public async Task<bool> AuthAccount(long tenantId, string account, string password)
public async Task<bool> AuthAccount(long? tenantId, string account, string password)
{
var sysLdap = await _sysLdapRep.GetFirstAsync(u => u.TenantId == tenantId) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
var ldapConn = new LdapConnection();

View File

@ -113,7 +113,7 @@ public class SysOAuthService : IDynamicApiController, ITransient
}
// 构建Token令牌默认回调登录为PC模式
var token = await App.GetRequiredService<SysAuthService>().CreateToken(wechatUser.SysUser, SqlSugarConst.DefaultAppId, LoginModeEnum.PC);
var token = await App.GetRequiredService<SysAuthService>().CreateToken(wechatUser.SysUser);
return new RedirectResult($"{redirectUrl}/#/login?token={token.AccessToken}");
}

View File

@ -45,8 +45,8 @@ public class ExcelHelper
Task.WhenAll(tasks).GetAwaiter().GetResult();
// 仅导出错误记录
var errorList = result.Where(u => !string.IsNullOrWhiteSpace(u.Error));
return ExportData(errorList.Any() ? errorList : []);
var errorList = result.Where(u => !string.IsNullOrWhiteSpace(u.Error)).ToList();
return ExportData(errorList.Count != 0 ? errorList : []);
}
catch (Exception ex)
{

View File

@ -2,7 +2,7 @@
"name": "admin.net.pro",
"type": "module",
"version": "2.4.33",
"lastBuildTime": "2025.01.14",
"lastBuildTime": "2025.01.15",
"description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架",
"author": "zuohuaijun",
"license": "MIT",
@ -39,7 +39,7 @@
"element-plus": "^2.9.3",
"exceljs": "^4.4.0",
"ezuikit-js": "^8.1.4",
"gcoord": "^1.0.6",
"gcoord": "^1.0.7",
"js-cookie": "^3.0.5",
"js-table2excel": "^1.1.2",
"jsplumb": "^2.15.6",
@ -100,7 +100,7 @@
"less": "^4.2.1",
"prettier": "^3.4.2",
"rollup-plugin-visualizer": "^5.14.0",
"sass": "^1.83.3",
"sass": "^1.83.4",
"terser": "^5.37.0",
"typescript": "^5.7.3",
"vite": "^6.0.7",

View File

@ -182,7 +182,7 @@ const onHandleCommandClick = (path: string) => {
showCancelButton: true,
confirmButtonText: t('message.user.logOutConfirm'),
cancelButtonText: t('message.user.logOutCancel'),
buttonSize: 'default',
// buttonSize: 'default',
beforeClose: async (action, instance, done) => {
if (action === 'confirm') {
instance.confirmButtonLoading = true;

View File

@ -8,7 +8,7 @@
</div>
</template>
<el-tabs v-model="activeTab" class="demo-tabs">
<el-tab-pane label="代码生成" name="codeGen" style="height: 600px">
<el-tab-pane label="代码生成" name="codeGen" style="height: 700px">
<div style="color: red; padding: 10px 10px; background: #faecd8; margin-bottom: 10px">
<el-icon style="transform: translateY(2px)"><ele-Bell /></el-icon>
<span> 若找不到在前端生成的实体/请检查配置文件中实体所在程序集或重启后台服务 </span>
@ -73,13 +73,6 @@
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="Name字段" prop="treeName" :rules="[{ required: true, message: '请选择树控件Name字段', trigger: 'blur' }]">
<el-select v-model="state.ruleForm.treeName" @change="treeNameChanged" value-key="value" filterable clearable class="w100">
<el-option v-for="item in state.columnData" :key="item.columnName" :label="item.columnName + ' ( ' + item.columnName + ' ) [' + item.columnComment + ']'" :value="item" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="业务名" prop="busName" :rules="[{ required: true, message: '业务名不能为空', trigger: 'blur' }]">
<el-input v-model="state.ruleForm.busName" placeholder="请输入" clearable />
@ -213,21 +206,31 @@
</template>
</el-col> -->
<el-col>
<el-divider content-position="center"> 左边布局显示树形列表右边布局上下结构显示主子表数据列表 </el-divider>
<!-- <p><el-tag style="border: 1 solid var(--el-border-color)">以下默认页面左右布局左边布局显示树形列表右边布局上下结构显示主子表数据列表</el-tag></p> -->
<el-tabs v-model="activeName" class="demo-tabs">
<el-tab-pane label="页面左边树" name="1">
<el-tab-pane label="页面左(树列表)" name="1">
<el-row :gutter="10">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="树库定位器" prop="configId2">
<el-form-item label="树 - 库定位器" prop="configId2">
<el-select v-model="state.ruleForm.configId2" placeholder="库名" filterable @change="dbChanged2()" class="w100">
<el-option v-for="item in state.dbData" :key="item.configId" :label="item.configId" :value="item.configId" />
</el-select>
</el-form-item>
</el-col>
<!-- <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="Name字段" prop="treeName" :rules="[{ required: true, message: '请选择树控件Name字段', trigger: 'blur' }]">
<el-select v-model="state.ruleForm.treeName" @change="treeNameChanged" value-key="value" filterable clearable class="w100">
<el-option v-for="item in state.columnData" :key="item.columnName" :label="item.columnName + ' ( ' + item.columnName + ' ) [' + item.columnComment + ']'" :value="item" />
</el-select>
</el-form-item>
</el-col> -->
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="左边树表" prop="leftTab">
<el-form-item label="树表名称">
<template v-slot:label>
<div>
左边树表
树表名称
<el-tooltip raw-content content="若找不到在前端生成的实体/表同上如表有下划线_则因实体去掉划线取不到字段。" placement="top">
<el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"><ele-QuestionFilled /></el-icon>
</el-tooltip>
@ -240,14 +243,14 @@
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="左边树关联字段" prop="leftKey">
<el-form-item label="树关联字段">
<el-select v-model="state.ruleForm.leftKey" @change="leftKeyChanged" value-key="value" filterable clearable class="w100">
<el-option v-for="item in state.lcolumnData" :key="item.columnName" :label="item.columnName + ' ( ' + item.columnName + ' ) [' + item.columnComment + ']'" :value="item" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="关联主表字段" prop="bottomKey">
<el-form-item label="关联主表字段">
<template v-slot:label>
<div>
关联主表字段
@ -263,7 +266,7 @@
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="左边树Name字段" prop="leftName">
<el-form-item label="树显示名称">
<el-select v-model="state.ruleForm.leftName" @change="leftNameChanged" value-key="value" filterable clearable class="w100">
<el-option v-for="item in state.lcolumnData" :key="item.columnName" :label="item.columnName + ' ( ' + item.columnName + ' ) [' + item.columnComment + ']'" :value="item" />
</el-select>
@ -271,26 +274,26 @@
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="模板" prop="template">
<el-form-item label="模板">
<el-input v-model="state.ruleForm.template" clearable placeholder="请输入" />
</el-form-item>
</el-col>
</el-row>
</el-tab-pane>
<el-tab-pane label="右区域下框" name="2">
<el-tab-pane label="页面右(主子表)" name="2">
<el-row :gutter="10">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="右区域下框库定位器" prop="configId3">
<el-form-item label="子表 - 库定位器" prop="configId3">
<el-select v-model="state.ruleForm.configId3" placeholder="库名" filterable @change="dbChanged3()" class="w100">
<el-option v-for="item in state.dbData" :key="item.configId" :label="item.configId" :value="item.configId" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="表名称" prop="bottomTab">
<el-form-item label="表名称" prop="bottomTab">
<template v-slot:label>
<div>
下框关联表名称
表名称
<el-tooltip raw-content content="若找不到在前端生成的实体/表同上如表有下划线_则因实体去掉划线取不到字段。" placement="top">
<el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"><ele-QuestionFilled /></el-icon>
</el-tooltip>
@ -302,7 +305,7 @@
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="下框表关联字段" prop="bottomKey">
<el-form-item label="表关联字段" prop="bottomKey">
<el-select v-model="state.ruleForm.bottomKey" @change="bottomKeyChanged" value-key="value" filterable clearable class="w100">
<el-option v-for="item in state.bcolumnData" :key="item.columnName" :label="item.columnName + ' ( ' + item.columnName + ' ) [' + item.columnComment + ']'" :value="item" />
</el-select>
@ -330,7 +333,7 @@
</el-row>
</el-form>
</el-tab-pane>
<el-tab-pane label="选择模板" name="template" style="height: 600px">
<el-tab-pane label="选择模板" name="template" style="height: 700px">
<el-table ref="templateTableRef" :data="templateTableData" @selection-change="handleSelectionChange" style="width: 100%">
<el-table-column type="selection" width="55" />
<el-table-column property="name" label="模板文件名" width="280" />