😎1、增加登录配置租户Id参数模式 2、代码优化

This commit is contained in:
zuohuaijun 2025-01-14 14:47:34 +08:00
parent bf2752df8c
commit 6f5d4646df
23 changed files with 347 additions and 205 deletions

View File

@ -45,11 +45,11 @@
<PackageReference Include="SixLabors.ImageSharp.Web" Version="3.1.3" /> <PackageReference Include="SixLabors.ImageSharp.Web" Version="3.1.3" />
<PackageReference Include="SKIT.FlurlHttpClient.Wechat.Api" Version="3.6.0" /> <PackageReference Include="SKIT.FlurlHttpClient.Wechat.Api" Version="3.6.0" />
<PackageReference Include="SKIT.FlurlHttpClient.Wechat.TenpayV3" Version="3.9.0" /> <PackageReference Include="SKIT.FlurlHttpClient.Wechat.TenpayV3" Version="3.9.0" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.173" /> <PackageReference Include="SqlSugarCore" Version="5.1.4.174" />
<PackageReference Include="SSH.NET" Version="2024.2.0" /> <PackageReference Include="SSH.NET" Version="2024.2.0" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.5.1" /> <PackageReference Include="System.Linq.Dynamic.Core" Version="1.5.1" />
<PackageReference Include="System.Net.Http" Version="4.3.4" /> <PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1162" /> <PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1163" />
<PackageReference Include="UAParser" Version="3.1.47" /> <PackageReference Include="UAParser" Version="3.1.47" />
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" /> <PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
</ItemGroup> </ItemGroup>

View File

@ -48,38 +48,34 @@ public class DictAttribute : ValidationAttribute, ITransient
protected override ValidationResult IsValid(object? value, ValidationContext validationContext) protected override ValidationResult IsValid(object? value, ValidationContext validationContext)
{ {
// 判断是否允许空值 // 判断是否允许空值
if (AllowNullValue && value == null) if (AllowNullValue && value == null) return ValidationResult.Success;
return ValidationResult.Success;
var valueAsString = value?.ToString(); var valueAsString = value?.ToString();
// 是否忽略空字符串 // 是否忽略空字符串
if (AllowEmptyStrings && string.IsNullOrEmpty(valueAsString)) if (AllowEmptyStrings && string.IsNullOrEmpty(valueAsString)) return ValidationResult.Success;
return ValidationResult.Success;
// 获取属性的类型 // 获取属性的类型
var property = validationContext.ObjectType.GetProperty(validationContext.MemberName); var property = validationContext.ObjectType.GetProperty(validationContext.MemberName!);
if (property == null) if (property == null) return new ValidationResult($"未知属性: {validationContext.MemberName}");
return new ValidationResult($"未知属性: {validationContext.MemberName}");
var propertyType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType; var propertyType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
// 枚举类型验证 // 枚举类型验证
if (propertyType.IsEnum) if (propertyType.IsEnum)
{ {
return !Enum.IsDefined(propertyType, value) if (!Enum.IsDefined(propertyType, value!)) return new ValidationResult($"提示:{ErrorMessage}|枚举值【{value}】不是有效的【{propertyType.Name}】枚举类型值!");
? new ValidationResult($"提示:{ErrorMessage}|枚举值【{value}】不是有效的【{propertyType.Name}】枚举类型值!") return ValidationResult.Success;
: ValidationResult.Success;
} }
// 先尝试从 ValidationContext 的依赖注入容器中拿服务,拿不到或类型不匹配时,再从全局的 App 容器中获取 // 先尝试从 ValidationContext 的依赖注入容器中拿服务,再从全局的 App 容器中获取
if (validationContext.GetService(typeof(SysDictDataService)) is not SysDictDataService sysDictDataService) if (validationContext.GetService(typeof(SysDictDataService)) is not SysDictDataService sysDictDataService)
sysDictDataService = App.GetRequiredService<SysDictDataService>(); sysDictDataService = App.GetRequiredService<SysDictDataService>();
// 获取字典值列表 // 获取字典值列表
var dictDataList = sysDictDataService.GetDataList(DictTypeCode).GetAwaiter().GetResult(); var dictDataList = sysDictDataService.GetDataList(DictTypeCode).GetAwaiter().GetResult();
// 使用 HashSet 来提高查找效率 // 使用 HashSet 来提高查找效率
var dictHash = new HashSet<string>(dictDataList.Select(u => u.Code)); var dictHash = new HashSet<string>(dictDataList.Select(u => u.Code));
if (!dictHash.Contains(valueAsString)) if (!dictHash.Contains(valueAsString)) return new ValidationResult($"提示:{ErrorMessage}|字典【{DictTypeCode}】不包含【{valueAsString}】!");
return new ValidationResult($"提示:{ErrorMessage}|字典【{DictTypeCode}】不包含【{valueAsString}】!");
return ValidationResult.Success; return ValidationResult.Success;
} }

View File

@ -71,10 +71,10 @@ public class ConfigConst
/// </summary> /// </summary>
public const string SysDomainLogin = "sys_domain_login"; public const string SysDomainLogin = "sys_domain_login";
/// <summary> ///// <summary>
/// 租户域名隔离登录验证 ///// 租户域名隔离登录验证
/// </summary> ///// </summary>
public const string SysTenantHostLogin = "sys_tenant_host_login"; //public const string SysTenantHostLogin = "sys_tenant_host_login";
/// <summary> /// <summary>
/// 行政区划同步层级 1-省级,2-市级,3-区县级,4-街道级,5-村级 /// 行政区划同步层级 1-省级,2-市级,3-区县级,4-街道级,5-村级

View File

@ -35,9 +35,4 @@ public class SqlSugarConst
/// 默认租户Id /// 默认租户Id
/// </summary> /// </summary>
public const long DefaultTenantId = 1300000000001; public const long DefaultTenantId = 1300000000001;
/// <summary>
/// 默认租户域名
/// </summary>
public const string DefaultTenantHost = "gitee.com";
} }

View File

@ -70,12 +70,14 @@ public partial class SysCodeGen : EntityBase
[SugarColumn(ColumnDescription = "数据库表名", Length = 128)] [SugarColumn(ColumnDescription = "数据库表名", Length = 128)]
[MaxLength(128)] [MaxLength(128)]
public string? TableName { get; set; } public string? TableName { get; set; }
/// <summary> /// <summary>
/// 自身树控件Name /// 树控件名称
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "自身树控件Name", Length = 64)] [SugarColumn(ColumnDescription = "树控件名称", Length = 64)]
[MaxLength(64)] [MaxLength(64)]
public string? TreeName { get; set; } public string? TreeName { get; set; }
/// <summary> /// <summary>
/// 命名空间 /// 命名空间
/// </summary> /// </summary>
@ -134,6 +136,7 @@ public partial class SysCodeGen : EntityBase
[SugarColumn(ColumnDescription = "打印模版名称", Length = 32)] [SugarColumn(ColumnDescription = "打印模版名称", Length = 32)]
[MaxLength(32)] [MaxLength(32)]
public string? PrintName { get; set; } public string? PrintName { get; set; }
/// <summary> /// <summary>
/// 左边树形结构表 /// 左边树形结构表
/// </summary> /// </summary>
@ -147,16 +150,18 @@ public partial class SysCodeGen : EntityBase
[SugarColumn(ColumnDescription = "左边关联字段", Length = 64)] [SugarColumn(ColumnDescription = "左边关联字段", Length = 64)]
[MaxLength(64)] [MaxLength(64)]
public string? LeftKey { get; set; } public string? LeftKey { get; set; }
/// <summary> /// <summary>
/// 左边关联主表字段 /// 左边关联主表字段
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "左边关联主表字段", Length = 64)] [SugarColumn(ColumnDescription = "左边关联主表字段", Length = 64)]
[MaxLength(64)] [MaxLength(64)]
public string? LeftPrimaryKey { get; set; } public string? LeftPrimaryKey { get; set; }
/// <summary> /// <summary>
/// 左边树Name /// 左边树名称
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "左边树Name", Length = 64)] [SugarColumn(ColumnDescription = "左边树名称", Length = 64)]
[MaxLength(64)] [MaxLength(64)]
public string? LeftName { get; set; } public string? LeftName { get; set; }
@ -173,6 +178,7 @@ public partial class SysCodeGen : EntityBase
[SugarColumn(ColumnDescription = "右区域下框表关联字段", Length = 64)] [SugarColumn(ColumnDescription = "右区域下框表关联字段", Length = 64)]
[MaxLength(64)] [MaxLength(64)]
public string? BottomKey { get; set; } public string? BottomKey { get; set; }
/// <summary> /// <summary>
/// 下表关联主表字段 /// 下表关联主表字段
/// </summary> /// </summary>
@ -186,6 +192,7 @@ public partial class SysCodeGen : EntityBase
[SugarColumn(ColumnDescription = "模板文件夹", Length = 64)] [SugarColumn(ColumnDescription = "模板文件夹", Length = 64)]
[MaxLength(64)] [MaxLength(64)]
public string? Template { get; set; } public string? Template { get; set; }
/// <summary> /// <summary>
/// 是否使用 Api Service /// 是否使用 Api Service
/// </summary> /// </summary>

View File

@ -4,8 +4,6 @@
// //
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using Admin.NET.Core;
namespace Admin.NET.Core.SeedData; namespace Admin.NET.Core.SeedData;
/// <summary> /// <summary>
@ -227,11 +225,7 @@ public class SysCodeGenTemplateSeedData : ISqlSugarEntitySeedData<SysCodeGenTemp
} }
] ]
"; ";
List<SysCodeGenTemplate> records = Newtonsoft.Json.JsonConvert.DeserializeObject<List<SysCodeGenTemplate>>(recordList); var records = JSON.Deserialize<List<SysCodeGenTemplate>>(recordList);
// 后处理数据的特殊字段
//for (int i = 0; i < records.Count; i++) { }
return records; return records;
} }
} }

View File

@ -31,7 +31,6 @@ public class SysConfigSeedData : ISqlSugarEntitySeedData<SysConfig>
new SysConfig{ Id=1300000000191, Name="RefreshToken过期时间", Code=ConfigConst.SysRefreshTokenExpire, Value="20160", SysFlag=YesNoEnum.Y, Remark="刷新Token过期时间分钟一般 refresh_token 的有效时间 > 2 * access_token 的有效时间)", OrderNo=100, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysConfig{ Id=1300000000191, Name="RefreshToken过期时间", Code=ConfigConst.SysRefreshTokenExpire, Value="20160", SysFlag=YesNoEnum.Y, Remark="刷新Token过期时间分钟一般 refresh_token 的有效时间 > 2 * access_token 的有效时间)", OrderNo=100, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysConfig{ Id=1300000000201, Name="发送异常日志邮件", Code=ConfigConst.SysErrorMail, Value="False", SysFlag=YesNoEnum.Y, Remark="是否发送异常日志邮件", OrderNo=110, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysConfig{ Id=1300000000201, Name="发送异常日志邮件", Code=ConfigConst.SysErrorMail, Value="False", SysFlag=YesNoEnum.Y, Remark="是否发送异常日志邮件", OrderNo=110, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysConfig{ Id=1300000000211, Name="域登录验证", Code=ConfigConst.SysDomainLogin, Value="False", SysFlag=YesNoEnum.Y, Remark="是否开启域登录验证", OrderNo=120, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysConfig{ Id=1300000000211, Name="域登录验证", Code=ConfigConst.SysDomainLogin, Value="False", SysFlag=YesNoEnum.Y, Remark="是否开启域登录验证", OrderNo=120, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysConfig{ Id=1300000000221, Name="租户隔离登录验证", Code=ConfigConst.SysTenantHostLogin, Value="False", SysFlag=YesNoEnum.Y, Remark="租户隔离登录验证", OrderNo=130, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysConfig{ Id=1300000000241, Name="行政区划同步层级", Code=ConfigConst.SysRegionSyncLevel, Value="3", SysFlag=YesNoEnum.Y, Remark="行政区划同步层级 1-省级,2-市级,3-区县级,4-街道级,5-村级", OrderNo=150, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysConfig{ Id=1300000000241, Name="行政区划同步层级", Code=ConfigConst.SysRegionSyncLevel, Value="3", SysFlag=YesNoEnum.Y, Remark="行政区划同步层级 1-省级,2-市级,3-区县级,4-街道级,5-村级", OrderNo=150, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
// 新业务系统记得更改密匙,通过接口(http://localhost:5005/api/sysCommon/smKeyPair)获取 // 新业务系统记得更改密匙,通过接口(http://localhost:5005/api/sysCommon/smKeyPair)获取
new SysConfig{ Id=1300000000251, Name="国密SM2密匙", Code=ConfigConst.SysSM2Key, Value="04851D329AA3E38C2E7670AFE70E6E70E92F8769CA27C8766B12209A0FFBA4493B603EF7A0B9B1E16F0E8930C0406EA0B179B68DF28E25334BDEC4AE76D907E9E9;3A61D1D30C6302DABFF36201D936D0143EEF0C850AF28C5CA6D5C045AF8C5C8A", SysFlag=YesNoEnum.Y, Remark="国密SM2密匙", OrderNo=160, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2024-11-21 00:00:00") }, new SysConfig{ Id=1300000000251, Name="国密SM2密匙", Code=ConfigConst.SysSM2Key, Value="04851D329AA3E38C2E7670AFE70E6E70E92F8769CA27C8766B12209A0FFBA4493B603EF7A0B9B1E16F0E8930C0406EA0B179B68DF28E25334BDEC4AE76D907E9E9;3A61D1D30C6302DABFF36201D936D0143EEF0C850AF28C5CA6D5C045AF8C5C8A", SysFlag=YesNoEnum.Y, Remark="国密SM2密匙", OrderNo=160, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2024-11-21 00:00:00") },

View File

@ -21,7 +21,7 @@ public class SysTenantSeedData : ISqlSugarEntitySeedData<SysTenant>
return return
[ [
new SysTenant{ Id=SqlSugarConst.DefaultTenantId, AppId=SqlSugarConst.DefaultTenantId, OrgId=SqlSugarConst.DefaultTenantId, UserId=1300000000111, Host=SqlSugarConst.DefaultTenantHost, 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, 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") },
]; ];
} }
} }

View File

@ -26,10 +26,10 @@ public class LoginInput
public string Password { get; set; } public string Password { get; set; }
/// <summary> /// <summary>
/// 租户域名 /// 租户Id
/// </summary> /// </summary>
//[Required(ErrorMessage = "租户域名不能为空")] //[Required(ErrorMessage = "租户Id不能为空")]
public string Host { get; set; } public long TenantId { get; set; }
/// <summary> /// <summary>
/// 验证码Id /// 验证码Id
@ -65,10 +65,10 @@ public class LoginPhoneInput
public string Code { get; set; } public string Code { get; set; }
/// <summary> /// <summary>
/// 租户域名 /// 租户Id
/// </summary> /// </summary>
//[Required(ErrorMessage = "租户域名不能为空")] //[Required(ErrorMessage = "租户Id不能为空")]
public string Host { get; set; } public long TenantId { get; set; }
/// <summary> /// <summary>
/// 登录模式 /// 登录模式

View File

@ -46,9 +46,6 @@ public class SysAuthService : IDynamicApiController, ITransient
[AllowAnonymous] [AllowAnonymous]
public virtual async Task<LoginOutput> Login([Required] LoginInput input) public virtual async Task<LoginOutput> Login([Required] LoginInput input)
{ {
//// 可以根据域名获取具体租户
//var host = _httpContextAccessor.HttpContext.Request.Host;
// 判断密码错误次数缓存30分钟 // 判断密码错误次数缓存30分钟
var keyPasswordErrorTimes = $"{CacheConst.KeyPasswordErrorTimes}{input.Account}"; var keyPasswordErrorTimes = $"{CacheConst.KeyPasswordErrorTimes}{input.Account}";
var passwordErrorTimes = _sysCacheService.Get<int>(keyPasswordErrorTimes); var passwordErrorTimes = _sysCacheService.Get<int>(keyPasswordErrorTimes);
@ -61,7 +58,7 @@ public class SysAuthService : IDynamicApiController, ITransient
if (await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysCaptcha) && !_captcha.Validate(input.CodeId.ToString(), input.Code)) throw Oops.Oh(ErrorCodeEnum.D0008); 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.Host, account: input.Account); var (tenant, user) = await GetLoginUserAndTenant(input.TenantId, account: input.Account);
// 账号是否被冻结 // 账号是否被冻结
if (user.Status == StatusEnum.Disable) throw Oops.Oh(ErrorCodeEnum.D1017); if (user.Status == StatusEnum.Disable) throw Oops.Oh(ErrorCodeEnum.D1017);
@ -92,53 +89,34 @@ public class SysAuthService : IDynamicApiController, ITransient
/// <summary> /// <summary>
/// 获取登录租户和用户 /// 获取登录租户和用户
/// </summary> /// </summary>
/// <param name="host"></param> /// <param name="tenantId"></param>
/// <param name="account"></param> /// <param name="account"></param>
/// <param name="phone"></param> /// <param name="phone"></param>
/// <returns></returns> /// <returns></returns>
[NonAction] [NonAction]
public async Task<(SysTenant tenant, SysUser user)> GetLoginUserAndTenant(string host, string account = null, string phone = null) public async Task<(SysTenant tenant, SysUser user)> GetLoginUserAndTenant(long tenantId, string account = null, string phone = null)
{ {
// 是否租户隔离登录验证 // 若没有传值租户Id则从请求页URL参数中获取租户Id空则默认租户
var isTenantHostLogin = await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysTenantHostLogin); if (tenantId < 1)
SysUser user;
SysTenant tenant;
// 租户隔离登录
if (isTenantHostLogin)
{ {
// 若租户域名为空或为本地域名,则取默认租户域名 var tenantidStr = _httpContextAccessor.HttpContext.Request.Query["tenantid"].ToString();
if (string.IsNullOrWhiteSpace(host) || host.StartsWith("localhost")) host = SqlSugarConst.DefaultTenantHost; tenantId = string.IsNullOrWhiteSpace(tenantidStr) ? SqlSugarConst.DefaultTenantId : long.Parse(tenantidStr);
}
// 租户是否存在或已禁用 // 判断租户是否存在及状态
tenant = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysTenant>>().GetFirstAsync(u => u.Host.Equals(host, StringComparison.CurrentCultureIgnoreCase)); var tenant = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysTenant>>().GetFirstAsync(u => u.Id == tenantId);
if (tenant?.Status != StatusEnum.Enable) throw Oops.Oh(ErrorCodeEnum.Z1003); if (tenant?.Status != StatusEnum.Enable) throw Oops.Oh(ErrorCodeEnum.Z1003);
// 根据入参类型、租户查询登录用户 // 判断账号是否存在
user = await _sysUserRep.AsQueryable().Includes(t => t.SysOrg).ClearFilter() var user = await _sysUserRep.AsQueryable().Includes(t => t.SysOrg).ClearFilter()
.Where(u => u.TenantId == tenant.Id || u.AccountType == AccountTypeEnum.SuperAdmin) .Where(u => u.AccountType == AccountTypeEnum.SuperAdmin || u.TenantId == tenantId)
.WhereIF(!string.IsNullOrWhiteSpace(account), u => u.Account.Equals(account)) .WhereIF(!string.IsNullOrWhiteSpace(account), u => u.Account.Equals(account))
.WhereIF(!string.IsNullOrWhiteSpace(phone), u => u.Phone.Equals(phone)) .WhereIF(!string.IsNullOrWhiteSpace(phone), u => u.Phone.Equals(phone))
.FirstAsync(); .FirstAsync();
_ = user ?? throw Oops.Oh(ErrorCodeEnum.D0009); _ = user ?? throw Oops.Oh(ErrorCodeEnum.D0009);
// 若登录的是超级管理员,则引用当前绑定的租户,这样登陆后操作的租户数据会与该租户关联 // 如果是超级管理员,则引用登录选择的租户进入系统
if (user.AccountType == AccountTypeEnum.SuperAdmin) user.TenantId = tenant.Id; if (user.AccountType == AccountTypeEnum.SuperAdmin) user.TenantId = tenantId;
}
else
{
// 根据入参类型查询登录用户
user = await _sysUserRep.AsQueryable().Includes(t => t.SysOrg).ClearFilter()
.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);
// 租户是否存在或已禁用
tenant = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysTenant>>().GetFirstAsync(u => u.Id == user.TenantId);
if (tenant?.Status != StatusEnum.Enable) throw Oops.Oh(ErrorCodeEnum.Z1003);
}
return (tenant, user); return (tenant, user);
} }
@ -223,7 +201,7 @@ public class SysAuthService : IDynamicApiController, ITransient
App.GetRequiredService<SysSmsService>().VerifyCode(new SmsVerifyCodeInput { Phone = input.Phone, Code = input.Code }); App.GetRequiredService<SysSmsService>().VerifyCode(new SmsVerifyCodeInput { Phone = input.Phone, Code = input.Code });
// 获取登录租户和用户 // 获取登录租户和用户
var (tenant, user) = await GetLoginUserAndTenant(input.Host, phone: input.Phone); var (tenant, user) = await GetLoginUserAndTenant(input.TenantId, phone: input.Phone);
return await CreateToken(user, tenant.AppId, input.LoginMode); return await CreateToken(user, tenant.AppId, input.LoginMode);
} }
@ -405,7 +383,7 @@ public class SysAuthService : IDynamicApiController, ITransient
{ {
Account = auth.UserName, Account = auth.UserName,
Password = CryptogramUtil.SM2Encrypt(auth.Password), Password = CryptogramUtil.SM2Encrypt(auth.Password),
Host = SqlSugarConst.DefaultTenantHost TenantId = SqlSugarConst.DefaultTenantId
}); });
_sysCacheService.Remove($"{CacheConst.KeyConfig}{ConfigConst.SysCaptcha}"); _sysCacheService.Remove($"{CacheConst.KeyConfig}{ConfigConst.SysCaptcha}");

View File

@ -44,6 +44,7 @@ public class CustomViewEngine : ViewEngineModel
return ClassName[..1].ToLower() + ClassName[1..]; // 首字母小写 return ClassName[..1].ToLower() + ClassName[1..]; // 首字母小写
} }
} }
public string? TreeName { get; set; } public string? TreeName { get; set; }
public string? LowerTreeName { get; set; } public string? LowerTreeName { get; set; }
public string? LeftTab { get; set; } public string? LeftTab { get; set; }

View File

@ -55,10 +55,12 @@ public class PageCodeGenInput : BasePageInput
/// 数据库表名 /// 数据库表名
/// </summary> /// </summary>
public virtual string TableName { get; set; } public virtual string TableName { get; set; }
/// <summary> /// <summary>
/// 自身树控件Name /// 树控件名称
/// </summary> /// </summary>
public string? TreeName { get; set; } public string? TreeName { get; set; }
/// <summary> /// <summary>
/// 命名空间 /// 命名空间
/// </summary> /// </summary>
@ -190,10 +192,12 @@ public class AddCodeGenInput : PageCodeGenInput
/// 左边关联字段 /// 左边关联字段
/// </summary> /// </summary>
public string? LeftKey { get; set; } public string? LeftKey { get; set; }
/// <summary> /// <summary>
/// 左边关联主表字段 /// 左边关联主表字段
/// </summary> /// </summary>
public string? LeftPrimaryKey { get; set; } public string? LeftPrimaryKey { get; set; }
/// <summary> /// <summary>
/// 左边树Name /// 左边树Name
/// </summary> /// </summary>

View File

@ -15,22 +15,25 @@ namespace Admin.NET.Core.Service;
public class SysCodeGenService : IDynamicApiController, ITransient public class SysCodeGenService : IDynamicApiController, ITransient
{ {
private readonly ISqlSugarClient _db; private readonly ISqlSugarClient _db;
private readonly DbConnectionOptions _dbConnectionOptions;
private readonly CodeGenOptions _codeGenOptions;
private readonly SqlSugarRepository<SysCodeGenTemplate> _codeGetTemplateRep; private readonly SqlSugarRepository<SysCodeGenTemplate> _codeGetTemplateRep;
private readonly SysCodeGenConfigService _codeGenConfigService; private readonly SysCodeGenConfigService _codeGenConfigService;
private readonly IViewEngine _viewEngine; private readonly IViewEngine _viewEngine;
private readonly CodeGenOptions _codeGenOptions;
public SysCodeGenService(ISqlSugarClient db, public SysCodeGenService(ISqlSugarClient db,
IOptions<DbConnectionOptions> dbConnectionOptions,
SqlSugarRepository<SysCodeGenTemplate> codeGetTemplateRep, SqlSugarRepository<SysCodeGenTemplate> codeGetTemplateRep,
SysCodeGenConfigService codeGenConfigService, SysCodeGenConfigService codeGenConfigService,
IViewEngine viewEngine, IViewEngine viewEngine,
IOptions<CodeGenOptions> codeGenOptions) IOptions<CodeGenOptions> codeGenOptions)
{ {
_db = db; _db = db;
_dbConnectionOptions = dbConnectionOptions.Value;
_codeGenOptions = codeGenOptions.Value;
_codeGetTemplateRep = codeGetTemplateRep; _codeGetTemplateRep = codeGetTemplateRep;
_codeGenConfigService = codeGenConfigService; _codeGenConfigService = codeGenConfigService;
_viewEngine = viewEngine; _viewEngine = viewEngine;
_codeGenOptions = codeGenOptions.Value;
} }
/// <summary> /// <summary>
@ -163,7 +166,7 @@ public class SysCodeGenService : IDynamicApiController, ITransient
[DisplayName("获取数据库库集合")] [DisplayName("获取数据库库集合")]
public async Task<List<DatabaseOutput>> GetDatabaseList() public async Task<List<DatabaseOutput>> GetDatabaseList()
{ {
var dbConfigs = App.GetOptions<DbConnectionOptions>().ConnectionConfigs; var dbConfigs = _dbConnectionOptions.ConnectionConfigs;
return await Task.FromResult(dbConfigs.Adapt<List<DatabaseOutput>>()); return await Task.FromResult(dbConfigs.Adapt<List<DatabaseOutput>>());
} }
@ -177,7 +180,7 @@ public class SysCodeGenService : IDynamicApiController, ITransient
var provider = _db.AsTenant().GetConnectionScope(configId); var provider = _db.AsTenant().GetConnectionScope(configId);
var dbTableInfos = provider.DbMaintenance.GetTableInfoList(false); var dbTableInfos = provider.DbMaintenance.GetTableInfoList(false);
var config = App.GetOptions<DbConnectionOptions>().ConnectionConfigs.FirstOrDefault(u => configId.Equals(u.ConfigId)); var config = _dbConnectionOptions.ConnectionConfigs.FirstOrDefault(u => configId.Equals(u.ConfigId));
IEnumerable<EntityInfo> entityInfos = await GetEntityInfos(false); // 获取所有实体定义 IEnumerable<EntityInfo> entityInfos = await GetEntityInfos(false); // 获取所有实体定义
entityInfos = entityInfos.OrderBy(u => u.EntityName.StartsWith("Sys") ? 1 : 0).ThenBy(u => u.EntityName); entityInfos = entityInfos.OrderBy(u => u.EntityName.StartsWith("Sys") ? 1 : 0).ThenBy(u => u.EntityName);
@ -217,7 +220,7 @@ public class SysCodeGenService : IDynamicApiController, ITransient
// 切库---多库代码生成用 // 切库---多库代码生成用
var provider = _db.AsTenant().GetConnectionScope(configId); var provider = _db.AsTenant().GetConnectionScope(configId);
var config = App.GetOptions<DbConnectionOptions>().ConnectionConfigs.FirstOrDefault(u => u.ConfigId.ToString() == configId); var config = _dbConnectionOptions.ConnectionConfigs.FirstOrDefault(u => u.ConfigId.ToString() == configId);
// 获取实体类型属性 // 获取实体类型属性
var entityType = provider.DbMaintenance.GetTableInfoList(false).FirstOrDefault(u => u.Name == tableName); var entityType = provider.DbMaintenance.GetTableInfoList(false).FirstOrDefault(u => u.Name == tableName);
if (entityType == null) return null; if (entityType == null) return null;
@ -276,7 +279,7 @@ public class SysCodeGenService : IDynamicApiController, ITransient
var entityType = GetEntityInfos().GetAwaiter().GetResult().FirstOrDefault(u => u.EntityName == input.TableName); var entityType = GetEntityInfos().GetAwaiter().GetResult().FirstOrDefault(u => u.EntityName == input.TableName);
if (entityType == null) if (entityType == null)
return null; return null;
var config = App.GetOptions<DbConnectionOptions>().ConnectionConfigs.FirstOrDefault(u => u.ConfigId.ToString() == input.ConfigId); var config = _dbConnectionOptions.ConnectionConfigs.FirstOrDefault(u => u.ConfigId.ToString() == input.ConfigId);
var dbTableName = config.DbSettings.EnableUnderLine ? UtilMethods.ToUnderLine(entityType.DbTableName) : entityType.DbTableName; var dbTableName = config.DbSettings.EnableUnderLine ? UtilMethods.ToUnderLine(entityType.DbTableName) : entityType.DbTableName;
int bracketIndex = dbTableName.IndexOf('{'); int bracketIndex = dbTableName.IndexOf('{');
@ -497,11 +500,11 @@ public class SysCodeGenService : IDynamicApiController, ITransient
string tResult = string.Empty; // 模板生成结果 string tResult = string.Empty; // 模板生成结果
var filename = templateList[i].Name; var filename = templateList[i].Name;
//更改默认首页模板 // 更改默认首页模板
if (filename == "web_views_index.vue.vm") if (filename == "web_views_index.vue.vm")
{ {
filename = string.IsNullOrEmpty(input.LeftTab) ? filename : "web_views_LeftTree.vue.vm";//左树右列表 filename = string.IsNullOrEmpty(input.LeftTab) ? filename : "web_views_LeftTree.vue.vm"; // 左树右列表
filename = string.IsNullOrEmpty(input.BottomTab) ? filename : "web_views_BottomIndx.vue.vm";//左数右上列表下列表属性 filename = string.IsNullOrEmpty(input.BottomTab) ? filename : "web_views_BottomIndx.vue.vm"; // 左数右上列表下列表属性
} }
var templateFilePath = Path.Combine(templatePath, filename); var templateFilePath = Path.Combine(templatePath, filename);
if (!File.Exists(templateFilePath)) continue; if (!File.Exists(templateFilePath)) continue;
@ -630,7 +633,7 @@ public class SysCodeGenService : IDynamicApiController, ITransient
PrintType = input.PrintType!, // 支持打印类型 PrintType = input.PrintType!, // 支持打印类型
PrintName = input.PrintName!, // 打印模板名称 PrintName = input.PrintName!, // 打印模板名称
IsApiService = input.IsApiService, IsApiService = input.IsApiService,
RemoteVerify = tableFieldList.Any(t => t.RemoteVerify == true) // 远程验证 RemoteVerify = tableFieldList.Any(t => t.RemoteVerify == true), // 远程验证
TreeName = input.TreeName, TreeName = input.TreeName,
LowerTreeName = string.IsNullOrEmpty(input.TreeName) ? "" : input.TreeName[..1].ToLower() + input.TreeName[1..], // 首字母小写 LowerTreeName = string.IsNullOrEmpty(input.TreeName) ? "" : input.TreeName[..1].ToLower() + input.TreeName[1..], // 首字母小写
LeftTab = input.LeftTab, LeftTab = input.LeftTab,
@ -659,11 +662,11 @@ public class SysCodeGenService : IDynamicApiController, ITransient
string tResult = string.Empty; // 模板生成结果 string tResult = string.Empty; // 模板生成结果
var filename = templateList[i].Name; var filename = templateList[i].Name;
//更改默认首页模板 // 更改默认首页模板
if (filename == "web_views_index.vue.vm") if (filename == "web_views_index.vue.vm")
{ {
filename = string.IsNullOrEmpty(input.LeftTab) ? filename : "web_views_LeftTree.vue.vm";//左树右列表 filename = string.IsNullOrEmpty(input.LeftTab) ? filename : "web_views_LeftTree.vue.vm"; // 左树右列表
filename = string.IsNullOrEmpty(input.BottomTab) ? filename : "web_views_BottomIndx.vue.vm";//左数右上列表下列表属性 filename = string.IsNullOrEmpty(input.BottomTab) ? filename : "web_views_BottomIndx.vue.vm"; // 左数右上列表下列表属性
} }
var templateFilePath = Path.Combine(templatePath, filename); var templateFilePath = Path.Combine(templatePath, filename);
if (!File.Exists(templateFilePath)) continue; if (!File.Exists(templateFilePath)) continue;

View File

@ -103,9 +103,7 @@ public class SysUserService : IDynamicApiController, ITransient
[DisplayName("增加用户")] [DisplayName("增加用户")]
public virtual async Task<long> AddUser(AddUserInput input) public virtual async Task<long> AddUser(AddUserInput input)
{ {
// 是否租户隔离登录验证 var query = _sysUserRep.AsQueryable().ClearFilter();
var isTenantHostLogin = await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysTenantHostLogin);
var query = _sysUserRep.AsQueryable().ClearFilter().WhereIF(isTenantHostLogin, u => u.TenantId == _userManager.TenantId || u.AccountType == AccountTypeEnum.SuperAdmin);
if (await query.AnyAsync(u => u.Account == input.Account)) throw Oops.Oh(ErrorCodeEnum.D1003); if (await query.AnyAsync(u => u.Account == input.Account)) throw Oops.Oh(ErrorCodeEnum.D1003);
if (!string.IsNullOrWhiteSpace(input.Phone) && await query.AnyAsync(u => u.Phone == input.Phone)) throw Oops.Oh(ErrorCodeEnum.D1032); if (!string.IsNullOrWhiteSpace(input.Phone) && await query.AnyAsync(u => u.Phone == input.Phone)) throw Oops.Oh(ErrorCodeEnum.D1032);
@ -138,10 +136,7 @@ public class SysUserService : IDynamicApiController, ITransient
[DisplayName("更新用户")] [DisplayName("更新用户")]
public virtual async Task UpdateUser(UpdateUserInput input) public virtual async Task UpdateUser(UpdateUserInput input)
{ {
// 是否租户隔离登录验证 var query = _sysUserRep.AsQueryable().ClearFilter().Where(u => u.Id != input.Id);
var isTenantHostLogin = await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysTenantHostLogin);
var query = _sysUserRep.AsQueryable().ClearFilter().Where(u => u.Id != input.Id)
.WhereIF(isTenantHostLogin, u => u.TenantId == _userManager.TenantId || u.AccountType == AccountTypeEnum.SuperAdmin);
if (await query.AnyAsync(u => u.Account == input.Account)) throw Oops.Oh(ErrorCodeEnum.D1003); if (await query.AnyAsync(u => u.Account == input.Account)) throw Oops.Oh(ErrorCodeEnum.D1003);
if (!string.IsNullOrWhiteSpace(input.Phone) && await query.AnyAsync(u => u.Phone == input.Phone)) throw Oops.Oh(ErrorCodeEnum.D1032); if (!string.IsNullOrWhiteSpace(input.Phone) && await query.AnyAsync(u => u.Phone == input.Phone)) throw Oops.Oh(ErrorCodeEnum.D1032);

View File

@ -2,7 +2,7 @@
"name": "admin.net.pro", "name": "admin.net.pro",
"type": "module", "type": "module",
"version": "2.4.33", "version": "2.4.33",
"lastBuildTime": "2025.01.13", "lastBuildTime": "2025.01.14",
"description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架", "description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架",
"author": "zuohuaijun", "author": "zuohuaijun",
"license": "MIT", "license": "MIT",
@ -88,8 +88,8 @@
"@types/node": "^20.17.12", "@types/node": "^20.17.12",
"@types/nprogress": "^0.2.3", "@types/nprogress": "^0.2.3",
"@types/sortablejs": "^1.15.8", "@types/sortablejs": "^1.15.8",
"@typescript-eslint/eslint-plugin": "^8.19.1", "@typescript-eslint/eslint-plugin": "^8.20.0",
"@typescript-eslint/parser": "^8.19.1", "@typescript-eslint/parser": "^8.20.0",
"@vitejs/plugin-vue": "^5.2.1", "@vitejs/plugin-vue": "^5.2.1",
"@vitejs/plugin-vue-jsx": "^4.1.1", "@vitejs/plugin-vue-jsx": "^4.1.1",
"@vue/compiler-sfc": "^3.5.13", "@vue/compiler-sfc": "^3.5.13",
@ -100,7 +100,7 @@
"less": "^4.2.1", "less": "^4.2.1",
"prettier": "^3.4.2", "prettier": "^3.4.2",
"rollup-plugin-visualizer": "^5.14.0", "rollup-plugin-visualizer": "^5.14.0",
"sass": "^1.83.1", "sass": "^1.83.3",
"terser": "^5.37.0", "terser": "^5.37.0",
"typescript": "^5.7.3", "typescript": "^5.7.3",
"vite": "^6.0.7", "vite": "^6.0.7",

View File

@ -130,6 +130,14 @@ export interface AddCodeGenInput {
*/ */
connectionString?: string | null; connectionString?: string | null;
/**
*
*
* @type {string}
* @memberof AddCodeGenInput
*/
treeName?: string | null;
/** /**
* *
* *
@ -249,4 +257,68 @@ export interface AddCodeGenInput {
* @memberof AddCodeGenInput * @memberof AddCodeGenInput
*/ */
codeGenTemplateIds?: Array<number> | null; codeGenTemplateIds?: Array<number> | null;
/**
*
*
* @type {string}
* @memberof AddCodeGenInput
*/
leftTab?: string | null;
/**
*
*
* @type {string}
* @memberof AddCodeGenInput
*/
leftKey?: string | null;
/**
*
*
* @type {string}
* @memberof AddCodeGenInput
*/
leftPrimaryKey?: string | null;
/**
* Name
*
* @type {string}
* @memberof AddCodeGenInput
*/
leftName?: string | null;
/**
*
*
* @type {string}
* @memberof AddCodeGenInput
*/
bottomTab?: string | null;
/**
*
*
* @type {string}
* @memberof AddCodeGenInput
*/
bottomKey?: string | null;
/**
*
*
* @type {string}
* @memberof AddCodeGenInput
*/
bottomPrimaryKey?: string | null;
/**
*
*
* @type {string}
* @memberof AddCodeGenInput
*/
template?: string | null;
} }

View File

@ -40,12 +40,12 @@ export interface LoginInput {
password: string; password: string;
/** /**
* * Id
* *
* @type {string} * @type {number}
* @memberof LoginInput * @memberof LoginInput
*/ */
host?: string | null; tenantId?: number;
/** /**
* Id * Id

View File

@ -40,12 +40,12 @@ export interface LoginPhoneInput {
code: string; code: string;
/** /**
* * Id
* *
* @type {string} * @type {number}
* @memberof LoginPhoneInput * @memberof LoginPhoneInput
*/ */
host?: string | null; tenantId?: number;
/** /**
* @type {LoginModeEnum} * @type {LoginModeEnum}

View File

@ -154,6 +154,14 @@ export interface PageCodeGenInput {
*/ */
tableName?: string | null; tableName?: string | null;
/**
*
*
* @type {string}
* @memberof PageCodeGenInput
*/
treeName?: string | null;
/** /**
* *
* *

View File

@ -150,6 +150,14 @@ export interface SysCodeGen {
*/ */
tableName?: string | null; tableName?: string | null;
/**
*
*
* @type {string}
* @memberof SysCodeGen
*/
treeName?: string | null;
/** /**
* *
* *
@ -222,6 +230,70 @@ export interface SysCodeGen {
*/ */
printName?: string | null; printName?: string | null;
/**
*
*
* @type {string}
* @memberof SysCodeGen
*/
leftTab?: string | null;
/**
*
*
* @type {string}
* @memberof SysCodeGen
*/
leftKey?: string | null;
/**
*
*
* @type {string}
* @memberof SysCodeGen
*/
leftPrimaryKey?: string | null;
/**
*
*
* @type {string}
* @memberof SysCodeGen
*/
leftName?: string | null;
/**
*
*
* @type {string}
* @memberof SysCodeGen
*/
bottomTab?: string | null;
/**
*
*
* @type {string}
* @memberof SysCodeGen
*/
bottomKey?: string | null;
/**
*
*
* @type {string}
* @memberof SysCodeGen
*/
bottomPrimaryKey?: string | null;
/**
*
*
* @type {string}
* @memberof SysCodeGen
*/
template?: string | null;
/** /**
* 使 Api Service * 使 Api Service
* *

View File

@ -130,6 +130,14 @@ export interface UpdateCodeGenInput {
*/ */
connectionString?: string | null; connectionString?: string | null;
/**
*
*
* @type {string}
* @memberof UpdateCodeGenInput
*/
treeName?: string | null;
/** /**
* *
* *
@ -250,6 +258,70 @@ export interface UpdateCodeGenInput {
*/ */
codeGenTemplateIds?: Array<number> | null; codeGenTemplateIds?: Array<number> | null;
/**
*
*
* @type {string}
* @memberof UpdateCodeGenInput
*/
leftTab?: string | null;
/**
*
*
* @type {string}
* @memberof UpdateCodeGenInput
*/
leftKey?: string | null;
/**
*
*
* @type {string}
* @memberof UpdateCodeGenInput
*/
leftPrimaryKey?: string | null;
/**
* Name
*
* @type {string}
* @memberof UpdateCodeGenInput
*/
leftName?: string | null;
/**
*
*
* @type {string}
* @memberof UpdateCodeGenInput
*/
bottomTab?: string | null;
/**
*
*
* @type {string}
* @memberof UpdateCodeGenInput
*/
bottomKey?: string | null;
/**
*
*
* @type {string}
* @memberof UpdateCodeGenInput
*/
bottomPrimaryKey?: string | null;
/**
*
*
* @type {string}
* @memberof UpdateCodeGenInput
*/
template?: string | null;
/** /**
* Id * Id
* *

View File

@ -215,8 +215,8 @@ const onSignIn = async () => {
const publicKey = window.__env__.VITE_SM_PUBLIC_KEY; const publicKey = window.__env__.VITE_SM_PUBLIC_KEY;
const password = sm2.doEncrypt(state.ruleForm.password, publicKey, 1); const password = sm2.doEncrypt(state.ruleForm.password, publicKey, 1);
const host = route.query.host ?? location.host; const tenantid = route.query.tenantid ?? 0;
const [err, res] = await feature(getAPI(SysAuthApi).apiSysAuthLoginPost({ ...state.ruleForm, password: password, host: host.toString() })); const [err, res] = await feature(getAPI(SysAuthApi).apiSysAuthLoginPost({ ...state.ruleForm, password: password, tenantId: Number(tenantid) }));
if (err) { if (err) {
getCaptcha(); // getCaptcha(); //
return; return;

View File

@ -74,13 +74,9 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20"> <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="Name字段" prop="treeName" <el-form-item label="Name字段" prop="treeName" :rules="[{ required: true, message: '请选择树控件Name字段', trigger: 'blur' }]">
:rules="[{ required: true, message: '请选择树控件Name字段', trigger: 'blur' }]"> <el-select v-model="state.ruleForm.treeName" @change="treeNameChanged" value-key="value" filterable clearable class="w100">
<el-select v-model="state.ruleForm.treeName" @change="treeNameChanged" <el-option v-for="item in state.columnData" :key="item.columnName" :label="item.columnName + ' ( ' + item.columnName + ' ) [' + item.columnComment + ']'" :value="item" />
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-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -222,10 +218,8 @@
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20"> <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="库名" <el-select v-model="state.ruleForm.configId2" placeholder="库名" filterable @change="dbChanged2()" class="w100">
filterable @change="dbChanged2()" class="w100"> <el-option v-for="item in state.dbData" :key="item.configId" :label="item.configId" :value="item.configId" />
<el-option v-for="item in state.dbData" :key="item.configId"
:label="item.configId" :value="item.configId" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -234,77 +228,51 @@
<template v-slot:label> <template v-slot:label>
<div> <div>
左边树表 左边树表
<el-tooltip raw-content <el-tooltip raw-content content="若找不到在前端生成的实体/表同上如表有下划线_则因实体去掉划线取不到字段。" placement="top">
content="若找不到在前端生成的实体/表同上如表有下划线_则因实体去掉划线取不到字段。" <el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"><ele-QuestionFilled /></el-icon>
placement="top">
<el-icon size="16"
style="margin-right: 3px; display: inline; vertical-align: middle"><ele-QuestionFilled /></el-icon>
</el-tooltip> </el-tooltip>
</div> </div>
</template> </template>
<el-select v-model="state.ruleForm.leftTab" <el-select v-model="state.ruleForm.leftTab" @change="leftTableChanged" value-key="value" filterable clearable class="w100">
@change="leftTableChanged" value-key="value" filterable <el-option v-for="item in state.tableData2" :key="item.entityName" :label="item.entityName + ' ( ' + item.tableName + ' ) [' + item.tableComment + ']'" :value="item" />
clearable class="w100">
<el-option v-for="item in state.tableData2"
:key="item.entityName"
:label="item.entityName + ' ( ' + item.tableName + ' ) [' + item.tableComment + ']'"
:value="item" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20"> <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="左边树关联字段" prop="leftKey"> <el-form-item label="左边树关联字段" prop="leftKey">
<el-select v-model="state.ruleForm.leftKey" @change="leftKeyChanged" <el-select v-model="state.ruleForm.leftKey" @change="leftKeyChanged" value-key="value" filterable clearable class="w100">
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-option v-for="item in state.lcolumnData"
:key="item.columnName"
:label="item.columnName + ' ( ' + item.columnName + ' ) [' + item.columnComment + ']'"
:value="item" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20"> <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">
<template v-slot:label> <template v-slot:label>
<div> <div>
关联主表字段 关联主表字段
<el-tooltip raw-content content="先选择主表才可以选择字段。" <el-tooltip raw-content content="先选择主表才可以选择字段。" placement="top">
placement="top"> <el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"><ele-QuestionFilled /></el-icon>
<el-icon size="16"
style="margin-right: 3px; display: inline; vertical-align: middle"><ele-QuestionFilled /></el-icon>
</el-tooltip> </el-tooltip>
</div> </div>
</template> </template>
<el-select v-model="state.ruleForm.leftPrimaryKey" <el-select v-model="state.ruleForm.leftPrimaryKey" @change="leftPrimaryKeyChanged" value-key="value" filterable clearable class="w100">
@change="leftPrimaryKeyChanged" value-key="value" filterable <el-option v-for="item in state.columnData" :key="item.columnName" :label="item.columnName + ' ( ' + item.columnName + ' ) [' + item.columnComment + ']'" :value="item" />
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-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20"> <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="左边树Name字段" prop="leftName">
<el-select v-model="state.ruleForm.leftName" <el-select v-model="state.ruleForm.leftName" @change="leftNameChanged" value-key="value" filterable clearable class="w100">
@change="leftNameChanged" value-key="value" filterable clearable <el-option v-for="item in state.lcolumnData" :key="item.columnName" :label="item.columnName + ' ( ' + item.columnName + ' ) [' + item.columnComment + ']'" :value="item" />
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-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20"> <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="模板" prop="template"> <el-form-item label="模板" prop="template">
<el-input v-model="state.ruleForm.template" clearable <el-input v-model="state.ruleForm.template" clearable placeholder="请输入" />
placeholder="请输入" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@ -313,10 +281,8 @@
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20"> <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="库名" <el-select v-model="state.ruleForm.configId3" placeholder="库名" filterable @change="dbChanged3()" class="w100">
filterable @change="dbChanged3()" class="w100"> <el-option v-for="item in state.dbData" :key="item.configId" :label="item.configId" :value="item.configId" />
<el-option v-for="item in state.dbData" :key="item.configId"
:label="item.configId" :value="item.configId" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -325,33 +291,20 @@
<template v-slot:label> <template v-slot:label>
<div> <div>
下框关联表名称 下框关联表名称
<el-tooltip raw-content <el-tooltip raw-content content="若找不到在前端生成的实体/表同上如表有下划线_则因实体去掉划线取不到字段。" placement="top">
content="若找不到在前端生成的实体/表同上如表有下划线_则因实体去掉划线取不到字段。" <el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"><ele-QuestionFilled /></el-icon>
placement="top">
<el-icon size="16"
style="margin-right: 3px; display: inline; vertical-align: middle"><ele-QuestionFilled /></el-icon>
</el-tooltip> </el-tooltip>
</div> </div>
</template> </template>
<el-select v-model="state.ruleForm.bottomTab" <el-select v-model="state.ruleForm.bottomTab" @change="bottomTableChanged" value-key="value" filterable clearable class="w100">
@change="bottomTableChanged" value-key="value" filterable <el-option v-for="item in state.tableData3" :key="item.entityName" :label="item.entityName + ' ( ' + item.tableName + ' ) [' + item.tableComment + ']'" :value="item" />
clearable class="w100">
<el-option v-for="item in state.tableData3"
:key="item.entityName"
:label="item.entityName + ' ( ' + item.tableName + ' ) [' + item.tableComment + ']'"
:value="item" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20"> <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" <el-select v-model="state.ruleForm.bottomKey" @change="bottomKeyChanged" value-key="value" filterable clearable class="w100">
@change="bottomKeyChanged" value-key="value" filterable <el-option v-for="item in state.bcolumnData" :key="item.columnName" :label="item.columnName + ' ( ' + item.columnName + ' ) [' + item.columnComment + ']'" :value="item" />
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> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -360,20 +313,13 @@
<template v-slot:label> <template v-slot:label>
<div> <div>
关联主表字段 关联主表字段
<el-tooltip raw-content content="先选择主表才可以选择字段。" <el-tooltip raw-content content="先选择主表才可以选择字段。" placement="top">
placement="top"> <el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"><ele-QuestionFilled /></el-icon>
<el-icon size="16"
style="margin-right: 3px; display: inline; vertical-align: middle"><ele-QuestionFilled /></el-icon>
</el-tooltip> </el-tooltip>
</div> </div>
</template> </template>
<el-select v-model="state.ruleForm.bottomPrimaryKey" <el-select v-model="state.ruleForm.bottomPrimaryKey" @change="bottomPrimaryKeyChanged" value-key="value" filterable clearable class="w100">
@change="bottomPrimaryKeyChanged" value-key="value" filterable <el-option v-for="item in state.columnData" :key="item.columnName" :label="item.columnName + ' ( ' + item.columnName + ' ) [' + item.columnComment + ']'" :value="item" />
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-select>
</el-form-item> </el-form-item>
</el-col> </el-col>